Core Changes: - Completely rewrote CustomResolver reducer with dependency-ordered processing - Enhanced plugin initialization with proper dependency injection - Improved delta processing and property value tracking - Added robust error handling for duplicate property IDs Resolver Improvements: - Updated to use new accumulator structure - Implemented execution order processing for plugins - Enhanced debug logging and error reporting - Simplified TimestampResolver by removing unused initializer Configuration Updates: - Added TypeScript path aliases for test helpers - Improved module resolution paths Key Benefits: - More robust plugin dependency management - More efficient state updates - Enhanced type safety - Better error messages and debugging - More consistent plugin initialization This refactoring focuses on improving the robustness of the resolver, especially around plugin lifecycle management and dependency handling. The changes ensure better separation of concerns and more predictable behavior when dealing with complex plugin dependencies.
4.2 KiB
4.2 KiB
ResolverPlugin Interface
Overview
The ResolverPlugin
interface defines the contract that all resolver plugins must implement. It provides type-safe access to plugin state and dependencies.
Interface Definition
interface ResolverPlugin<T = unknown, D extends string = never> {
/**
* Unique identifier for the plugin
*/
readonly name: string;
/**
* Array of property IDs this plugin depends on
* @default []
*/
readonly dependencies?: readonly D[];
/**
* Initializes the plugin's state
* @returns Initial state object
*/
initialize(): T;
/**
* Processes a new value and updates the plugin's state
* @param currentState Current plugin state
* @param newValue New value to process
* @param delta Delta information
* @param dependencies Resolved states of all declared dependencies
* @returns Updated plugin state
*/
update(
currentState: T,
newValue: PropertyTypes,
delta: CollapsedDelta,
dependencies: DependencyStates
): T;
/**
* Resolves the final value from the current state
* @param state Current plugin state
* @param dependencies Resolved states of all declared dependencies
* @returns Resolved value or undefined if no value should be set
*/
resolve(
state: T,
dependencies: DependencyStates
): PropertyTypes | undefined;
}
Type Parameters
Parameter | Description |
---|---|
T |
Type of the plugin's internal state |
D |
Union type of dependency names (must extend string ) |
Methods
initialize()
Initializes the plugin's internal state. Called once when the resolver is created.
Returns: T
- The initial state object
update(currentState, newValue, delta, dependencies)
Processes a new value and updates the plugin's state.
Parameters:
currentState: T
- Current plugin statenewValue: PropertyTypes
- New value to processdelta: CollapsedDelta
- Delta informationdependencies: DependencyStates
- Resolved states of all declared dependencies
Returns: T
- Updated plugin state
resolve(state, dependencies)
Resolves the final value from the current state.
Parameters:
state: T
- Current plugin statedependencies: DependencyStates
- Resolved states of all declared dependencies
Returns: PropertyTypes | undefined
- Resolved value or undefined if no value should be set
Example Implementation
class CounterPlugin implements ResolverPlugin<CounterState> {
initialize(): CounterState {
return { count: 0 };
}
update(
state: CounterState,
_newValue: unknown,
_delta: CollapsedDelta,
_deps: {}
): CounterState {
return { count: state.count + 1 };
}
resolve(state: CounterState): number {
return state.count;
}
}
Best Practices
- Immutability: Always return new state objects instead of mutating
- Purity: Keep methods pure and side-effect free
- Error Handling: Handle unexpected input gracefully
- Documentation: Document expected types and behavior
Common Patterns
Accessing Dependencies
class PriceCalculator implements ResolverPlugin<PriceState, 'basePrice' | 'taxRate'> {
readonly dependencies = ['basePrice', 'taxRate'] as const;
update(
_state: PriceState,
_newValue: unknown,
_delta: CollapsedDelta,
deps: DependencyStates,
): PriceState {
const basePrice = deps.basePrice as number;
const taxRate = deps.taxRate as number;
return { total: basePrice * (1 + taxRate) };
}
// ...
}
Optional Dependencies
class OptionalDepPlugin implements ResolverPlugin<State, 'required' | 'optional?'> {
readonly dependencies = ['required', 'optional?'] as const;
update(
state: State,
_newValue: unknown,
_delta: CollapsedDelta,
deps: any,
): State {
const required = deps.required as number; // Always present
const optional = deps['optional?'] as number | undefined; // Might be undefined
// ...
}
// ...
}