Package Architecture
Relevant source files
The following files were used as context for generating this wiki page:
The @cellarnode/beverage-utils package provides a standardized set of utilities for classifying, formatting, and normalizing beverage data within the CellarNode platform. Its architecture is designed to be framework-agnostic at its core, while providing specialized adapters for React, Vue, and Angular to facilitate integration with TanStack Query.
Sources: README.md:3-8, package.json:11-35
Core Module Structure
The package is structured as a TypeScript library using ES modules. It exposes a main entry point for core utilities and specific entry points for framework integrations.
| Entry Point | Target Framework | Purpose |
|---|---|---|
. | Core (Universal) | Logic for mapping, formatting, and normalization. |
./react | React | Hooks and query options for React Query. |
./vue | Vue | Composables and query options for Vue Query. |
./angular | Angular | Service functions for Angular Query. |
Sources: package.json:12-32, README.md:10-18
Dependency Graph
The core logic is "zero-dependency" (excluding i18n-iso-countries), while framework adapters list peer dependencies that are only required if that specific adapter is used.
Explanation: This diagram shows how the framework-specific adapters rely on the core mapping logic defined in label-map.ts, while index.ts aggregates all core utilities.
Sources: src/index.ts:1-6, src/react/use-beverage-label-map.ts:1-3, package.json:44-51
Data Classification and Mapping
The system relies on a hierarchical classification of beverages consisting of Categories and Subtypes.
Data Model
The classification data structure is defined by three primary interfaces:
| Interface | Description | Key Fields |
|---|---|---|
BeverageSubtype | Specific type of beverage (e.g., "Red"). | id, name, oivType |
BeverageCategory | Broad group (e.g., "Still Wine"). | id, name, subtypes |
BeverageClassification | Root container for all categories. | categories |
Sources: src/types.ts:1-16
The LabelMap Builder
To resolve ambiguity (where subtype IDs like red might exist under multiple categories), the package implements a composite key strategy. The buildLabelMap function iterates through classifications to create a flat lookup object.
Explanation: The mapping process creates three types of entries: Category ID, Subtype ID (last-write-wins), and a unique Composite Key (category:subtype).
Sources: src/label-map.ts:5-15, README.md:20-31
Formatting and Normalization Logic
Display Formatters
Two primary functions in src/format.ts handle the conversion of identifiers into human-readable strings:
formatBeverageLabel: Looks up a key in aLabelMap. If not found, it "humanizes" the slug by replacing underscores with spaces and applying Title Case.formatBeverageType: Combines a category and an optional subtype into a "Category / Subtype" string, prioritizing composite keys for the subtype part to ensure accuracy.
Sources: src/format.ts:3-23, tests/format.test.ts:14-41
Normalization Utilities
The normalize.ts module provides utilities for data cleansing and standardization:
normalizeToken: Trims, lowercases, strips diacritics (using NFD normalization), and replaces spaces/underscores with hyphens.resolveCountryCode: Converts various country string formats (English, Spanish, Swedish names or Alpha-3 codes) into ISO Alpha-2 codes.parseVolumeToLiters: A parser that extracts numeric liter values from diverse strings such as "750ml", "20 hl", or complex patterns like "bottles 750ml x 4000".
Sources: src/normalize.ts:7-130
Integration Architecture
Framework adapters use TanStack Query (formerly React Query) to fetch and manage the state of beverage classifications.
Fetching Flow
All adapters share a common beverageLabelMapOptions implementation logic which targets the CellarNode public API.
Explanation: The data is fetched from a remote endpoint and immediately transformed into a LabelMap before being cached with a staleTime of Infinity.
Sources: src/react/use-beverage-label-map.ts:7-21, src/angular/beverage-label-map.service.ts:7-21, README.md:120-138
Summary
The @cellarnode/beverage-utils architecture prioritizes utility and reliability by decoupling core logic (mapping, formatting, normalization) from framework-specific state management. By using composite keys for beverage subtypes and providing robust normalization for countries and volumes, it ensures consistent data display across the CellarNode platform.