Aglaea — AI-Powered Fashion, Beauty, and Personal Style Platform
This document specifies the implemented contracts of the Aglaea domain: the
foundation type system, domain entities, enumerations, the REST API surface, the
domain event system, persistence schemas, and configuration. Every type, enum
value, endpoint, and event below is drawn directly from the source under
libs/aglaea/.
Aglaea is a pure library domain — it has no apps/aglaea/ or
services/aglaea/. It is implemented as 93 libraries under libs/aglaea/.
For a plain-English description of what each library does, see features.md.
For how they are layered and wired together, see architecture.md. This
document is the definitive contract reference — the source of truth for type
shapes, API paths, event strings, and schema field names.
Table of Contents#
- Foundation Type System (
@aglaea/core) - Core Domain Constants (
@aglaea/core) - Configuration (
@aglaea/core) - AI Orchestration Types (
@aglaea/ai-orchestrator) - REST API Surface (
@aglaea/api-services) - Domain Event System (
@aglaea/events) - API-Services Event Catalog (
@aglaea/api-services) - Persistence Schemas (
@aglaea/database) - SDK Surface (
@aglaea/sdk) - Technology Stack
1. Foundation Type System (@aglaea/core)#
@aglaea/core (libs/aglaea/core/src/types.ts) is the foundation type system
for the domain. Every composite type is defined as a Zod schema with an inferred
TypeScript type (z.infer), which means all types are validated at runtime
wherever they cross a module boundary. The package depends only on zod, so it
can be safely imported by any library in the domain without introducing
additional dependency weight.
The index.ts barrel re-exports types, constants, validation, errors,
color-science, body-analysis, skin-analysis, hair-analysis,
fashion-taxonomy, style-profiling, recommendation-engine,
occasion-context, trend-analysis, fabric-intelligence,
fragrance-profiling, logging, config, feature-flags, rate-limiting,
and caching.
1.1 Branded ID Types#
Aglaea uses five nominal (branded) string types to prevent accidental mixing of IDs across different entity types at compile time. Each is validated against a UUID v4 pattern by its corresponding Zod schema.
| Type | Schema | Purpose |
|---|---|---|
PersonalProfileId |
PersonalProfileIdSchema |
Identifier for a personal style profile |
WardrobeItemId |
WardrobeItemIdSchema |
Identifier for a wardrobe item |
OutfitId |
OutfitIdSchema |
Identifier for a styled outfit |
AnalysisId |
AnalysisIdSchema |
Identifier for an analysis session (skin/hair/etc.) |
RecommendationId |
RecommendationIdSchema |
Identifier for a recommendation |
Each schema enforces UUID_V4_PATTERN
(^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$) and
returns a typed z.ZodType for the branded type.
1.2 Color Science Types#
Colors are stored in multiple spaces simultaneously because different spaces serve different purposes. RGB and Pantone are for display and production output; HSL is for human-readable hue/saturation/lightness reasoning; LAB is the canonical internal space for color-distance calculations and harmony scoring, because LAB is perceptually uniform — equal numeric deltas correspond to equal perceived differences.
| Schema | Type | Fields |
|---|---|---|
RGBSchema |
RGB |
r, g, b — integers 0–255 (sRGB color space) |
HSLSchema |
HSL |
h (0–360 degrees), s (0–100), l (0–100) |
LABSchema |
LAB |
L (0–100), a (−128 to 127), b (−128 to 127) — CIE L*a*b*, perceptually uniform |
CMYKSchema |
CMYK |
c, m, y, k — percentages 0–100 (print production) |
PantoneColorSchema |
PantoneColor |
code (Pantone reference), name, hex (6-digit hex approximation) |
The following enums capture color-system concepts used throughout the recommendation engine:
ColorHarmonySchema (ColorHarmony) — color-wheel relationship enum, used
to score and label the palette harmony of an outfit recommendation:
Complementary, Analogous, Triadic, SplitComplementary, Tetradic,
Monochromatic.
SeasonalSubtypeSchema (SeasonalSubtype) — the 12-season color analysis
system, which refines the four parent seasons along warmth, value, and chroma
axes: LightSpring, WarmSpring, ClearSpring, LightSummer, SoftSummer,
CoolSummer, SoftAutumn, WarmAutumn, DeepAutumn, DeepWinter,
CoolWinter, ClearWinter.
SeasonSchema (Season) — parent season grouping: Spring, Summer,
Autumn, Winter.
SkinUndertoneSchema (SkinUndertone) — Cool, Warm, Neutral,
Olive.
ColorTemperatureSchema (ColorTemperature) — Cool, Warm, Neutral.
SeasonalColorPaletteSchema (SeasonalColorPalette) is the primary output
of color analysis and the primary input to outfit and accessory recommendations.
Its fields are:
| Field | Type | Required | Meaning |
|---|---|---|---|
subtype |
SeasonalSubtype |
yes | The 12-season subtype |
season |
Season |
yes | Parent season grouping |
dominantCharacteristic |
string |
yes | Primary characteristic (e.g. "Light", "Warm") |
bestColors |
LAB[] (min 1) |
yes | Core flattering palette |
goodColors |
LAB[] |
yes | Acceptable but non-optimal colors |
avoidColors |
LAB[] |
yes | Colors that clash with or wash out the type |
neutrals |
LAB[] (min 1) |
yes | Recommended neutral palette for staples |
metalTones |
string[] |
yes | Best jewelry/hardware metal tones |
1.3 Body Measurement Types#
Body measurements are stored in centimeters. All ten fields are constrained to physiologically plausible maximums (e.g., bust ≤ 300 cm) so that measurement errors from photo analysis are caught at the schema boundary rather than producing absurd downstream recommendations.
BodyMeasurementsSchema (BodyMeasurements) — all values in centimeters,
all positive:
| Field | Constraint | Meaning |
|---|---|---|
bust |
positive, ≤ 300 | Full bust circumference at widest point |
waist |
positive, ≤ 300 | Natural waist at narrowest point above the navel |
hip |
positive, ≤ 300 | Hip circumference at widest point |
inseam |
positive, ≤ 150 | Inner leg from crotch to ankle |
shoulder |
positive, ≤ 100 | Shoulder width across the back |
armLength |
positive, ≤ 120 | Shoulder point to wrist bone |
torsoLength |
positive, ≤ 100 | Base of neck to natural waist |
thighCircumference |
positive, ≤ 120 | Thigh circumference at widest point |
neckCircumference |
positive, ≤ 80 | Neck circumference at the base |
wristCircumference |
positive, ≤ 40 | Wrist circumference at narrowest point |
BodyShapeSchema (BodyShape) — eight values: Hourglass, Pear,
Apple, Rectangle, InvertedTriangle, Oval, Diamond, Athletic. Each
enum entry carries a doc comment describing the measurement relationship that
defines it.
BodyProportionsSchema (BodyProportions) holds the ratios calculated from
raw measurements. These ratios — not the body shape label alone — determine
which visual-balance corrections are most appropriate, so two users with the
same body shape label but different ratios receive different silhouette
guidance.
| Field | Constraint | Meaning |
|---|---|---|
waistToHipRatio |
positive, ≤ 2 | Waist-to-hip ratio (WHR) |
shoulderToHipRatio |
positive, ≤ 3 | Upper/lower body balance |
bustToWaistRatio |
positive, ≤ 3 | Upper-body definition |
torsoToLegRatio |
positive, ≤ 3 | Whether torso or legs are longer |
shoulderToWaistRatio |
positive, ≤ 3 | Influences neckline/top recommendations |
bodyShape |
BodyShape |
Body shape classification from the ratios |
SizeRegionSchema (SizeRegion) — US, UK, EU, JP, AU, KR.
ClothingSizeSchema (ClothingSize): region (SizeRegion), category
(string), sizeValue (string), optional alphaSize, optional numericSize,
optional modifier (Petite | Regular | Tall | Plus).
1.4 Skin Analysis Types#
The skin analysis type system models both the categorical classification of skin (type, tone, concerns) and the quantitative scoring of individual biomarkers. The separation matters: categorical classifications drive routine logic (sensitive skin gets different formulations than oily skin), while biomarker scores enable longitudinal tracking (a user can see their hydration score improving over time).
SkinTypeSchema (SkinType) — Normal, Dry, Oily, Combination,
Sensitive.
SkinConcernSchema (SkinConcern) — twelve concerns, each of which maps to
specific recommended active ingredients in SKIN_CONCERN_INGREDIENTS: Acne,
Wrinkles, DarkSpots, LargePores, Dullness, Redness, Dehydration,
Hyperpigmentation, SunDamage, Texture, UndereyeCircles, Sagging.
FitzpatrickScaleSchema (FitzpatrickScale) — TypeI through TypeVI,
the Fitzpatrick UV-response phototype scale. Used to determine recommended SPF
and burn-time guidance from FITZPATRICK_UV_DATA.
SensitivityLevelSchema (SensitivityLevel) — Mild, Moderate,
Severe. Used by IngredientSensitivity to record the severity of a known
reaction to a specific ingredient.
SkinAnalysisResultSchema (SkinAnalysisResult) is the output type of a
skin analysis run. It carries both the categorical classifications and eleven
quantitative biomarker scores, each on a 0–100 scale. Note the direction of
scoring: positive-attribute scores (hydration, firmness, clarity) read 100 =
best; negative-attribute scores (oiliness, redness) read 0 = none, 100 = severe.
Fields: id (AnalysisId), analyzedAt (datetime), skinType (SkinType),
concerns (SkinConcern[]), plus twelve quantitative scores each constrained
0–100: hydration, oiliness, elasticity, pigmentation, texture,
wrinkleDepth, poreSize, redness, firmness, clarity, radiance.
SkinToneSchema (SkinTone) — fitzpatrickType (FitzpatrickScale),
undertone (SkinUndertone), labColor (LAB), optional hexApproximation.
IngredientSensitivitySchema (IngredientSensitivity) — ingredientName
(string), sensitivityLevel (SensitivityLevel), optional notes.
1.5 Hair Analysis Types#
Hair analysis types model the physical properties of hair that determine product and routine compatibility. The Andre Walker curl-pattern system (1A–4C) is the industry standard for curl classification, and porosity is the single most important determinant of whether a product will moisturize or weigh down the hair.
HairTypeSchema (HairType) — the Andre Walker curl-pattern system:
Straight1A, Straight1B, Straight1C, Wavy2A, Wavy2B, Wavy2C,
Curly3A, Curly3B, Curly3C, Coily4A, Coily4B, Coily4C.
HairDensitySchema (HairDensity) — Fine, Medium, Thick (strand
diameter classification).
HairPorositySchema (HairPorosity) — Low, Medium, High. Porosity
determines the balance between moisture-rich and protein-rich formulations a
hair type needs.
HairConditionSchema (HairCondition) — Healthy, SlightlyDamaged,
Damaged, SeverelyDamaged.
HairColorSchema (HairColor) — naturalColor (LAB), currentColor
(LAB), isColorTreated (boolean), grayPercentage (0–100), optional
naturalLevel (1–10), optional currentLevel (1–10).
HairAnalysisResultSchema (HairAnalysisResult) is the output type of a
hair analysis run: id (AnalysisId), analyzedAt, hairType, density,
porosity, condition, color (HairColor), plus four 0–100 scores:
scalpHealth, moisture, elasticity, shine.
1.6 Fashion Taxonomy Types#
Fashion taxonomy provides the controlled vocabulary that every garment classification and recommendation module uses to describe clothes. Without a shared taxonomy, garment categories and style terms would be ad-hoc strings with no consistent meaning across libraries.
GarmentCategorySchema (GarmentCategory) — twelve top-level categories:
Tops, Bottoms, Dresses, Outerwear, Swimwear, Activewear,
Sleepwear, Underwear, Accessories, Shoes, Bags, Jewelry.
GarmentSubcategorySchema (GarmentSubcategory) — a flat enum of
sub-categories grouped by parent category, including (among others)
TShirt/ButtonDown/Blouse/Sweater (Tops), Jeans/Trousers/Skirt
(Bottoms), MiniDress/Gown/Jumpsuit (Dresses), Blazer/Coat/Trench
(Outerwear), Bikini/OnePiece (Swimwear), Sneakers/Heels/Boots (Shoes),
Tote/Crossbody/Clutch (Bags), and Necklace/Ring/Earrings (Jewelry).
FabricTypeSchema (FabricType) — twenty fabric types: Cotton, Silk,
Wool, Linen, Polyester, Nylon, Rayon, Cashmere, Denim, Leather,
Suede, Satin, Chiffon, Velvet, Tweed, Jersey, Lycra, Organza,
Tulle, Lace.
FabricWeightSchema (FabricWeight) — Sheer, Lightweight, Midweight,
Heavyweight.
PatternSchema (Pattern) — fourteen patterns: Solid, Stripes,
Plaid, Floral, PolkaDots, Geometric, Abstract, Paisley,
Houndstooth, Camo, AnimalPrint, Chevron, Gingham, TieDye.
StyleAestheticSchema (StyleAesthetic) — fifteen aesthetics that classify
the dominant style identity of a garment or a user's preferences: Classic,
Minimalist, Bohemian, Streetwear, Preppy, Romantic, Edgy, Sporty,
Glamorous, Vintage, AvantGarde, Scandinavian, Coastal, Western,
Gothic.
GarmentConditionSchema (GarmentCondition) — New, Excellent, Good,
Fair, Poor.
SeasonSuitabilitySchema (SeasonSuitability) — four boolean flags:
spring, summer, autumn, winter.
WardrobeItemSchema (WardrobeItem) is the full wardrobe item entity. It
carries all attributes needed to generate outfit recommendations (category,
colors, fabric, formality, occasions, seasons) alongside administrative fields
for wardrobe management (purchase price, wear count, condition, storage).
| Field | Type | Required | Notes |
|---|---|---|---|
id |
WardrobeItemId |
yes | UUID v4 |
profileId |
PersonalProfileId |
yes | Owning profile |
category |
GarmentCategory |
yes | |
subcategory |
GarmentSubcategory |
yes | |
brand |
string |
yes | |
primaryColor |
LAB |
yes | Primary color in LAB space |
accentColors |
LAB[] |
default [] |
Accent/secondary colors |
fabric |
FabricType |
yes | Primary fabric |
fabricBlend |
FabricType[] |
default [] |
Additional blend fabrics |
fabricWeight |
FabricWeight |
yes | |
pattern |
Pattern |
yes | |
size |
ClothingSize |
yes | |
styleAesthetic |
StyleAesthetic |
yes | Dominant aesthetic |
seasonSuitability |
SeasonSuitability |
yes | |
formality |
number int 1–10 |
yes | 1 = extremely casual, 10 = black-tie |
condition |
GarmentCondition |
yes | |
purchaseDate |
string datetime |
optional | |
purchasePrice |
number nonnegative |
optional | |
currency |
string length 3 |
optional | ISO 4217 |
wearsCount |
number int nonneg. |
default 0 |
Total times worn |
lastWorn |
string datetime |
optional | |
careInstructions |
string |
optional | |
notes |
string |
optional | |
imageUrl |
string url |
optional | |
tags |
string[] |
default [] |
|
isAvailable |
boolean |
default true |
Not lent / in repair |
createdAt |
string datetime |
yes | |
updatedAt |
string datetime |
yes |
1.7 Occasion and Context Types#
Occasion and context types capture the circumstances for which an outfit is being recommended. The outfit engine uses all of these fields together: occasion determines formality bounds, dress code refines them, weather context gates fabric and layering choices, and time of day affects color and formality appropriateness.
OccasionTypeSchema (OccasionType) — fifteen occasions: Casual,
Business, BusinessCasual, Formal, BlackTie, Cocktail, DateNight,
Wedding, Interview, Outdoor, Beach, Festival, Travel, Gym,
Lounge.
WeatherContextSchema (WeatherContext) — temperatureMin /
temperatureMax (Celsius, −60 to 60), humidity (0–100), precipitation
(None | Light Rain | Heavy Rain | Snow | Sleet | Drizzle), uvIndex
(0–15), optional windSpeed (km/h, nonnegative), optional description.
DressCodeSchema (DressCode) — refines the formality band within an
occasion: WhiteTie, BlackTie, Cocktail, BusinessFormal,
BusinessCasual, SmartCasual, Casual, Athleisure, Resort.
TimeOfDaySchema (TimeOfDay) — EarlyMorning, Morning, Afternoon,
Evening, Night.
OutfitContextSchema (OutfitContext) — occasion (OccasionType),
optional dressCode (DressCode), weather (WeatherContext), optional
location, optional setting (Indoor | Outdoor | Both), timeOfDay
(TimeOfDay), optional durationHours (positive), optional culturalContext,
specialRequirements (string[], default []).
1.8 Recommendation Types#
The recommendation types capture both the output of the outfit engine and the multi-dimensional confidence scores that explain why an outfit was chosen. Exposing confidence as seven distinct scores rather than one overall number lets the user understand the tradeoffs — an outfit may score high on color harmony but low on occasion fit.
RecommendationConfidenceSchema (RecommendationConfidence) — seven scores
each constrained 0–1: overall, styleMatch, colorHarmony, occasionFit,
trendAlignment, bodyFlattery, weatherAppropriateness.
StylingNoteSchema (StylingNote) — category (Fit | Layering |
Accessories | Color | Proportions | Shoes | Hair | Makeup |
General), note (string), importance (Essential | Recommended |
Optional).
OutfitRecommendationSchema (OutfitRecommendation) is the primary output
type of the outfit engine. It includes the wardrobe item IDs that compose the
outfit, the full multi-dimensional confidence score, optional swap suggestions
for any item in the outfit, and a human-readable rationale explaining the
recommendation.
| Field | Type | Required | Notes |
|---|---|---|---|
id |
RecommendationId |
yes | |
profileId |
PersonalProfileId |
yes | |
items |
WardrobeItemId[] (min 1) |
yes | Wardrobe items composing the outfit |
context |
OutfitContext |
yes | Context the outfit was recommended for |
confidence |
RecommendationConfidence |
yes | Multi-dimensional confidence |
stylingNotes |
StylingNote[] |
default [] |
|
colorHarmonyType |
ColorHarmony |
optional | Harmony type achieved by the palette |
rationale |
string |
yes | Human-readable reasoning |
alternatives |
array of { replaceItemId, alternativeIds[], reason } |
default [] |
Swap suggestions |
generatedAt |
string datetime |
yes |
ShoppingRecommendationSchema (ShoppingRecommendation) — id,
profileId, productName, optional brand, category (GarmentCategory),
subcategory (GarmentSubcategory), matchReasons (string[], min 1),
priceMin / priceMax (nonnegative), currency (length 3), priorityScore
(0–1), confidence (RecommendationConfidence), pairsWith
(WardrobeItemId[], default []), styleAesthetic, optional suggestedColor
(LAB), optional suggestedFabric (FabricType), optional productUrl,
optional imageUrl, generatedAt.
1.9 Trend Analysis Types#
Trend types model the lifecycle and origin of fashion trends, enabling the forecasting engine to distinguish between a micro-trend at emergence, a macro-trend at peak, and a classic that has outlasted its original cycle.
TrendStatusSchema (TrendStatus) — Emerging, Growing, Peak,
Declining, Classic, Revival.
TrendCategorySchema (TrendCategory) — Color, Silhouette, Pattern,
Fabric, Styling, Accessory.
TrendOriginSchema (TrendOrigin) — Runway, Street, Social,
Celebrity, Editorial, Subculture.
FashionTrendSchema (FashionTrend) — name, category
(TrendCategory), status (TrendStatus), relevanceScore (0–1), seasons
(Season[], min 1), emergenceYear (int 1900–2100), origin (TrendOrigin),
description, alignedAesthetics (StyleAesthetic[], default []),
keyPieces (string[], default []), keyColors (LAB[], default []),
predictedLongevity (OneSeasonFad | MultiSeason | YearPlus |
EnduringSeveral | PermanentClassic), momentum (Accelerating | Stable |
Decelerating), lastUpdated.
1.10 Fragrance Types#
Fragrance types model the olfactory preference profile — preferred families, individual notes, known avoidances, and occasion/season preferences — that the fragrance matching engine uses to recommend perfumes without the user being able to smell them first.
FragranceFamilySchema (FragranceFamily) — twelve families: Floral,
Oriental, Woody, Fresh, Citrus, Aromatic, Chypre, Fougere,
Gourmand, Aquatic, Green, Musk.
NotePositionSchema (NotePosition) — Top, Middle, Base (fragrance
pyramid position; top notes are detected first and fade fastest, base notes
linger longest).
FragranceNoteSchema (FragranceNote) — ingredient (string), intensity
(NotePosition), description (string).
FragranceProfileSchema (FragranceProfile) — families
(FragranceFamily[], min 1), preferredNotes (FragranceNote[], default
[]), avoidances (array of { ingredient, reason, details? } where reason
is Dislike | Allergy | Sensitivity | Migraine | Other),
occasionPreferences (array of
{ occasion, preferredFamilies, preferredIntensity } where preferredIntensity
is Light | Moderate | Strong | Statement), optional
preferredConcentration (EauFraiche | EauDeCologne | EauDeToilette |
EauDeParfum | Parfum), seasonalPreferences (array of
{ season, preferredFamilies }).
1.11 Composite Profile Type#
PersonalStyleProfileSchema (PersonalStyleProfile) is the root entity of the
domain — the aggregate that recommendation modules read from. It brings together
the outputs of every analysis engine into a single object. All analysis fields
are optional because the profile can be partially complete; the
profileCompleteness score (computed by @aglaea/unified-profile) tracks which
analyses are present.
| Field | Type | Required | Meaning |
|---|---|---|---|
id |
PersonalProfileId |
yes | |
displayName |
string |
yes | |
seasonalPalette |
SeasonalColorPalette |
optional | Seasonal color analysis result |
skinTone |
SkinTone |
optional | |
bodyMeasurements |
BodyMeasurements |
optional | |
bodyProportions |
BodyProportions |
optional | Calculated proportions |
skinAnalysis |
SkinAnalysisResult |
optional | Latest skin analysis |
ingredientSensitivities |
IngredientSensitivity[] |
default [] |
|
hairAnalysis |
HairAnalysisResult |
optional | Latest hair analysis |
styleAesthetics |
StyleAesthetic[] |
default [] |
Ranked preferred aesthetics |
sizes |
ClothingSize[] |
default [] |
Preferred sizes by region |
fragranceProfile |
FragranceProfile |
optional | |
createdAt |
string datetime |
yes | |
updatedAt |
string datetime |
yes |
2. Core Domain Constants (@aglaea/core)#
constants.ts provides reference data tables consumed by analysis and
recommendation modules. These are read-only lookup tables — they encode domain
knowledge (what SPF does Fitzpatrick Type III require? what ratio thresholds
classify a body as pear-shaped?) that would otherwise be hardcoded in individual
libraries. Centralizing them here ensures consistency and allows them to be
updated in one place.
- Size conversion charts —
WOMENS_SIZE_CHART,MENS_SIZE_CHART,WOMENS_SHOE_CHART,MENS_SHOE_CHART(US/UK/EU mappings),ALPHA_TO_US_WOMENS,ALPHA_TO_US_MENS(alpha-to-numeric maps). - Measurement-to-size ranges —
BUST_TO_US_SIZE,WAIST_TO_US_SIZE,HIP_TO_US_SIZE(cm ranges mapped to US women's sizes). - Color theory constants —
COLOR_HARMONY_ANGLES(HSL wheel offsets per harmony type),CIE_D65/CIE_D50/CIE_A(illuminant white points),SRGB_TO_XYZ_MATRIX,XYZ_TO_SRGB_MATRIX,LAB_EPSILON(0.008856),LAB_KAPPA(903.3). FITZPATRICK_UV_DATA— per-phototype UV reaction data: minimum erythema dose range, burn-time range, recommended SPF, UV response description, melanin index range.FABRIC_CARE_INSTRUCTIONS— per-fabric care set: wash, dry, iron, notes,maxWashTemp,dryerSafe,bleachSafe.BODY_SHAPE_THRESHOLDS— ratio thresholds for body-shape classification (hourglass, pear, apple, rectangle, invertedTriangle, athletic, diamond, oval).IDEAL_PROPORTIONS— reference proportion ratios (golden waist-to-hip, balanced shoulder-to-hip, fashion-figure head ratio, torso-to-leg ratio).SEASONAL_PALETTE_ANCHORS— per-SeasonalSubtypeanchor data: parent season, dominant characteristic, anchorbestColors/neutrals/avoidColorsin LAB, andmetalTones.SUBCATEGORY_FORMALITY— default formality score (1–10) per garment subcategory.SUBCATEGORY_TO_CATEGORY— maps each subcategory to its parent category.LAYERING_ORDER— stacking order (0 = innermost) per garment category.FRAGRANCE_NOTE_TIMING— top/middle/base note onset, peak, fade, and duration (minutes);FRAGRANCE_CONCENTRATION_LONGEVITY— longevity hours and oil-percentage ranges per concentration tier.PATTERN_COMPATIBILITY— per-pattern set of mix-compatible patterns.FABRIC_SEASONAL_SCORES— fabric suitability score (0–1) per season.SKIN_CONCERN_INGREDIENTS— per-SkinConcernrecommended ingredient list with a description.
3. Configuration (@aglaea/core)#
config.ts defines AglaeaConfigSchema (AglaeaConfig), a section-oriented,
Zod-validated configuration object. Every section has defaults, so {} parses
to a valid config — libraries never have to handle a missing config section.
Config values can be overridden at startup via direct object overrides or via
environment variables, following the AGLAEA_<SECTION>_<KEY> naming convention
(e.g., AGLAEA_COLOR_ANALYSIS_ENABLED=false).
The configuration sections and their key fields are:
| Section | Schema | Key fields (with defaults) |
|---|---|---|
colorAnalysis |
ColorAnalysisConfigSchema |
enabled (true), defaultIlluminant (D65), ciede2000Threshold (2.0), seasonalAnalysisVersion (1) |
skinAnalysis |
SkinAnalysisConfigSchema |
enabled (true), minConfidenceScore (0.7), biomarkerCount (12), fitzpatrickAccuracy (0.95) |
hairAnalysis |
HairAnalysisConfigSchema |
enabled (true), walkerClassificationVersion (2) |
bodyAnalysis |
BodyAnalysisConfigSchema |
enabled (true), sizeConversionRegions (['US','UK','EU']) |
recommendations |
RecommendationsConfigSchema |
enabled (true), maxResults (20), minConfidenceThreshold (0.5), enableAIEnhancement (false), trendWeight (0.3), personalStyleWeight (0.5) |
trends |
TrendsConfigSchema |
enabled (true), forecastHorizonDays (90), minDataPointsForPrediction (30), adoptionCurveModel (rogers) |
fragrance |
FragranceConfigSchema |
enabled (true), maxRecommendations (10), seasonalBonus (0.15) |
cache |
CacheConfigSchema |
defaultTTLMs (300000), skinAnalysisTTLMs (1800000), trendDataTTLMs (300000), colorPaletteTTLMs (3600000) |
rateLimit |
RateLimitConfigSchema |
enabled (true), defaultWindowMs (60000), defaultMaxRequests (100) |
Loading and accessors. loadAglaeaConfig(overrides?) merges, in priority
order: schema defaults, caller-supplied overrides, and environment-variable
overrides. Environment variables use a flat AGLAEA_<SECTION>_<KEY> format
(example: AGLAEA_COLOR_ANALYSIS_ENABLED=false →
{ colorAnalysis: { enabled: false } }). validateAglaeaConfig(config) returns
a safeParse-style result. getConfigSection(config, section) is a type-safe
section accessor. mergeConfigs(base, override) deep-merges two partials
(arrays replace, not concatenate). ConfigManager is a stateful container with
getAll/get/set/reset and change-subscription support.
4. AI Orchestration Types (@aglaea/ai-orchestrator)#
libs/aglaea/ai-orchestrator/src/types.ts defines the contracts that every
library uses when requesting ML inference. The library ships orchestrator,
model-registry, ab-testing, fallback, inference-queue,
batch-inference, monitoring, cost-tracker, warmup, and edge-deployment
modules — the types below are shared across all of them.
Branded type. ModelId (Brand<string, 'ModelId'>), constructed via
modelId(raw).
ModelProvider (enum) — the set of supported ML inference providers:
OpenAI, Anthropic, HuggingFace, Custom, RunPod, Replicate, Local.
ModelCapability (enum) — the ten capabilities a model can be registered
for: SkinAnalysis, HairAnalysis, BodyAnalysis, ColorAnalysis,
OutfitRecommendation, TrendForecasting, FragranceMatching, VirtualTryOn,
FabricDetection, StyleClassification.
ModelConfig — the full descriptor of a registered model: id (ModelId),
name, provider (ModelProvider), endpoint, optional apiKey (in-memory
only, never persisted), capabilities (ModelCapability[]), version,
maxBatchSize, timeoutMs, costPerInference (USD), isActive,
warmupRequired, optional metadata.
InferencePriority — low | normal | high | critical. The helper
priorityWeight(p) maps these to numeric weights (critical 4 → low 1) for
the priority queue.
InferenceRequest<T> — id, modelId, input, priority, timestamp,
optional metadata. InferenceResult<T> — requestId, modelId,
output, latencyMs, cost, cached, modelVersion, timestamp.
InferenceError — requestId, modelId, error, code, retryable.
ABTestConfig — id, name, controlModelId, treatmentModelId,
trafficSplitPercent (0–100), startDate, optional endDate, capability,
isActive. ABTestResult — testId, controlMetrics, treatmentMetrics,
optional winner (control | treatment), confidence.
ModelMetrics — per-model performance tracking: totalRequests,
successRate, avgLatencyMs, p95LatencyMs, p99LatencyMs, avgCost,
totalCost, errorRate.
QueuedInference — wraps a request with its resolve/reject callbacks
and enqueuedAt. QueueStats — processed, failed, queued,
avgWaitMs.
Edge deployment. TargetDevice (cpu | gpu | tpu | npu),
Quantization (none | int8 | int4 | fp16), EdgeDeploymentConfig
(modelId, targetDevice, quantization, maxMemoryMb, batchSize,
warmupSamples).
Warm-up. WarmupResult (modelId, success, latencyMs, optional
error), WarmupStatus (cold | warming | warm | failed).
5. REST API Surface (@aglaea/api-services)#
@aglaea/api-services defines the REST API as typed RouteDefinition objects
grouped by domain module, aggregated by api-registry.ts. The registry constant
API_VERSION is v1 and BASE_PATH is /api/v1. All endpoint paths use
colon-prefixed path parameters (e.g. :profileId).
A RouteDefinition carries method, path, summary, tags, optional
requestBody (a SchemaRef with description, contentType, schema),
optional queryParams / pathParams (maps of ParamDef), a responses map
(HTTP status → ResponseDef), an auth field (required | optional |
none), and an optional rateLimit ({ windowMs, max }).
The registry aggregates 64 endpoints across seven modules. All endpoints
require authentication (auth: 'required') except the eight Trends/Discovery
endpoints noted below.
5.1 Profile Endpoints (profile-endpoints.ts)#
These ten endpoints cover the full lifecycle of a personal style profile — creation, retrieval, update, deletion, photo upload, preference management, history browsing, and external sync.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/v1/profiles/:profileId |
Get personal profile by ID | req. |
| POST | /api/v1/profiles |
Create a new personal profile | req. |
| PUT | /api/v1/profiles/:profileId |
Update an existing profile | req. |
| DELETE | /api/v1/profiles/:profileId |
Delete a profile and all associated data | req. |
| GET | /api/v1/profiles/:profileId/summary |
Get condensed profile summary with key metrics | req. |
| POST | /api/v1/profiles/:profileId/photo |
Upload or replace the profile photo | req. |
| GET | /api/v1/profiles/:profileId/preferences |
Get style/notification/privacy preferences | req. |
| PUT | /api/v1/profiles/:profileId/preferences |
Update user preferences | req. |
| GET | /api/v1/profiles/:profileId/history |
Get paginated activity history | req. |
| POST | /api/v1/profiles/:profileId/sync |
Trigger a full sync from external sources | req. |
5.2 Analysis Endpoints (analysis-endpoints.ts)#
These ten endpoints submit photos or measurements for analysis and retrieve
results. The six analysis-submission endpoints (POST /analysis/{type}) accept
multipart/form-data and respond 202 with an AnalysisJob schema — analysis
is asynchronous. They enforce per-minute rate limits (15/min for
skin/color/face/hair/nails, 10/min for body).
| Method | Path | Purpose | Auth |
|---|---|---|---|
| POST | /api/v1/analysis/skin |
Run a comprehensive skin analysis | req. |
| POST | /api/v1/analysis/color |
Run seasonal and undertone color analysis | req. |
| POST | /api/v1/analysis/face-shape |
Run face shape detection and proportion analysis | req. |
| POST | /api/v1/analysis/body |
Run body shape and proportion analysis | req. |
| POST | /api/v1/analysis/hair |
Run hair type, texture, and condition analysis | req. |
| POST | /api/v1/analysis/nails |
Run nail shape, health, and color analysis | req. |
| GET | /api/v1/analysis/:analysisId |
Retrieve a completed analysis result by ID | req. |
| GET | /api/v1/analysis/history/:profileId |
Get paginated analysis history (filterable by type) | req. |
| POST | /api/v1/analysis/compare |
Compare two analyses (before/after) to track progress | req. |
| DELETE | /api/v1/analysis/:analysisId |
Delete an analysis result and its images | req. |
5.3 Recommendation Endpoints (recommendation-endpoints.ts)#
These ten endpoints generate personalized recommendations across the platform's
major recommendation types — outfit, shopping, style tips, color palette,
skincare routine, hair care, and makeup. All are POST requests because they
consume context (occasion, weather, skin analysis ID) from the request body.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| POST | /api/v1/recommendations/outfit |
Generate outfit recommendations for an occasion | req. |
| POST | /api/v1/recommendations/shopping |
Get shopping recommendations to fill wardrobe gaps | req. |
| POST | /api/v1/recommendations/style-tips |
Get personalized style tips | req. |
| POST | /api/v1/recommendations/color-palette |
Generate a personalized color palette | req. |
| POST | /api/v1/recommendations/skincare-routine |
Generate a skincare routine from skin analysis | req. |
| POST | /api/v1/recommendations/hair-care |
Generate a hair care routine from hair analysis | req. |
| POST | /api/v1/recommendations/makeup-look |
Generate a makeup look from face shape and coloring | req. |
| GET | /api/v1/recommendations/:recId |
Get detailed recommendation by ID | req. |
| POST | /api/v1/recommendations/:recId/feedback |
Submit feedback on a recommendation | req. |
| GET | /api/v1/recommendations/history/:profileId |
Get paginated recommendation history | req. |
5.4 Wardrobe Endpoints (wardrobe-endpoints.ts)#
These ten endpoints manage the digital wardrobe — adding, updating, and removing items, logging wear events, retrieving analytics, identifying gaps, and generating a capsule wardrobe from existing inventory.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/v1/wardrobe/:profileId |
Get the full wardrobe inventory | req. |
| POST | /api/v1/wardrobe/:profileId/items |
Add a new item to the wardrobe | req. |
| PUT | /api/v1/wardrobe/:profileId/items/:itemId |
Update an existing wardrobe item | req. |
| DELETE | /api/v1/wardrobe/:profileId/items/:itemId |
Remove an item from the wardrobe | req. |
| POST | /api/v1/wardrobe/:profileId/items/:itemId/wear |
Log that an item was worn on a date | req. |
| GET | /api/v1/wardrobe/:profileId/analytics |
Get analytics including cost-per-wear | req. |
| GET | /api/v1/wardrobe/:profileId/gaps |
Identify wardrobe gaps | req. |
| POST | /api/v1/wardrobe/:profileId/capsule |
Generate a capsule wardrobe from existing items | req. |
| GET | /api/v1/wardrobe/:profileId/outfits |
Get outfit history with wear dates and ratings | req. |
| POST | /api/v1/wardrobe/:profileId/digitize |
Digitize a clothing item from a photo (AI) | req. |
5.5 Virtual Try-On Endpoints (tryon-endpoints.ts)#
These eight endpoints submit try-on requests for different item types and retrieve results. Try-on sessions are persistent — a session ID is returned immediately, and the rendered result is retrieved asynchronously.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| POST | /api/v1/tryon/fashion |
Virtually try on a clothing item (body-mapped) | req. |
| POST | /api/v1/tryon/makeup |
Virtually try on a makeup look (face-mapped AR) | req. |
| POST | /api/v1/tryon/hair |
Virtually try on a hairstyle or hair color | req. |
| POST | /api/v1/tryon/nails |
Virtually try on a nail design or color | req. |
| POST | /api/v1/tryon/accessories |
Virtually try on glasses, jewelry, or hats | req. |
| POST | /api/v1/tryon/avatar |
Generate a 3D avatar from profile data and photos | req. |
| GET | /api/v1/tryon/:sessionId |
Get the result of a try-on session | req. |
| DELETE | /api/v1/tryon/:sessionId |
Delete a try-on session and its rendered assets | req. |
5.6 Conversation Endpoints (conversation-endpoints.ts)#
These eight endpoints drive multi-turn dialogue with the AI style assistant. Sessions are stateful — each message is sent to an existing session so the assistant can maintain context across turns.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| POST | /api/v1/conversations |
Start a conversation with the AI style assistant | req. |
| POST | /api/v1/conversations/:sessionId/message |
Send a message and receive a response | req. |
| GET | /api/v1/conversations/:sessionId |
Retrieve the full message history | req. |
| DELETE | /api/v1/conversations/:sessionId |
End and archive a conversation session | req. |
| GET | /api/v1/conversations/:sessionId/suggestions |
Get contextual follow-up suggestions | req. |
| POST | /api/v1/conversations/:sessionId/feedback |
Rate a specific assistant response | req. |
| GET | /api/v1/conversations/active |
List all active conversations for the user | req. |
| POST | /api/v1/conversations/:sessionId/context |
Update the conversation context | req. |
5.7 Trends and Discovery Endpoints (trends-endpoints.ts)#
These eight endpoints are the only ones in the registry that do not require
authentication for all callers — trend and discovery data can be surfaced to
anonymous users for browsing. Personalized endpoints (/personalized/:profileId
and /forecast) require authentication because they depend on profile data.
| Method | Path | Purpose | Auth |
|---|---|---|---|
| GET | /api/v1/trends |
Get current trending styles, colors, silhouettes | optional |
| GET | /api/v1/trends/:trendId |
Get detailed information about a trend | optional |
| GET | /api/v1/trends/personalized/:profileId |
Get trends personalized to a profile | required |
| GET | /api/v1/trends/forecast |
Get trend forecasts for upcoming seasons | required |
| GET | /api/v1/discovery/inspiration |
Get a curated inspiration feed | optional |
| GET | /api/v1/discovery/celebrities/:celebId |
Get style profile and looks for a celebrity | optional |
| GET | /api/v1/discovery/communities |
List style communities and interest groups | optional |
| POST | /api/v1/discovery/search |
Search across trends, looks, products, communities | optional |
6. Domain Event System (@aglaea/events)#
@aglaea/events provides the typed domain event system that decouples Aglaea's
modules. When an analysis completes, it publishes a typed event; the
skincare-routine library subscribes to that event and updates its
recommendations without the analysis library needing to know it exists. This
decoupling is what allows 93 libraries to coordinate without forming a tightly
coupled dependency web.
The library ships publisher, consumer, dead-letter, replay, analytics,
and helpers modules over the core types below.
6.1 Core Event Types#
These types form the envelope that wraps every domain event, regardless of payload type.
DomainEvent<T>—id(UUID v4),type(dot-namespaced),timestamp(ISO-8601),version(schema version number),source(originating context), optionalcorrelationId, optionalcausationId, optionalmetadata,payload(typedT).EventEnvelope<T>— wraps aDomainEvent<T>withroutingKey, optionalpartition, optionaldeduplicationId.EventHandler<T>—(event: DomainEvent<T>) => void | Promise<void>.EventFilter— optionalsource,correlationId,metadatapredicate.EventSubscription—id,eventType,handler, optionalfilter.EventPublisherOptions— optionalretries,timeout.DeadLetterEntry<T>—event,error,attemptCount,lastAttempt, optionalnextRetry.ReplayFilter— optionalfromTimestamp,toTimestamp,eventTypes,source,correlationId,limit,offset.
6.2 Event Type Constants (AGLAEA_EVENT_TYPES)#
The AGLAEA_EVENT_TYPES const object defines 23 event type strings grouped by
domain area. AglaeaEventType is the TypeScript union of all values, which
means any switch statement on an event type will produce a compile error if a
case is missed.
| Constant | Type string | Payload interface |
|---|---|---|
SKIN_ANALYSIS_COMPLETED |
aglaea.analysis.skin-completed |
SkinAnalysisCompletedPayload |
BODY_ANALYSIS_COMPLETED |
aglaea.analysis.body-completed |
BodyAnalysisCompletedPayload |
HAIR_ANALYSIS_COMPLETED |
aglaea.analysis.hair-completed |
HairAnalysisCompletedPayload |
COLOR_ANALYSIS_COMPLETED |
aglaea.analysis.color-completed |
ColorAnalysisCompletedPayload |
ANALYSIS_FAILED |
aglaea.analysis.failed |
AnalysisFailedPayload |
OUTFIT_RECOMMENDED |
aglaea.recommendation.outfit-recommended |
OutfitRecommendedPayload |
SHOPPING_RECOMMENDED |
aglaea.recommendation.shopping-recommended |
ShoppingRecommendedPayload |
STYLE_TIP_GENERATED |
aglaea.recommendation.style-tip-generated |
StyleTipGeneratedPayload |
RECOMMENDATION_ACCEPTED |
aglaea.recommendation.accepted |
RecommendationAcceptedPayload |
RECOMMENDATION_REJECTED |
aglaea.recommendation.rejected |
RecommendationRejectedPayload |
WARDROBE_ITEM_ADDED |
aglaea.wardrobe.item-added |
WardrobeItemAddedPayload |
WARDROBE_ITEM_REMOVED |
aglaea.wardrobe.item-removed |
WardrobeItemRemovedPayload |
WARDROBE_ITEM_WORN |
aglaea.wardrobe.item-worn |
WardrobeItemWornPayload |
WARDROBE_ITEM_UPDATED |
aglaea.wardrobe.item-updated |
WardrobeItemUpdatedPayload |
OUTFIT_CREATED |
aglaea.wardrobe.outfit-created |
OutfitCreatedPayload |
OUTFIT_RATED |
aglaea.wardrobe.outfit-rated |
OutfitRatedPayload |
PURCHASE_COMPLETED |
aglaea.purchase.completed |
PurchaseCompletedPayload |
WISHLIST_ADDED |
aglaea.purchase.wishlist-added |
WishlistAddedPayload |
CART_ABANDONED |
aglaea.purchase.cart-abandoned |
CartAbandonedPayload |
RETURN_INITIATED |
aglaea.purchase.return-initiated |
ReturnInitiatedPayload |
PROFILE_FOLLOWED |
aglaea.social.profile-followed |
ProfileFollowedPayload |
OUTFIT_SHARED |
aglaea.social.outfit-shared |
OutfitSharedPayload |
STYLE_BOARD_CREATED |
aglaea.social.style-board-created |
StyleBoardCreatedPayload |
STYLE_BOARD_LIKED |
aglaea.social.style-board-liked |
StyleBoardLikedPayload |
6.3 Event Payloads#
The following payload shapes illustrate the data carried by the most commonly
consumed events. All payload types are defined in events.ts and are exported
alongside their event constants.
SkinAnalysisCompletedPayload—profileId,analysisId,skinType(oily|dry|combination|normal|sensitive),fitzpatrickType(1–6),overallScore,concerns(SkinConcern[]where each is{ name, severity: 'low' | 'moderate' | 'high' }),timestamp.BodyAnalysisCompletedPayload—profileId,analysisId,bodyShape(hourglass|pear|apple|rectangle|inverted-triangle),measurements(MeasurementsSummary: optionalbust,waist,hips,height,weight,inseam).HairAnalysisCompletedPayload—profileId,analysisId,hairType(1a–4c),healthScore.ColorAnalysisCompletedPayload—profileId,analysisId,seasonalSubtype(12-value kebab-case union),undertone(warm|cool|neutral).OutfitRecommendedPayload—profileId,recommendationId,itemIds,confidenceScore,occasion.WardrobeItemAddedPayload—profileId,itemId,category,brand.PurchaseCompletedPayload—profileId,productId,price,retailer,category.
7. API-Services Event Catalog (@aglaea/api-services)#
Separately from @aglaea/events, @aglaea/api-services declares 39
EventDefinition records intended for documentation and contract generation,
aggregated by api-registry.ts. These records describe the same domain events
from the perspective of the API contract surface — each EventDefinition
carries type (dot identifier), description, payload (schema name), topic
(message-bus topic), and version. They use a different naming scheme from the
@aglaea/events constants and are not the runtime event type strings.
| Module file | Count | Topics |
|---|---|---|
analysis-events.ts |
10 | analysis.skin, analysis.color, analysis.face, analysis.body, analysis.hair, analysis.nails, analysis.progress, analysis.batch, analysis.profile |
recommendation-events.ts |
10 | recommendations.{outfit,shopping,routine,feedback,lifecycle,trends,tips,batch} |
wardrobe-events.ts |
10 | wardrobe.{items,wear,outfits,analytics,capsule} |
social-events.ts |
10 | social.{outfits,communities,challenges,achievements,experts,feed} |
Representative event types from the catalog: skin.analysis.completed,
color.analysis.completed, outfit.recommended, routine.recommended,
trend.alert.generated, wardrobe.item.added, wardrobe.gap.detected,
wardrobe.capsule.generated, outfit.shared, community.joined,
challenge.completed, achievement.unlocked, badge.earned.
8. Persistence Schemas (@aglaea/database)#
@aglaea/database defines the persistence layer. It uses Knex for SQL
migrations and Zod schemas for table row validation. There is no Prisma in
this domain — the direct Knex + Zod pattern was chosen to keep the persistence
layer free of ORM magic and to share the same Zod validation approach as the
rest of @aglaea/core. The target store is PostgreSQL. Dependencies are
minimal: knex, zod, and uuid.
8.1 Migrations#
Migrations are Knex up/down functions, re-exported from
migrations/index.ts. They run in sequence; each migration builds on the
previous one.
20260318000001_initial_setup— creates theuuid-osspandpg_trgmPostgreSQL extensions, the custom enum types (e.g.seasonal_subtype,skin_undertone,color_temperature,gender_type), and the sharedupdated_attrigger function.20260318000002_core_tables— core domain tables (profiles, analysis records, wardrobe, outfits, recommendations).20260318000003_catalog_tables— product and catalog tables (products, trends, ingredients, fabrics, brands, retailers).20260318000004_social_audit— social and audit tables (follows, shares, style boards, audit log).20260318000005_seed_data— reference data seeding.
8.2 Schema Modules#
The schema/index.ts barrel re-exports sixteen schema modules. Each module
exports a full record schema plus CreateInput and (for mutable entities)
UpdateInput variants derived via .omit() / .partial():
common, profiles, skin-analysis, body-measurements, wardrobe,
outfits, products, recommendations, preferences, feedback, trends,
ingredients, fabrics, brands, social, audit.
common.ts provides shared building blocks used by all other modules:
UuidSchema, TimestampSchema, HexColorSchema, UrlSchema,
CurrencyCodeSchema, PaginationSchema, SortDirectionSchema, SortSchema,
DateRangeFilterSchema, NumericRangeFilterSchema, JsonValueSchema,
JsonObjectSchema, StringArrayJsonSchema.
8.3 PersonalProfile (profiles.ts)#
PersonalProfileSchema is the database-layer representation of a user's
profile. It carries less data than PersonalStyleProfile from @aglaea/core
because analysis results are stored in their own tables and joined when needed,
rather than embedded in the profile row.
PersonalProfileSchema — id (UUID), userId (UUID, FK to auth system),
displayName, email, optional dateOfBirth (YYYY-MM-DD), optional gender
(GenderSchema: female | male | non_binary | prefer_not_to_say |
other), optional location (LocationSchema: city, country ISO 3166-1
alpha-2, timezone IANA), optional profileImageUrl, optional
seasonalSubtype (SeasonalSubtypeDbSchema, 12-value), optional
skinUndertone (Cool | Warm | Neutral | Olive), optional
colorTemperature (Cool | Warm | Neutral), createdAt, updatedAt.
8.4 SkinAnalysisRecord (skin-analysis.ts)#
Each analysis run produces a dated history row, not an overwrite, so the full
history of a user's skin condition is reconstructable over time. The eleven
dimension scores here correspond exactly to those in SkinAnalysisResultSchema
from @aglaea/core.
SkinAnalysisRecordSchema — id, profileId (FK), analysisDate, skinType
(SkinTypeDbSchema), optional fitzpatrickType, optional undertone, eleven
0–100 dimension scores (hydrationScore, oilinessScore, elasticityScore,
pigmentationScore, textureScore, wrinkleDepthScore, poreSizeScore,
rednessScore, firmnessScore, clarityScore, radianceScore) plus a
computed overallScore, concerns (SkinConcernDb[], default []),
recommendations (string[], default []), optional imageUrl, optional
metadata (JSON), createdAt. The module also exports
IngredientSensitivityRecordSchema.
8.5 BodyMeasurementRecord (body-measurements.ts)#
Like skin analysis, body measurements are stored as dated history rows. All measurement fields are optional because not every photo submission produces all ten measurements.
BodyMeasurementRecordSchema — id, profileId (FK), measurementDate, ten
optional cm circumference/length fields (bustCm, waistCm, hipCm,
inseamCm, shoulderWidthCm, armLengthCm, torsoLengthCm,
thighCircumferenceCm, neckCircumferenceCm, wristCircumferenceCm), optional
heightCm and weightKg, optional bodyShape (BodyShapeDbSchema:
Hourglass, Pear, Apple, Rectangle, InvertedTriangle, Oval,
Diamond, Athletic), optional bodyProportions (JSON), optional
clothingSizes (JSON), optional notes, optional measuredBy
(MeasuredBySchema), createdAt, updatedAt.
8.6 WardrobeItemDb (wardrobe.ts)#
The wardrobe item database schema. Note that colors are stored as hex strings (for display) rather than LAB (for computation), since LAB conversion is applied in the application layer when the item is loaded for recommendation processing.
WardrobeItemDbSchema — id, profileId (FK to personal_profiles),
category (GarmentCategoryDbSchema), optional subcategory (free-text),
optional brand, name, optional description, optional primaryColorHex /
accentColorHex (HexColorSchema), optional fabricType
(FabricTypeDbSchema), optional fabricBlend (free-text), optional pattern
(PatternDbSchema), optional styleAesthetic (StyleAestheticDbSchema),
seasonSuitability (string-array JSON, default []), optional formalityLevel
(1–10), optional condition (GarmentConditionDbSchema), optional
purchaseDate / purchasePrice / purchaseCurrency / retailer, optional
size / sizeRegion, wearCount (default 0), optional lastWornDate,
optional careInstructions, imageUrls (JSON array, default []), tags
(JSON array, default []), isFavorite (default false), isArchived
(default false), createdAt, updatedAt.
8.7 Outfit (outfits.ts)#
OutfitSchema — id, profileId (FK), name, optional description, items
(string-array JSON of wardrobe item UUIDs, default []), optional occasion,
optional dressCode, optional weatherContext (JSON), optional dateWorn,
optional rating (1–5), optional notes, optional imageUrl, isPublic
(default false), createdAt, updatedAt. The module also exports
OutfitItemSchema.
8.8 RecommendationRecord (recommendations.ts)#
Recommendation records persist the full multi-dimensional confidence scores and feedback, enabling the preference learning pipeline to train on both the recommendation and its outcome.
RecommendationRecordSchema — id, profileId (FK), type
(RecommendationTypeDbSchema: outfit | shopping | style_tip |
color_palette), optional context (JSON), items (array of JSON objects,
default []), seven optional 0–1 confidence scores (confidenceScore,
styleMatchScore, colorHarmonyScore, occasionFitScore,
trendAlignmentScore, bodyFlatteryScore, weatherScore), stylingNotes
(array of JSON objects, default []), optional wasAccepted, optional
feedbackRating (1–5), optional feedbackText, createdAt.
8.9 Product (products.ts)#
Products in the Aglaea database represent catalog items from retail partners, normalized into the domain's type system for matching against profile attributes.
ProductSchema — id, optional externalId (retailer sync key), name,
optional brand, optional description, optional category / subcategory,
optional price / priceCurrency, optional retailerUrl, imageUrls (JSON
array), colors (JSON array), optional fabricType, optional pattern,
optional styleAesthetic, sizes (JSON array), seasonSuitability (JSON
array), optional formalityLevel (1–10), optional rating (0–5), optional
reviewCount, isAvailable (default true), optional lastSyncedAt,
timestamps.
8.10 Trend (trends.ts)#
TrendSchema plus its database-layer enums. Note that the database enums for
trend category, status, and origin use more granular values than the
@aglaea/core types, providing richer data for the trend forecasting engine.
TrendCategoryDbSchema—color,fabric,pattern,silhouette,style,accessory,footwear,beauty,lifestyle(nine values, vs. six in core).TrendStatusDbSchema—emerging,rising,peak,declining,fading,classic,revived(seven values, vs. six in core).TrendOriginDbSchema—runway,street,celebrity,social_media,subculture,designer,vintage_revival,technology,sustainability(nine values, vs. six in core).
8.11 Other Schema Modules#
These modules cover user preferences, feedback, reference data (ingredients, fabrics, brands), social interactions, and audit logging.
preferences.ts—UserPreferencesSchemaplusSustainabilityPreferenceSchema,SizePreferenceSchema,CoveragePreferenceSchema,FragranceConcentrationSchema.feedback.ts—FeedbackSchema,FeedbackTargetTypeSchema.ingredients.ts—IngredientSchema,IngredientCategoryDbSchema.fabrics.ts—FabricMaterialSchema,FabricCategoryDbSchema,PriceRangeDbSchema.brands.ts—BrandSchemaandRetailerSchema,BrandPriceRangeDbSchema.social.ts—FollowSchema,ShareSchema,StyleBoardSchema,ShareTargetTypeSchema,SharePlatformSchema.audit.ts—AuditLogSchema,AuditActionSchema.
9. SDK Surface (@aglaea/sdk)#
@aglaea/sdk provides a high-level TypeScript client for consuming the
@aglaea/api-services endpoints. It handles authentication, retry logic, and
file uploads, so callers interact with typed domain methods rather than raw HTTP
requests.
AglaeaClient is the primary entry point, composed of five sub-components also
exported as instance fields:
http(AglaeaHttpClient) — the underlying HTTP clientinterceptors(InterceptorManager) — request/response interceptor chainretry(RetryHandler) — exponential back-off withmaxRetriesdefaulting to 3 andbaseDelayMsto 1000batch(BatchProcessor) — batch multiple requests togetheruploader(FileUploader) — handles multipart file uploads for analysis and photo endpoints
The client is constructed from an AglaeaSDKConfig. Its domain methods wrap
REST endpoints through the retry handler:
| Method | HTTP call |
|---|---|
analyzeSkin(profileId, imageUrl) |
POST /api/v1/analysis/skin |
analyzeBody(profileId, measurements) |
POST /api/v1/analysis/body |
getColorPalette(profileId) |
GET /api/v1/profiles/:profileId/colors |
getOutfitRecommendation(profileId, context) |
POST /api/v1/recommendations/outfit |
getShoppingRecommendations(...) |
GET /api/v1/recommendations/shopping |
getTrends(category?, limit?) |
GET /api/v1/trends |
getWardrobeItems(profileId) |
GET /api/v1/profiles/:profileId/wardrobe |
addWardrobeItem(profileId, item) |
POST /api/v1/profiles/:profileId/wardrobe |
uploadImage(profileId, file) |
(file upload via FileUploader) |
destroy() |
Releases client resources |
The SDK module set also includes auth, websocket, and http-client. The SDK
uses its own client-facing result types (APIResponse<T>, SkinAnalysisResult,
BodyAnalysisResult, ColorPalette, OutfitRecommendation, WardrobeItem,
TrendsResponse, ShoppingRecommendationsResponse, UploadResult, etc.)
defined in sdk/src/types.ts.
10. Technology Stack#
The technology choices below apply across all 93 Aglaea libraries. Each library
declares its own package.json, exposing source directly via
"main": "./src/index.ts" — there is no compiled output checked in, and
libraries depend on each other via TypeScript path aliases resolved at build
time.
| Layer | Technology / Library |
|---|---|
| Language | TypeScript (Node.js, ESM — every package is "type": "module") |
| Validation | Zod (every @aglaea/core and @aglaea/database schema) |
| Persistence | PostgreSQL — Knex for migrations, Zod schemas for row validation |
| Database extns. | uuid-ossp, pg_trgm |
| Dependency mgmt. | pnpm catalog (catalog: refs) |
| Testing | Vitest (*.test.ts / *.spec.ts co-located per library) |
| Build executor | @nx/js:tsc |
| Test executor | @nx/vite:test |
Dependencies are kept minimal by design: @aglaea/core depends only on zod;
@aglaea/database on knex, zod, uuid. This keeps the foundation layer
importable without pulling in unnecessary transitive dependencies.
Grounding#
Every type, enum value, endpoint path, event constant, schema field, and
configuration default in this document was read directly from source under
libs/aglaea/ — primarily core/src/types.ts, core/src/constants.ts,
core/src/config.ts, ai-orchestrator/src/types.ts, events/src/types.ts,
events/src/events.ts, api-services/src/*.ts, database/src/schema/*.ts,
database/src/migrations/*.ts, and sdk/src/client.ts. Counts (93 libraries,
64 endpoints, 23 event constants, 39 EventDefinition records, 16 schema
modules) reflect the current monorepo state. Aglaea is a pure library domain; no
apps/aglaea/ or services/aglaea/ exists.