UNPKG

mod-engine

Version:

A TypeScript library for typed attributes and modifiers with deterministic evaluation

532 lines (519 loc) 18.8 kB
interface EnumAttributeSchema { readonly key: string; readonly kind: "enum"; readonly values: readonly string[]; readonly cardinality?: "single" | "multi"; } interface BooleanAttributeSchema { readonly key: string; readonly kind: "boolean"; } interface NumberAttributeSchema { readonly key: string; readonly kind: "number"; readonly min?: number; readonly max?: number; readonly integer?: boolean; } interface StringAttributeSchema { readonly key: string; readonly kind: "string"; readonly pattern?: string; readonly minLen?: number; readonly maxLen?: number; } type AttributeSchema = EnumAttributeSchema | BooleanAttributeSchema | NumberAttributeSchema | StringAttributeSchema; interface ConfigSpec { readonly metrics: readonly string[]; readonly operations: readonly string[]; readonly attributes: readonly AttributeSchema[]; } type MetricOf<C extends ConfigSpec> = C["metrics"][number]; type OperationOf<C extends ConfigSpec> = C["operations"][number]; type AttrKeyOf<C extends ConfigSpec> = C["attributes"][number]["key"]; type ExtractAttr<C extends ConfigSpec, K extends AttrKeyOf<C>> = Extract<C["attributes"][number], { key: K; }>; type AttrValueOf<C extends ConfigSpec, K extends AttrKeyOf<C>> = ExtractAttr<C, K> extends EnumAttributeSchema ? ExtractAttr<C, K>["cardinality"] extends "multi" ? readonly ExtractAttr<C, K>["values"][number][] : ExtractAttr<C, K>["values"][number] : ExtractAttr<C, K> extends BooleanAttributeSchema ? boolean : ExtractAttr<C, K> extends NumberAttributeSchema ? number : ExtractAttr<C, K> extends StringAttributeSchema ? string : never; type Attributes<C extends ConfigSpec> = { [K in AttrKeyOf<C>]?: AttrValueOf<C, K>; }; type EqualityCondition<C extends ConfigSpec> = { [K in AttrKeyOf<C>]: { op: "eq"; attr: K; value: AttrValueOf<C, K>; }; }[AttrKeyOf<C>]; type InclusionCondition<C extends ConfigSpec> = { [K in AttrKeyOf<C>]: { op: "in"; attr: K; values: AttrValueOf<C, K>[]; }; }[AttrKeyOf<C>]; type ContainsCondition<C extends ConfigSpec> = { [K in AttrKeyOf<C>]: { op: "includes"; attr: K; value: AttrValueOf<C, K>; }; }[AttrKeyOf<C>]; type Condition<C extends ConfigSpec> = { op: "and"; clauses: Condition<C>[]; } | { op: "or"; clauses: Condition<C>[]; } | { op: "not"; clause: Condition<C>; } | EqualityCondition<C> | InclusionCondition<C> | ContainsCondition<C> | { op: "gt" | "gte" | "lt" | "lte"; attr: AttrKeyOf<C>; value: number; }; type Stacking = "stack" | "unique" | { uniqueBy: string; }; interface Modifier<C extends ConfigSpec> { readonly metric: MetricOf<C>; readonly operation: OperationOf<C>; readonly value: number; readonly conditions?: Condition<C>; readonly stacking?: Stacking; readonly priority?: number; readonly source?: string; } interface ItemSpec<C extends ConfigSpec> { readonly name?: string; readonly attributes: Attributes<C>; readonly modifiers: readonly Modifier<C>[]; } interface AppliedTrace<C extends ConfigSpec> { metric: MetricOf<C>; operation: OperationOf<C>; value: number; before: number; after: number; priority?: number; source?: string; conditionMatched: boolean; } interface ModifierApplication<C extends ConfigSpec> { readonly modifier: Modifier<C>; /** Value of the modifier as provided via Builder.by */ readonly appliedValue: number; /** Metric value before the modifier was applied */ readonly before: number; /** Metric value after applying the modifier */ readonly after: number; /** @deprecated kept for backwards-compat – use after */ readonly resultingValue: number; } interface EvaluationResult<C extends ConfigSpec> { readonly metrics: Record<MetricOf<C>, number>; readonly applied: readonly ModifierApplication<C>[]; } interface EvalContext<C extends ConfigSpec> { readonly item: ItemSpec<C>; readonly modifier: Modifier<C>; readonly currentMetrics: Record<MetricOf<C>, number>; } type OperationImpl<C extends ConfigSpec> = (current: number, value: number, context: EvalContext<C>) => number; interface ValidationError$1 { readonly path: string; readonly message: string; readonly code: string; } interface ValidationResult { readonly ok: boolean; readonly errors?: readonly ValidationError$1[]; } interface SerializedData<T = unknown> { readonly version: number; readonly data: T; } /** * Fluent builder for creating item specifications */ declare class Builder<C extends ConfigSpec> { private config; private itemName?; private attributes; private modifiers; private currentCondition?; private currentStacking?; private currentPriority?; private currentSource?; private groupDepth; constructor(config: C, name?: string); /** * Sets an attribute value with type safety */ set<K extends AttrKeyOf<C>>(key: K, value: AttrValueOf<C, K>): Builder<C>; /** * Applies multiple attributes from an object. Useful for defaults. * Accepts a partial attributes map and merges into the current builder state. */ setAttributes(attrs: Partial<Attributes<C>>): Builder<C>; /** * Sets a condition for subsequent modifiers */ when(condition: Condition<C>): Builder<C>; /** * Sets metadata for subsequent modifiers */ with(metadata: { stacking?: Stacking; priority?: number; source?: string; }): Builder<C>; /** * Adds an increase modifier (sum operation) */ increase<M extends MetricOf<C>>(metric: M): { by(value: number): Builder<C>; }; /** * Adds a decrease modifier (subtract operation) */ decrease<M extends MetricOf<C>>(metric: M): { by(value: number): Builder<C>; }; /** * Adds a multiply modifier */ multiply<M extends MetricOf<C>>(metric: M): { by(value: number): Builder<C>; }; /** * Adds a generic modifier with specified operation */ apply<M extends MetricOf<C>, O extends OperationOf<C>>(metric: M, operation: O): { by(value: number): Builder<C>; }; /** * Internal method to add a modifier */ private addModifier; /** * Resets the current context (condition, stacking, etc.) */ private resetCurrentContext; /** * Clears any pending condition so subsequent modifiers are unaffected. */ clearConditions(): Builder<C>; /** * Clears stacking / priority / source metadata applied to subsequent modifiers. */ clearMeta(): Builder<C>; /** * Creates a temporary grouping context where the provided condition/meta apply * to all modifiers executed within the callback. After the callback the * previous context is restored automatically. */ group(options: { when?: Condition<C>; with?: { stacking?: Stacking; priority?: number; source?: string; }; }, fn: (b: Builder<C>) => void): Builder<C>; /** * Builds and returns the immutable item specification */ build(): ItemSpec<C>; /** * Creates a copy of the builder with the same state */ clone(): Builder<C>; /** * Gets the current number of modifiers */ get modifierCount(): number; /** * Gets the current number of attributes set */ get attributeCount(): number; /** * Clears all modifiers */ clearModifiers(): Builder<C>; /** * Clears all attributes */ clearAttributes(): Builder<C>; /** * Resets the builder to initial state */ reset(): Builder<C>; } /** * Helper type for condition builders */ interface ConditionBuilder<C extends ConfigSpec> { /** * Creates an equality condition */ eq<K extends AttrKeyOf<C>>(attr: K, value: AttrValueOf<C, K>): Condition<C>; /** * Creates an inclusion condition (value in array) */ in<K extends AttrKeyOf<C>>(attr: K, values: AttrValueOf<C, K>[]): Condition<C>; /** * Creates a contains condition (array contains value) */ includes<K extends AttrKeyOf<C>>(attr: K, value: AttrValueOf<C, K>): Condition<C>; /** * Creates comparison conditions */ gt(attr: AttrKeyOf<C>, value: number): Condition<C>; gte(attr: AttrKeyOf<C>, value: number): Condition<C>; lt(attr: AttrKeyOf<C>, value: number): Condition<C>; lte(attr: AttrKeyOf<C>, value: number): Condition<C>; /** * Creates logical conditions */ and(...conditions: Condition<C>[]): Condition<C>; or(...conditions: Condition<C>[]): Condition<C>; not(condition: Condition<C>): Condition<C>; } /** * Creates a condition builder for type-safe condition construction */ declare function createConditionBuilder<C extends ConfigSpec>(): ConditionBuilder<C>; /** * Defines a configuration and returns it with strong typing */ declare function defineConfig<const C extends ConfigSpec>(config: C): C; /** * Engine interface that provides all core functionality */ interface Engine<C extends ConfigSpec> { builder(name?: string): Builder<C>; evaluate(item: ItemSpec<C>, options?: { base?: Partial<Record<MetricOf<C>, number>>; }): EvaluationResult<C>; validateItem(item: ItemSpec<C>): ValidationResult; registerOperation?(name: string, impl: OperationImpl<C>, options?: { precedence?: number; }): void; } /** * Creates an engine instance from a configuration */ declare function createEngine<const C extends ConfigSpec>(config: C, options?: { strictOperations?: boolean; }): Engine<C>; /** * Type-safe engine builder that enforces operation registration */ declare class EngineBuilder<C extends ConfigSpec> { private config; private operations; private builtinOps; constructor(config: C); /** * Register a custom operation with its implementation */ withOperation<OpName extends OperationOf<C>>(name: OpName extends "sum" | "subtract" | "multiply" ? never : OpName, impl: OperationImpl<C>, options?: { precedence?: number; }): EngineBuilder<C>; /** * Register multiple operations at once */ withOperations(operations: Record<Exclude<OperationOf<C>, "sum" | "subtract" | "multiply">, { impl: OperationImpl<C>; precedence?: number; }>): EngineBuilder<C>; /** * Build the engine - validates that all custom operations are registered */ build(): Engine<C>; } /** * Create a type-safe engine builder that enforces operation registration */ declare function createEngineBuilder<const C extends ConfigSpec>(config: C): EngineBuilder<C>; /** * Union type of all built-in operations */ type BuiltinOperation = "sum" | "subtract" | "multiply"; /** * Helper function to create a typed array of built-in operations * Provides autocomplete and type safety for operation selection */ declare function builtinOps<T extends BuiltinOperation[]>(...operations: T): T; /** * Built-in operation: sum * Adds the modifier value to the current metric value */ declare function sumOperation<C extends ConfigSpec>(): OperationImpl<C>; /** * Built-in operation: subtract * Subtracts the modifier value from the current metric value */ declare function subtractOperation<C extends ConfigSpec>(): OperationImpl<C>; /** * Built-in operation: multiply * Multiplies the current metric value by the modifier value */ declare function multiplyOperation<C extends ConfigSpec>(): OperationImpl<C>; /** * Registry of built-in operations with their precedence values */ interface OperationInfo<C extends ConfigSpec> { impl: OperationImpl<C>; precedence: number; } /** * Creates a map of built-in operations */ declare function createBuiltInOperations<C extends ConfigSpec>(): Map<string, OperationInfo<C>>; /** * Validates that a numeric result is safe (not NaN, Infinity, etc.) */ declare function validateNumericResult(value: number, path: string): number; /** * Validates an item specification against the config */ declare function validateItem<C extends ConfigSpec>(item: ItemSpec<C>, config: C): ValidationResult; /** * Validates a configuration specification */ declare function validateConfig<C extends ConfigSpec>(config: C): void; /** * Validates that all operations declared in config have implementations */ declare function validateOperations<C extends ConfigSpec>(config: C, operations: Map<string, OperationInfo<C>>): void; /** * Serializes an item specification to JSON-compatible format */ declare function serializeItem<C extends ConfigSpec>(item: ItemSpec<C>): SerializedData<ItemSpec<C>>; /** * Deserializes an item specification from JSON-compatible format */ declare function deserializeItem<C extends ConfigSpec>(serialized: SerializedData<ItemSpec<C>>): ItemSpec<C>; /** * Serializes an array of modifiers to JSON-compatible format */ declare function serializeModifiers<C extends ConfigSpec>(modifiers: readonly Modifier<C>[]): SerializedData<readonly Modifier<C>[]>; /** * Deserializes an array of modifiers from JSON-compatible format */ declare function deserializeModifiers<C extends ConfigSpec>(serialized: SerializedData<readonly Modifier<C>[]>): readonly Modifier<C>[]; /** * Serializes an evaluation result to JSON-compatible format */ declare function serializeEvaluationResult<C extends ConfigSpec>(result: EvaluationResult<C>): SerializedData<EvaluationResult<C>>; /** * Deserializes an evaluation result from JSON-compatible format */ declare function deserializeEvaluationResult<C extends ConfigSpec>(serialized: SerializedData<EvaluationResult<C>>): EvaluationResult<C>; /** * Converts data to JSON string with proper error handling */ declare function toJSON(data: unknown): string; /** * Parses JSON string with proper error handling */ declare function fromJSON<T>(json: string): T; /** * Creates a deep clone of serializable data */ declare function deepClone<T>(data: T): T; /** * Evaluates a condition against the given attributes */ declare function evaluateCondition<C extends ConfigSpec>(condition: Condition<C>, attributes: Attributes<C>, path?: string): boolean; /** * Validates that a condition is well-formed */ declare function validateCondition<C extends ConfigSpec>(condition: Condition<C>, config: C, path?: string): void; /** * Evaluates an item specification and returns the computed metrics */ declare function evaluateItem<C extends ConfigSpec>(item: ItemSpec<C>, operations: Map<string, OperationInfo<C>>, config: C, baseMetrics?: Partial<Record<MetricOf<C>, number>>): EvaluationResult<C>; /** * Creates a snapshot of current metrics for immutability */ declare function createMetricsSnapshot<C extends ConfigSpec>(metrics: Record<MetricOf<C>, number>): Record<MetricOf<C>, number>; /** * Validates that all metrics in the config are present in the result */ declare function validateMetricsCompleteness<C extends ConfigSpec>(metrics: Record<string, number>, config: C): void; /** * Transforms an EvaluationResult into a stable "AppliedTrace" shape that is * convenient for debugging and UI consumption. No additional computation is * performed – the function only maps existing data. */ declare function explainEvaluation<C extends ConfigSpec>(result: EvaluationResult<C>): AppliedTrace<C>[]; /** * Creates a flat, computed representation combining evaluated metrics and attributes. * * This helper produces a predictable shape that's useful for: * - API responses and database storage * - Frontend state management * - Analytics and reporting * - Caching computed results * - Testing and debugging * - Data export/import (CSV, JSON) * - External integrations * * @param engine - The engine to use for evaluation * @param item - The item specification to evaluate * @param baseStats - Optional base metric values to start from * @returns Flat object with name, computed metrics, and all attributes */ declare function toSnapshot<C extends ConfigSpec>(engine: Engine<C>, item: ItemSpec<C>, baseStats?: Partial<Record<MetricOf<C>, number>>): { readonly name: string; readonly metrics: Record<MetricOf<C>, number>; } & Attributes<C>; /** * Base error class for all mod engine errors */ declare abstract class ModEngineError extends Error { readonly code: string; readonly path: string | undefined; constructor(message: string, code: string, path?: string); } /** * Error thrown when schema configuration is invalid */ declare class SchemaError extends ModEngineError { constructor(message: string, path?: string); } /** * Error thrown when validation fails */ declare class ValidationError extends ModEngineError { constructor(message: string, path?: string); } /** * Error thrown when an operation fails */ declare class OperationError extends ModEngineError { constructor(message: string, path?: string); } /** * Error thrown when condition evaluation fails */ declare class ConditionError extends ModEngineError { constructor(message: string, path?: string); } /** * Error thrown when evaluation fails */ declare class EvaluationError extends ModEngineError { constructor(message: string, path?: string); } /** * Error thrown when serialization/deserialization fails */ declare class SerializationError extends ModEngineError { constructor(message: string, path?: string); } export { type AttrKeyOf, type AttrValueOf, type AttributeSchema, type Attributes, type BooleanAttributeSchema, Builder, type BuiltinOperation, type Condition, type ConditionBuilder, ConditionError, type ConfigSpec, type Engine, EngineBuilder, type EnumAttributeSchema, type EvalContext, EvaluationError, type EvaluationResult, type ItemSpec, type MetricOf, ModEngineError, type Modifier, type ModifierApplication, type NumberAttributeSchema, OperationError, type OperationImpl, type OperationInfo, type OperationOf, SchemaError, SerializationError, type SerializedData, type Stacking, type StringAttributeSchema, ValidationError, type ValidationError$1 as ValidationErrorType, type ValidationResult, builtinOps, createBuiltInOperations, createConditionBuilder, createEngine, createEngineBuilder, createMetricsSnapshot, deepClone, defineConfig, deserializeEvaluationResult, deserializeItem, deserializeModifiers, evaluateCondition, evaluateItem, explainEvaluation, fromJSON, multiplyOperation, serializeEvaluationResult, serializeItem, serializeModifiers, subtractOperation, sumOperation, toJSON, toSnapshot, validateCondition, validateConfig, validateItem, validateMetricsCompleteness, validateNumericResult, validateOperations };