/** * @file Entity Normalizer * @description Production-grade entity normalization for transforming nested * API responses into flat, normalized state structures. * * Features: * - Automatic entity extraction from nested data * - Relationship handling (hasMany, belongsTo) * - Circular reference detection * - Schema-based normalization * - TypeScript type inference * * @example * ```typescript * import { normalize, schema } from '@/lib/data/normalization'; * * const userSchema = schema.entity('users'); * const postSchema = schema.entity('posts', { * author: userSchema, * }); * * const { entities, result } = normalize(apiResponse, postSchema); * // entities.users = { '1': {...}, '2': {...} } * // entities.posts = { '101': {...author: '1'} } * ``` */ /** * Entity with required ID field */ export interface Entity { id: string | number; [key: string]: unknown; } /** * Normalized entity map */ export type EntityMap = Record; /** * Normalized entities by type */ export type NormalizedEntities = Record; /** * Normalization result */ export interface NormalizationResult { /** Normalized entities by type */ entities: NormalizedEntities; /** Result reference(s) - ID or array of IDs */ result: TResult; } /** * Entity schema definition */ export interface EntitySchemaDefinition { /** Entity type name */ name: string; /** ID attribute name */ idAttribute?: string | ((entity: Entity) => string); /** Relationship definitions */ relations?: Record; /** Process entity after extraction */ processStrategy?: (entity: Entity) => Entity; /** Merge strategy for duplicate entities */ mergeStrategy?: (existingEntity: Entity, newEntity: Entity) => Entity; /** Fields to exclude from normalization */ excludeFields?: string[]; } /** * Schema types */ export type Schema = EntitySchema | ArraySchema | ObjectSchema | UnionSchema | ValueSchema; /** * Entity Schema */ export declare class EntitySchema { readonly _type: "entity"; readonly name: string; readonly idAttribute: string | ((entity: Entity) => string); readonly relations: Record; readonly processStrategy?: (entity: Entity) => Entity; readonly mergeStrategy: (existingEntity: Entity, newEntity: Entity) => Entity; readonly excludeFields: string[]; constructor(name: string, definition?: Omit); /** * Get entity ID */ getId(entity: Entity): string; /** * Define a relationship */ define(relations: Record): EntitySchema; } /** * Array Schema */ export declare class ArraySchema { readonly _type: "array"; readonly schema: Schema; constructor(schema: Schema); } /** * Object Schema */ export declare class ObjectSchema { readonly _type: "object"; readonly schema: Record; constructor(schema: Record); } /** * Union Schema (for polymorphic relationships) */ export declare class UnionSchema { readonly _type: "union"; readonly schemas: Record; readonly schemaAttribute: string | ((entity: Entity) => string); constructor(schemas: Record, schemaAttribute?: string | ((entity: Entity) => string)); /** * Get schema for entity */ getSchema(entity: Entity): EntitySchema | undefined; } /** * Value Schema (pass-through) */ export declare class ValueSchema { readonly _type: "value"; } /** * Schema factory functions */ export declare const schema: { /** * Create entity schema */ entity: (name: string, relations?: Record, options?: Omit) => EntitySchema; /** * Create array schema */ array: (itemSchema: Schema) => ArraySchema; /** * Create object schema */ object: (shape: Record) => ObjectSchema; /** * Create union schema */ union: (schemas: Record, schemaAttribute?: string | ((entity: Entity) => string)) => UnionSchema; /** * Create value schema (pass-through) */ value: () => ValueSchema; }; /** * Normalize data with a schema * * @param data - Data to normalize * @param inputSchema * @returns Normalized result with entities and result reference * * @example * ```typescript * // Single entity * const { entities, result } = normalize(user, userSchema); * // result = "1" * // entities.users = { "1": {...} } * * // Array of entities * const { entities, result } = normalize(users, schema.array(userSchema)); * // result = ["1", "2", "3"] * // entities.users = { "1": {...}, "2": {...}, "3": {...} } * ``` */ export declare function normalize(data: unknown, inputSchema: Schema): NormalizationResult; /** * Normalize and merge with existing entities * * @param data - New data to normalize * @param inputSchema * @param existingEntities - Existing normalized entities * @returns Merged normalization result */ export declare function normalizeAndMerge(data: unknown, inputSchema: Schema, existingEntities: NormalizedEntities): NormalizationResult; /** * Normalize batch of data * * @param items - Array of items to normalize * @param entitySchema * @returns Combined normalization result */ export declare function normalizeBatch(items: T[], entitySchema: EntitySchema): NormalizationResult; /** * Get entity from normalized state */ export declare function getEntity(entities: NormalizedEntities, entityType: string, id: string): T | undefined; /** * Get multiple entities from normalized state */ export declare function getEntities(entities: NormalizedEntities, entityType: string, ids: string[]): T[]; /** * Get all entities of a type */ export declare function getAllEntities(entities: NormalizedEntities, entityType: string): T[]; /** * Update entity in normalized state */ export declare function updateEntity(entities: NormalizedEntities, entityType: string, id: string, updates: Partial): NormalizedEntities; /** * Remove entity from normalized state */ export declare function removeEntity(entities: NormalizedEntities, entityType: string, id: string): NormalizedEntities; /** * Merge two normalized states */ export declare function mergeEntities(target: NormalizedEntities, source: NormalizedEntities, mergeStrategy?: 'overwrite' | 'keep' | 'merge'): NormalizedEntities;