Beverage Type Formatter
Relevant source files
The following files were used as context for generating this wiki page:
The Beverage Type Formatter is a core module within the @cellarnode/beverage-utils library designed to convert technical beverage identifiers (slugs) into human-readable display strings. It facilitates the presentation of beverage categories and subtypes by utilizing a lookup map or falling back to automated string humanization.
This system is essential for the CellarNode platform to ensure that ambiguous subtype IDs (e.g., "red" appearing under both "Still Wine" and "Sparkling Wine") are resolved correctly using composite keys. It provides utility functions that support pure TypeScript projects as well as framework-specific adapters for React, Vue, and Angular.
Sources: README.md:3-8, README.md:16-19, src/format.ts
Core Data Structures
The formatting logic relies on specific interfaces that define the hierarchy of beverage classifications. A BeverageClassification consists of multiple BeverageCategory objects, each containing an array of BeverageSubtype objects.
| Interface | Field | Type | Description |
|---|---|---|---|
BeverageSubtype | id | string | Unique identifier within a category (e.g., "red"). |
name | string | Display name (e.g., "Red Wine"). | |
BeverageCategory | id | string | Unique identifier (e.g., "wine"). |
name | string | Display name (e.g., "Still Wine"). | |
subtypes | BeverageSubtype[] | List of associated subtypes. |
Sources: src/types.ts:1-15
Label Mapping and Disambiguation
To handle duplicate subtype IDs across different categories, the system builds a LabelMap. This map uses composite keys in the format category:subtype to ensure unambiguous lookups.
The buildLabelMap function iterates through the classification data to populate a flat record where keys can be category IDs, subtype IDs, or composite keys.
Sources: src/label-map.ts:3-15, README.md:27-33
Formatting Logic
The module provides two primary functions for string transformation: formatBeverageLabel and formatBeverageType.
Single Label Formatting
formatBeverageLabel is the base utility for converting a single key into a label. It follows a specific priority:
- Map Lookup: If a
LabelMapis provided and contains the key, it returns the mapped value. - Humanization: If no map match is found, it replaces underscores with spaces and capitalizes the first letter of each word.
- Null Safety: Returns an empty string if the input is null or undefined.
Sources: src/format.ts:3-10
Composite Type Formatting
formatBeverageType handles the combination of a category and an optional subtype. It specifically utilizes composite keys (category:subtype) to resolve the subtype's display name accurately before joining them with a separator (/).
Sources: src/format.ts:12-22, README.md:47-55
String Normalization and Tokenization
Behind the scenes, the utility includes a normalizeToken function to ensure that identifiers are deterministic before comparison or storage. This process involves stripping diacritics, lowercasing, and collapsing whitespace.
export function normalizeToken(raw: string | null | undefined): string {
if (!raw) return "";
return raw
.normalize("NFD")
.replace(/[\u0300-\u036f]/g, "")
.trim()
.toLowerCase()
.replace(/[\s_]+/g, "-")
.replace(/-+/g, "-")
.replace(/^-|-$/g, "");
}
Sources: src/normalize.ts:7-17
Integration with Frameworks
The formatter functions are designed to work in conjunction with framework-specific hooks (React, Vue) or services (Angular) that fetch the classification data via @tanstack/query. These adapters fetch data from the CellarNode API, build a LabelMap using buildLabelMap, and provide it to the formatting functions.
| Function | Framework | Source File |
|---|---|---|
useBeverageLabelMap | React | src/react/use-beverage-label-map.ts |
useBeverageLabelMap | Vue | src/vue/use-beverage-label-map.ts |
injectBeverageLabelMap | Angular | src/angular/beverage-label-map.service.ts |
Sources: README.md:65-115, src/index.ts:1-6
The Beverage Type Formatter provides a robust mechanism for consistent beverage labeling across the CellarNode ecosystem. By combining strict type definitions, composite key disambiguation, and automated humanization fallbacks, it ensures technical data is presented clearly to users regardless of the UI framework used.