- Update ResolverPlugin interface with allStates parameter - Modify CustomResolver to pass all plugin states - Update built-in plugins for compatibility - Add comprehensive tests for inter-plugin dependencies - Add detailed documentation with examples
7.1 KiB
Custom Resolvers
Overview
The CustomResolver
class provides a flexible system for resolving property conflicts in a distributed system. This document covers the implementation details, including the support for inter-plugin dependencies.
Current Implementation
Core Components
-
ResolverPlugin Interface
- Defines the contract for all resolver implementations
- Key methods:
initialize()
: Creates initial stateupdate()
: Processes new values with timestampsresolve()
: Produces final value from accumulated state
-
CustomResolver Class
- Manages resolution of entity properties using configured plugins
- Implements the core resolution logic:
initializer
: Creates initial state structurereducer
: Processes deltas and updates state using pluginsresolver
: Produces final resolved values
-
Built-in Plugins
LastWriteWinsPlugin
: Keeps the most recent valueFirstWriteWinsPlugin
: Keeps the first value seenConcatenationPlugin
: Combines string values with a separatorMajorityVotePlugin
: Selects the most common valueMinPlugin
/MaxPlugin
: Tracks minimum/maximum numeric values
Inter-Plugin Dependencies
Overview
The system now supports inter-plugin dependencies, allowing plugins to access the state of other plugins during both the update and resolve phases. This enables the creation of more sophisticated resolution strategies that can depend on multiple properties.
Implementation Details
ResolverPlugin Interface
The ResolverPlugin
interface has been updated to include an optional allStates
parameter in both the update
and resolve
methods:
interface ResolverPlugin<T = unknown> {
name: string;
// Initialize the state for a property
initialize(): T;
// Process a new value for the property
update(
currentState: T,
newValue: PropertyTypes,
delta: CollapsedDelta,
allStates?: Record<PropertyID, unknown> // Access to other plugin states
): T;
// Resolve the final value from the accumulated state
resolve(
state: T,
allStates?: Record<PropertyID, unknown> // Access to other plugin states
): PropertyTypes | undefined;
}
CustomResolver Class
The CustomResolver
class has been enhanced to:
- Collect all plugin states before processing updates
- Pass the complete state to each plugin during updates and resolution
- Maintain backward compatibility with existing plugins
Example: Discounted Price Plugin
Here's a practical example of a plugin that calculates a discounted price based on another property:
class DiscountedPricePlugin implements ResolverPlugin<{ price: number }> {
name = 'discounted-price';
initialize() {
return { price: 0 };
}
update(
state: { price: number },
newValue: PropertyTypes,
_delta: CollapsedDelta,
_allStates?: Record<PropertyID, unknown>
) {
if (typeof newValue === 'number') {
return { price: newValue };
}
return state;
}
resolve(
state: { price: number },
allStates?: Record<PropertyID, unknown>
): number | undefined {
// Access the discount value from another plugin's state
const discountState = allStates?.['discount'] as { value: number } | undefined;
if (discountState) {
// Apply discount if available
return state.price * (1 - (discountState.value / 100));
}
return state.price;
}
}
// Usage with a discount plugin
const resolver = new CustomResolver(losslessView, {
price: new DiscountedPricePlugin(),
discount: new LastWriteWinsPlugin()
});
Best Practices
-
Dependency Management:
- Clearly document which properties your plugin depends on
- Handle cases where dependencies might be undefined
- Consider using TypeScript type guards for safer property access
-
Performance Considerations:
- Access only the states you need in the
allStates
object - Consider caching resolved values if the same calculation is performed multiple times
- Access only the states you need in the
-
Testing:
- Test plugins with and without their dependencies
- Verify behavior when dependencies are updated in different orders
- Test edge cases like missing or invalid dependencies
Built-in Plugins
All built-in plugins have been updated to be compatible with the new interface:
LastWriteWinsPlugin
FirstWriteWinsPlugin
ConcatenationPlugin
MajorityVotePlugin
MinPlugin
MaxPlugin
These plugins maintain backward compatibility while supporting the new functionality.
Implementation Status
The inter-plugin dependency feature has been implemented and includes:
- Updated
ResolverPlugin
interface withallStates
parameter - Enhanced
CustomResolver
class for state sharing between plugins - Updated all built-in plugins for compatibility
- Comprehensive test coverage including:
- Basic functionality of all built-in plugins
- Inter-plugin dependency scenarios
- Edge cases and error conditions
- Complete documentation with examples
Usage Examples
Basic Usage
const resolver = new CustomResolver(losslessView, {
title: new LastWriteWinsPlugin(),
price: new LastWriteWinsPlugin(),
discount: new LastWriteWinsPlugin()
});
With Dependent Plugins
const resolver = new CustomResolver(losslessView, {
basePrice: new LastWriteWinsPlugin(),
discount: new LastWriteWinsPlugin(),
finalPrice: new DiscountedPricePlugin() // Depends on discount
});
Complex Example
const resolver = new CustomResolver(losslessView, {
// Basic properties
name: new LastWriteWinsPlugin(),
description: new ConcatenationPlugin(' '),
// Pricing
basePrice: new LastWriteWinsPlugin(),
taxRate: new LastWriteWinsPlugin(),
discount: new LastWriteWinsPlugin(),
// Calculated fields
subtotal: new SubtotalCalculator(), // Uses basePrice and quantity
tax: new TaxCalculator(), // Uses subtotal and taxRate
total: new TotalCalculator() // Uses subtotal, tax, and discount
});
Future Enhancements
- Plugin Dependencies: Explicitly declare dependencies between plugins
- Caching: Cache resolved values for better performance
- Validation: Add validation to prevent circular dependencies
- Debugging: Add logging for plugin execution order and state access
- Optimization: Lazy-load plugin states to improve performance with many properties
Example Configurations
Basic Usage
const resolver = new CustomResolver(losslessView, {
title: new LastWriteWinsPlugin(),
price: new LastWriteWinsPlugin(),
discount: new LastWriteWinsPlugin()
});
With Dependent Plugins
const resolver = new CustomResolver(losslessView, {
basePrice: new LastWriteWinsPlugin(),
discount: new LastWriteWinsPlugin(),
finalPrice: new DiscountedPricePlugin()
});
Future Enhancements
- Plugin Dependencies: Explicitly declare dependencies between plugins
- Caching: Cache resolved values for better performance
- Validation: Add validation to prevent circular dependencies
- Debugging: Add logging for plugin execution order and state access