cleanup: remove dead code, stale shipped scaffolding, internal docs
Dead code with zero callers (deprecation notes promised removal): - RelationCSR index: always-off option (useRelationCsrIndex), never enabled in production, wired through RelationManager/RelationUpdates/ RelationLookup. Removed the module and all wiring. - getAggregatedBlurredValue (RelationManager) and aggregateBlurredValues (ValueManager): @deprecated shims, zero callers. - QualitativeRelationalComparatorRule._aggregateBlurredValues: @deprecated shim, zero callers. Kept compareRelationValues: non-deprecated public API, coherent and clock-threaded, just currently callerless. Stale scaffolding shipping in the published artifact (files: src/): - src/ast/tests/* and src/ast/examples/*: orphaned duplicates of tests/ast/, zero references anywhere, 11 files in the tarball. Removed; the live copies live in tests/ast/. Internal docs moved out of the shipped surface (1266 lines) to docs/internal/: VALUE_OPTIMIZATION_SUMMARY, rules API_SPECIFICATION, ast README, qualitative README — repo-kept, not packaged. Tarball .md count: 11 -> 1. Rigor 251/251, full suite 853/791/0.
This commit is contained in:
@@ -0,0 +1,273 @@
|
||||
# Value Handling Optimization Summary
|
||||
|
||||
## Overview
|
||||
|
||||
This document outlines the comprehensive optimization of value handling in the RuleEvaluator system, which eliminates wasteful recalculations and dramatically improves performance for complex rule evaluations.
|
||||
|
||||
## Problems Identified
|
||||
|
||||
### 1. **Redundant Value Fetching**
|
||||
- **Issue**: Multiple rules in the same evaluation tree were independently fetching the same values from relations
|
||||
- **Example**: A union rule with 3 child RelationalComparatorRules would fetch `user.balance` 3 times
|
||||
- **Impact**: O(n) redundant database/relation queries per evaluation
|
||||
|
||||
### 2. **Missing Value Propagation**
|
||||
- **Issue**: LogicalOperators (union/intersection) weren't combining `collectedValues` from child rules
|
||||
- **Impact**: Lost value information that could be reused by parent rules
|
||||
|
||||
### 3. **Wasteful Inference Triggering**
|
||||
- **Issue**: Inference engine was called even when sufficient values existed but weren't being considered
|
||||
- **Impact**: Expensive similarity calculations when values were already available
|
||||
|
||||
### 4. **No Context Sharing**
|
||||
- **Issue**: Each rule evaluation was isolated with no mechanism to share computed values
|
||||
- **Impact**: Repeated work across the evaluation tree
|
||||
|
||||
## Solution: ValueContext System
|
||||
|
||||
### Core Components
|
||||
|
||||
#### 1. **ValueContext Class** (`ValueContext.js`)
|
||||
```javascript
|
||||
export class ValueContext {
|
||||
// Centralized value cache and aggregation
|
||||
// - Value cache: entity:relation -> raw values
|
||||
// - Aggregated cache: entity:relation:method -> aggregated result
|
||||
// - Performance tracking: hits/misses/fetch counts
|
||||
}
|
||||
```
|
||||
|
||||
**Key Features:**
|
||||
- **Caching**: Eliminate redundant fetches with `entity:relation` keyed cache
|
||||
- **Aggregation**: Pre-compute and cache aggregated values (sum, max, min, etc.)
|
||||
- **Smart Inference**: Check value sufficiency before triggering expensive inference
|
||||
- **Performance Tracking**: Monitor cache hit rates and fetch reduction
|
||||
|
||||
#### 2. **Enhanced RuleEvaluator** (`RuleEvaluator.js`)
|
||||
```javascript
|
||||
evaluateRule(userId, userKey, objectId, objectKey, rule, visited, currentRelation, options = {}) {
|
||||
const { valueContext = null } = options;
|
||||
const finalValueContext = valueContext || new ValueContext(this.arbiter);
|
||||
|
||||
// Pass valueContext to all child rule evaluations
|
||||
// Add collected values to context
|
||||
// Use context to make smarter inference decisions
|
||||
}
|
||||
```
|
||||
|
||||
**Key Improvements:**
|
||||
- **Context Propagation**: Pass ValueContext through entire evaluation tree
|
||||
- **Value Collection**: Automatically add `collectedValues` to context
|
||||
- **Smart Inference**: Check `hasSufficientValues()` before expensive inference
|
||||
- **Batch Optimization**: Shared ValueContext across batch evaluations
|
||||
|
||||
#### 3. **Optimized LogicalOperators** (`LogicalOperators_v2.js`)
|
||||
```javascript
|
||||
evaluateUnion(userId, userKey, objectId, objectKey, rule, visited, currentRelation, options = {}) {
|
||||
const allCollectedValues = []; // Track from all child rules
|
||||
|
||||
for (const child of childRules) {
|
||||
const res = this.ruleEvaluator.evaluateRule(/* ... */, options);
|
||||
|
||||
// Merge collected values from child
|
||||
if (res.collectedValues) {
|
||||
allCollectedValues.push(...res.collectedValues);
|
||||
if (valueContext) {
|
||||
valueContext.addCollectedValues(res.collectedValues, child.type, child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { /* ... */, collectedValues: allCollectedValues };
|
||||
}
|
||||
```
|
||||
|
||||
**Key Improvements:**
|
||||
- **Value Merging**: Combine `collectedValues` from all child rules
|
||||
- **Context Integration**: Add child values to shared ValueContext
|
||||
- **Preserved Information**: Ensure no value information is lost in logical operations
|
||||
|
||||
#### 4. **Optimized RelationalComparatorRule** (`RelationalComparatorRule_v2.js`)
|
||||
```javascript
|
||||
_evaluateOperand(/* ... */, options, side) {
|
||||
const { valueContext = null } = options;
|
||||
|
||||
// 1. Check if rule already provided values (collectedValues)
|
||||
if (ruleResult.collectedValues?.length > 0) {
|
||||
return this._useCollectedValues(ruleResult.collectedValues);
|
||||
}
|
||||
|
||||
// 2. Check ValueContext cache before extraction
|
||||
if (valueContext?.hasValues(entityId, relationName)) {
|
||||
return valueContext.getAggregatedValue(entityId, relationName, aggregator);
|
||||
}
|
||||
|
||||
// 3. Fallback to original extraction (but cache results)
|
||||
return this._extractValuesWithContext(/* ... */, valueContext);
|
||||
}
|
||||
```
|
||||
|
||||
**Key Improvements:**
|
||||
- **Cached Value Reuse**: Check ValueContext before fetching from relations
|
||||
- **Collected Value Utilization**: Prefer values from child rule `collectedValues`
|
||||
- **Fallback Safety**: Maintain backward compatibility with original extraction logic
|
||||
|
||||
## Performance Benefits
|
||||
|
||||
### Quantitative Improvements
|
||||
|
||||
#### Value Fetch Reduction
|
||||
- **Before**: O(n × m) fetches where n = rule count, m = unique values per rule
|
||||
- **After**: O(k) fetches where k = unique entity:relation combinations
|
||||
- **Typical Reduction**: 60-90% fewer database/relation queries
|
||||
|
||||
#### Cache Effectiveness
|
||||
- **Hit Rate**: 70-95% in complex rule evaluations
|
||||
- **Memory Usage**: Minimal overhead (values cached only for evaluation duration)
|
||||
- **Invalidation**: Automatic cleanup after evaluation completion
|
||||
|
||||
#### Inference Optimization
|
||||
- **Before**: Inference triggered on every zero-result rule
|
||||
- **After**: Inference only when `hasSufficientValues()` returns false
|
||||
- **Typical Reduction**: 40-70% fewer expensive inference operations
|
||||
|
||||
### Example Performance Case
|
||||
|
||||
**Scenario**: User purchasing premium feature
|
||||
```
|
||||
Rule Structure:
|
||||
└── UNION
|
||||
├── RELATIONAL_COMPARATOR (user.balance >= feature.price)
|
||||
└── INTERSECTION
|
||||
├── CHAIN (user -> subscriptions)
|
||||
└── UNION
|
||||
├── RELATIONAL_COMPARATOR (user.credit >= feature.price)
|
||||
└── RELATIONAL_COMPARATOR (user.balance >= feature.price)
|
||||
```
|
||||
|
||||
**Old System Value Fetches:**
|
||||
- `user.balance`: 2 times (redundant!)
|
||||
- `user.credit`: 1 time
|
||||
- `feature.price`: 3 times (redundant!)
|
||||
- **Total**: 6 fetches
|
||||
|
||||
**New System Value Fetches:**
|
||||
- `user.balance`: 1 time (cached)
|
||||
- `user.credit`: 1 time (cached)
|
||||
- `feature.price`: 1 time (cached)
|
||||
- **Total**: 3 fetches (50% reduction)
|
||||
|
||||
## Implementation Guidelines
|
||||
|
||||
### 1. **Using ValueContext**
|
||||
|
||||
```javascript
|
||||
// Single evaluation with context
|
||||
const valueContext = new ValueContext(arbiter);
|
||||
const result = ruleEvaluator.evaluateRule(/* ... */, { valueContext });
|
||||
|
||||
// Batch evaluation (automatic shared context)
|
||||
const results = ruleEvaluator.batchEvaluateRules(queries);
|
||||
|
||||
// Performance monitoring
|
||||
const stats = valueContext.getStats();
|
||||
console.log(`Cache hit rate: ${stats.hitRate * 100}%`);
|
||||
```
|
||||
|
||||
### 2. **Adding Value Collection to Custom Rules**
|
||||
|
||||
```javascript
|
||||
export class CustomRule extends BaseRule {
|
||||
_evaluateRule(userId, userKey, objectId, objectKey, rule, visited, currentRelation, options) {
|
||||
const { valueContext } = options;
|
||||
|
||||
// Collect values during evaluation
|
||||
const collectedValues = [];
|
||||
|
||||
// ... rule logic ...
|
||||
|
||||
// Add values you've collected
|
||||
const collectedValue = this._createCollectedValue(
|
||||
value, possibility, path, source, metadata
|
||||
);
|
||||
collectedValues.push(collectedValue);
|
||||
|
||||
return this._createStandardResult(authResult, collectedValues);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 3. **Performance Monitoring**
|
||||
|
||||
```javascript
|
||||
// Enable value context stats in production
|
||||
const result = ruleEvaluator.evaluateRule(/* ... */, {
|
||||
valueContext,
|
||||
trackPerformance: true
|
||||
});
|
||||
|
||||
const stats = ruleEvaluator.getValueContextStats({ valueContext });
|
||||
logger.info('Rule evaluation performance', {
|
||||
cacheHitRate: stats.hitRate,
|
||||
fetchReduction: stats.fetchCount,
|
||||
executionTime: result.executionTime
|
||||
});
|
||||
```
|
||||
|
||||
## Migration Strategy
|
||||
|
||||
### Phase 1: Backward Compatibility
|
||||
- ✅ **Complete**: All optimizations work alongside existing code
|
||||
- ✅ **Complete**: No breaking changes to existing rule configurations
|
||||
- ✅ **Complete**: Automatic fallback to original logic when ValueContext unavailable
|
||||
|
||||
### Phase 2: Gradual Adoption
|
||||
- **Recommended**: Use ValueContext in new rule evaluations
|
||||
- **Recommended**: Enable for batch operations (automatic)
|
||||
- **Optional**: Retrofit existing custom rules to use ValueContext
|
||||
|
||||
### Phase 3: Full Optimization
|
||||
- **Future**: Require ValueContext for all evaluations
|
||||
- **Future**: Remove fallback extraction logic
|
||||
- **Future**: Add advanced caching strategies (LRU, TTL, etc.)
|
||||
|
||||
## Validation & Testing
|
||||
|
||||
### Correctness Validation
|
||||
- ✅ **Complete**: All optimizations maintain identical results to original system
|
||||
- ✅ **Complete**: Comprehensive test coverage with `ValueOptimizationDemo`
|
||||
- ✅ **Complete**: Edge case handling (missing values, cache misses, etc.)
|
||||
|
||||
### Performance Testing
|
||||
- ✅ **Available**: Demo shows 50%+ fetch reduction in typical scenarios
|
||||
- ✅ **Available**: Cache hit rates consistently above 70%
|
||||
- ✅ **Available**: Performance tracking and monitoring built-in
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
### 1. **Advanced Caching**
|
||||
- **LRU Eviction**: Limit memory usage in long-running processes
|
||||
- **TTL Support**: Expire stale values automatically
|
||||
- **Persistent Cache**: Cross-evaluation value persistence
|
||||
|
||||
### 2. **Smart Prefetching**
|
||||
- **Dependency Analysis**: Pre-fetch values based on rule structure
|
||||
- **Batch Loading**: Group value fetches by entity/relation patterns
|
||||
- **Predictive Caching**: Learn from evaluation patterns
|
||||
|
||||
### 3. **Distributed Caching**
|
||||
- **Redis Integration**: Share ValueContext across service instances
|
||||
- **Cluster Coordination**: Synchronized cache invalidation
|
||||
- **Partitioning**: Shard value cache by entity patterns
|
||||
|
||||
## Conclusion
|
||||
|
||||
The ValueContext optimization system provides:
|
||||
|
||||
1. **🚀 Performance**: 50-90% reduction in redundant value fetches
|
||||
2. **💡 Intelligence**: Smart inference decisions based on available values
|
||||
3. **🔄 Compatibility**: Zero breaking changes to existing code
|
||||
4. **📊 Observability**: Built-in performance monitoring and stats
|
||||
5. **🛡️ Reliability**: Maintained result consistency with comprehensive testing
|
||||
|
||||
This optimization eliminates the core inefficiencies in value handling while maintaining full backward compatibility and providing a foundation for future enhancements.
|
||||
@@ -0,0 +1,340 @@
|
||||
# AST Module - DSL Compiler for Zanzibar-Graph
|
||||
|
||||
This module provides a complete Abstract Syntax Tree (AST) system for parsing and compiling the Evidence DSL into rule configurations that interface with the zanzibar-graph authorization system.
|
||||
|
||||
## Overview
|
||||
|
||||
The AST module consists of three main components:
|
||||
|
||||
1. **Parser** - Converts DSL text into AST nodes
|
||||
2. **Generator** - Converts AST nodes into rule configurations
|
||||
3. **Compiler** - Orchestrates the parsing and generation process
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
DSL Text → Parser → AST Nodes → Generator → Rule Configs → setRelationConfig()
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- **Complete DSL Support** - Supports all DSL constructs from the specification
|
||||
- **Modular Design** - Clean separation of concerns with pluggable components
|
||||
- **Error Handling** - Comprehensive error reporting and validation
|
||||
- **Program Management** - Support for multiple compiled programs
|
||||
- **Rule Generation** - Automatic conversion to existing rule system
|
||||
- **Extensible** - Easy to add new node types and generators
|
||||
|
||||
## Quick Start
|
||||
|
||||
```javascript
|
||||
import { DSLCompiler } from './src/ast/index.js';
|
||||
|
||||
// Create compiler with arbiter instance
|
||||
const compiler = new DSLCompiler(arbiter);
|
||||
|
||||
// Compile DSL text
|
||||
const result = compiler.compile(dslText, 'my-program');
|
||||
|
||||
if (result.success) {
|
||||
console.log(`Generated ${result.generatedCount} rules`);
|
||||
} else {
|
||||
console.error('Compilation errors:', result.errors);
|
||||
}
|
||||
```
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. AST Nodes (`/nodes/`)
|
||||
|
||||
The AST nodes represent the parsed structure of the DSL:
|
||||
|
||||
- **BaseNode** - Base class for all AST nodes
|
||||
- **ProgramNode** - Root node containing all definitions
|
||||
- **DefinitionNode** - Type definitions with fields and behaviors
|
||||
- **FactNode** - Fact definitions with parameters and properties
|
||||
- **EvidenceNode** - Evidence definitions with bodies
|
||||
- **MeasureNode** - Measure definitions for value collection
|
||||
- **ExpressionNode** - Expressions, variables, and literals
|
||||
- **And many more...**
|
||||
|
||||
### 2. Parser (`/parser/`)
|
||||
|
||||
The DSL parser converts text into AST nodes:
|
||||
|
||||
```javascript
|
||||
import { DSLParser } from './parser/DSLParser.js';
|
||||
|
||||
const parser = new DSLParser();
|
||||
const program = parser.parse(dslText);
|
||||
```
|
||||
|
||||
### 3. Generator (`/generator/`)
|
||||
|
||||
The rule generator converts AST nodes into rule configurations:
|
||||
|
||||
```javascript
|
||||
import { RuleGenerator } from './generator/RuleGenerator.js';
|
||||
|
||||
const generator = new RuleGenerator(arbiter);
|
||||
const result = generator.generateRules(program);
|
||||
```
|
||||
|
||||
### 4. Compiler (`DSLCompiler.js`)
|
||||
|
||||
The main compiler orchestrates the entire process:
|
||||
|
||||
```javascript
|
||||
import { DSLCompiler } from './DSLCompiler.js';
|
||||
|
||||
const compiler = new DSLCompiler(arbiter);
|
||||
const result = compiler.compile(dslText, 'program-name');
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Compilation
|
||||
|
||||
```javascript
|
||||
const dsl = `
|
||||
definition User {
|
||||
role: string
|
||||
isActive: boolean
|
||||
}
|
||||
|
||||
fact hasRole(user: User, role: string) CACHE eager
|
||||
|
||||
evidence canRead(user: User, doc: Document) {
|
||||
hasRole(user, 'admin')
|
||||
}
|
||||
`;
|
||||
|
||||
const result = compiler.compile(dsl, 'auth');
|
||||
```
|
||||
|
||||
### Complex DSL with Defeasible Logic
|
||||
|
||||
```javascript
|
||||
const complexDSL = `
|
||||
evidence canAccessCritical(user: User, resource: Resource) {
|
||||
// Strict requirement
|
||||
ALWAYS user.isActive
|
||||
|
||||
// Defeasible access
|
||||
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
|
||||
|
||||
// Requirements
|
||||
REQUIRES hasClearance(user, resource.level)
|
||||
|
||||
// Fusion evidence
|
||||
fusion majority {
|
||||
user.isTrusted
|
||||
user.hasRecentActivity
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
const result = compiler.compile(complexDSL, 'critical-access');
|
||||
```
|
||||
|
||||
### Multiple Programs
|
||||
|
||||
```javascript
|
||||
const programs = {
|
||||
'auth': `
|
||||
fact hasRole(user: User, role: string)
|
||||
evidence canRead(user: User, doc: Document) {
|
||||
hasRole(user, 'admin')
|
||||
}
|
||||
`,
|
||||
'finance': `
|
||||
fact hasBalance(user: User, amount: number)
|
||||
evidence canWithdraw(user: User, amount: number) {
|
||||
hasBalance(user, amount)
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
const result = compiler.compileMultiple(programs);
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### DSLCompiler
|
||||
|
||||
#### Methods
|
||||
|
||||
- `compile(dslText, programName)` - Compile DSL text into rules
|
||||
- `compileMultiple(programs)` - Compile multiple programs
|
||||
- `validate(dslText)` - Validate DSL without compilation
|
||||
- `getCompiledProgram(name)` - Get compiled program by name
|
||||
- `getAllCompiledPrograms()` - Get all compiled programs
|
||||
- `removeCompiledProgram(name)` - Remove compiled program
|
||||
- `clearCompiledPrograms()` - Clear all programs
|
||||
- `getCompilationStats()` - Get compilation statistics
|
||||
|
||||
#### Properties
|
||||
|
||||
- `arbiter` - The arbiter instance
|
||||
- `parser` - The DSL parser
|
||||
- `generator` - The rule generator
|
||||
|
||||
### DSLParser
|
||||
|
||||
#### Methods
|
||||
|
||||
- `parse(dslText)` - Parse DSL text into AST
|
||||
- `getErrors()` - Get parser errors
|
||||
|
||||
### RuleGenerator
|
||||
|
||||
#### Methods
|
||||
|
||||
- `generateRules(program)` - Generate rules from AST
|
||||
- `getErrors()` - Get generator errors
|
||||
- `getGeneratedRules()` - Get generated rules
|
||||
|
||||
## Supported DSL Constructs
|
||||
|
||||
### Type Definitions
|
||||
|
||||
```typescript
|
||||
definition User {
|
||||
role: string
|
||||
isActive: boolean
|
||||
lastActive: timestamp BEHAVES {
|
||||
decaying down hourly
|
||||
} CACHE lazy
|
||||
}
|
||||
```
|
||||
|
||||
### Facts
|
||||
|
||||
```typescript
|
||||
fact hasRole(user: User, role: string) CACHE eager
|
||||
fact isMember(user: User, group: Group) transitive CACHE lazy
|
||||
```
|
||||
|
||||
### Evidence
|
||||
|
||||
```typescript
|
||||
evidence canRead(user: User, doc: Document) {
|
||||
// Direct evidence
|
||||
owns(user, doc)
|
||||
|
||||
// Pattern matching
|
||||
isMember(user, *group) {
|
||||
canRead(group, doc)
|
||||
} limit 5
|
||||
|
||||
// Defeasible logic
|
||||
WHEN hasRole(user, 'admin') UNLESS isSuspended(user)
|
||||
}
|
||||
```
|
||||
|
||||
### Measures
|
||||
|
||||
```typescript
|
||||
measure userBalance(user: User) {
|
||||
user.balance
|
||||
} PROVIDES number
|
||||
|
||||
measure userPermissions(user: User) {
|
||||
fusion max {
|
||||
user.role.permissions
|
||||
user.group.permissions
|
||||
}
|
||||
} PROVIDES Permission[]
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
The compiler provides comprehensive error handling:
|
||||
|
||||
```javascript
|
||||
const result = compiler.compile(dslText);
|
||||
|
||||
if (!result.success) {
|
||||
console.error('Compilation failed:');
|
||||
result.errors.forEach(error => console.error(` - ${error}`));
|
||||
}
|
||||
|
||||
if (result.warnings.length > 0) {
|
||||
console.warn('Warnings:');
|
||||
result.warnings.forEach(warning => console.warn(` - ${warning}`));
|
||||
}
|
||||
```
|
||||
|
||||
## Testing
|
||||
|
||||
Run the test suite:
|
||||
|
||||
```javascript
|
||||
import { runDSLCompilerTests } from './tests/DSLCompiler.test.js';
|
||||
|
||||
const testResults = runDSLCompilerTests(arbiter);
|
||||
console.log('Test Results:', testResults);
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
See the examples directory for comprehensive usage examples:
|
||||
|
||||
- `examples/DSLExample.js` - Complete usage examples
|
||||
- `tests/DSLCompiler.test.js` - Test suite
|
||||
|
||||
## Integration with Existing System
|
||||
|
||||
The AST module integrates seamlessly with the existing zanzibar-graph system:
|
||||
|
||||
1. **Parser** converts DSL text to AST nodes
|
||||
2. **Generator** converts AST nodes to rule configurations
|
||||
3. **Compiler** applies rules via `arbiter.setRelationConfig()`
|
||||
|
||||
The generated rules are compatible with all existing rule types:
|
||||
- Direct rules
|
||||
- Computed rules
|
||||
- Parent rules
|
||||
- Tuple-to-userset rules
|
||||
- Similarity rules
|
||||
- Multi-hop rules
|
||||
- Logical operators
|
||||
- Defeasible logic
|
||||
|
||||
## Extensibility
|
||||
|
||||
The AST system is designed to be extensible:
|
||||
|
||||
1. **Add new node types** by extending `BaseNode`
|
||||
2. **Add new parsers** by extending `DSLParser`
|
||||
3. **Add new generators** by extending `RuleGenerator`
|
||||
4. **Add new compilers** by extending `DSLCompiler`
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **Lazy evaluation** - AST nodes are created on demand
|
||||
- **Efficient parsing** - Token-based parsing with minimal memory usage
|
||||
- **Rule caching** - Generated rules are cached for reuse
|
||||
- **Batch processing** - Support for compiling multiple programs at once
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
- **Incremental compilation** - Only recompile changed parts
|
||||
- **Parallel processing** - Compile multiple programs in parallel
|
||||
- **Advanced optimizations** - Rule optimization and simplification
|
||||
- **IDE support** - Language server protocol support
|
||||
- **Visualization** - AST visualization tools
|
||||
|
||||
## Contributing
|
||||
|
||||
When contributing to the AST module:
|
||||
|
||||
1. Follow the existing code structure
|
||||
2. Add comprehensive tests for new features
|
||||
3. Update documentation
|
||||
4. Ensure backward compatibility
|
||||
5. Follow the established patterns
|
||||
|
||||
## License
|
||||
|
||||
This module is part of the zanzibar-graph project and follows the same license terms.
|
||||
@@ -0,0 +1,256 @@
|
||||
# Qualitative Capacity System
|
||||
|
||||
This module provides a complete implementation of qualitative capacities (q-capacities) as described in the research paper on qualitative capacities and their applications to evidential reasoning, decision making, and imprecise possibility.
|
||||
|
||||
## Overview
|
||||
|
||||
A qualitative capacity γ: 2^W → L is a monotonic set-function where:
|
||||
- γ(∅) = 0, γ(W) = 1
|
||||
- If A ⊆ B, then γ(A) ≤ γ(B)
|
||||
- L is a finite totally ordered scale with order-reversing negation
|
||||
|
||||
The core design principle is to use the Qualitative Möbius Transform (QMT) γ# as the canonical internal representation for any q-capacity γ.
|
||||
|
||||
## Core Components
|
||||
|
||||
### 1. SetUtils
|
||||
|
||||
Utility functions for working with Sets as Map keys, providing canonical string representations for consistent and efficient Map operations.
|
||||
|
||||
```javascript
|
||||
import { getSetKey, setFromKey, setsEqual } from './src/qualitative/index.js';
|
||||
|
||||
const set = new Set(['a', 'b', 'c']);
|
||||
const key = getSetKey(set); // "a,b,c"
|
||||
const reconstructed = setFromKey(key); // Set(['a', 'b', 'c'])
|
||||
const areEqual = setsEqual(set, reconstructed); // true
|
||||
```
|
||||
|
||||
### 2. QualitativeScale
|
||||
|
||||
Finite totally ordered scales with order-reversing negation.
|
||||
|
||||
```javascript
|
||||
import { QualitativeScale } from './src/qualitative/index.js';
|
||||
|
||||
// Create a 5-point scale
|
||||
const scale = QualitativeScale.fivePoint(); // [0, 0.25, 0.5, 0.75, 1]
|
||||
|
||||
// Test operations
|
||||
console.log(scale.min(0.25, 0.75)); // 0.25
|
||||
console.log(scale.max(0.25, 0.75)); // 0.75
|
||||
console.log(scale.negate(0.25)); // 0.75 (order-reversing)
|
||||
```
|
||||
|
||||
### 3. QualitativeCapacity
|
||||
|
||||
Q-capacities with QMT internal representation.
|
||||
|
||||
```javascript
|
||||
import { QualitativeCapacity } from './src/qualitative/index.js';
|
||||
|
||||
const stateSpace = ['s1', 's2', 's3'];
|
||||
const scale = QualitativeScale.ternary();
|
||||
|
||||
// Create a simple support capacity
|
||||
const ssc = QualitativeCapacity.createSimpleSupport(
|
||||
stateSpace,
|
||||
['s1'],
|
||||
0.5,
|
||||
scale
|
||||
);
|
||||
|
||||
// Get capacity values
|
||||
console.log(ssc.getCapacity(['s1'])); // 0.5
|
||||
console.log(ssc.getCapacity(['s1', 's2'])); // 1
|
||||
|
||||
// Check if it's a necessity measure
|
||||
console.log(ssc.isNecessityMeasure()); // true
|
||||
```
|
||||
|
||||
### 4. QualitativeFusion
|
||||
|
||||
Theoretically sound fusion rules for capacity combination.
|
||||
|
||||
```javascript
|
||||
import { QualitativeFusion } from './src/qualitative/index.js';
|
||||
|
||||
// Create multiple capacities
|
||||
const cap1 = QualitativeCapacity.createSimpleSupport(stateSpace, ['s1'], 0.5, scale);
|
||||
const cap2 = QualitativeCapacity.createSimpleSupport(stateSpace, ['s2'], 0.5, scale);
|
||||
|
||||
// Normalized conjunctive fusion (theoretically sound)
|
||||
const fused = QualitativeFusion.normalizedConjunctive([cap1, cap2]);
|
||||
|
||||
// Disjunctive fusion
|
||||
const disjunctive = QualitativeFusion.disjunctive(cap1, cap2);
|
||||
|
||||
// Sugeno integral for decision making
|
||||
const decisionFunction = { 's1': 0.5, 's2': 1, 's3': 0.5 };
|
||||
const sugenoValue = QualitativeFusion.sugenoIntegral(fused, decisionFunction);
|
||||
```
|
||||
|
||||
### 5. OWAQualitativeFusion
|
||||
|
||||
Bag algebras for sophisticated qualitative aggregation.
|
||||
|
||||
```javascript
|
||||
import { OWAQualitativeFusion } from './src/qualitative/index.js';
|
||||
|
||||
const values = [0.25, 0.5, 0.75];
|
||||
const metas = [{ source: 'rule1' }, { source: 'rule2' }, { source: 'rule3' }];
|
||||
|
||||
// Different aggregation modes
|
||||
const maxResult = OWAQualitativeFusion.max(values, metas, scale);
|
||||
const majorityResult = OWAQualitativeFusion.majority(values, metas, scale);
|
||||
const optimisticResult = OWAQualitativeFusion.optimistic(values, metas, scale);
|
||||
|
||||
// Configurable activation threshold
|
||||
const selectiveResult = OWAQualitativeFusion.max(values, metas, scale, 0.8);
|
||||
|
||||
// Proper Sugeno integral
|
||||
const sugenoResult = OWAQualitativeFusion.sugenoIntegral(capacity, decisionFunction);
|
||||
```
|
||||
|
||||
### 6. QMTOWAFusion
|
||||
|
||||
Theoretically sound OWA-like operators that work directly on QMTs.
|
||||
|
||||
```javascript
|
||||
import { QMTOWAFusion } from './src/qualitative/index.js';
|
||||
|
||||
// These methods preserve monotonicity by working on QMTs directly
|
||||
const optimistic = QMTOWAFusion.optimisticFusion([cap1, cap2]);
|
||||
const pessimistic = QMTOWAFusion.pessimisticFusion([cap1, cap2]);
|
||||
const majority = QMTOWAFusion.majorityFusion([cap1, cap2]);
|
||||
const priority = QMTOWAFusion.priorityFusion([cap1, cap2], [10, 5]);
|
||||
```
|
||||
|
||||
## Theoretical Considerations
|
||||
|
||||
### Pointwise OWA Fusion Warning
|
||||
|
||||
The `pointwiseOWAFusion` method (formerly `fuseCapacities`) performs pointwise OWA fusion on capacity values, which **does NOT guarantee** that the result is a valid qualitative capacity. The resulting set-function may violate the fundamental monotonicity property: A⊆B ⟹ γ(A)≤γ(B).
|
||||
|
||||
**Use this method only for experimental purposes or when monotonicity is not required.**
|
||||
|
||||
For theoretically sound capacity fusion, use:
|
||||
- `QualitativeFusion.normalizedConjunctive()`
|
||||
- `QualitativeFusion.disjunctive()`
|
||||
- `QMTOWAFusion` methods
|
||||
|
||||
### Qualitative OWA Operator
|
||||
|
||||
The qualitative OWA operator implements a novel weighted maximum where weights act as "gates" that must pass a threshold to allow their corresponding values to be considered. This is distinct from the standard Sugeno integral but provides a practical way to introduce weight influence in purely ordinal contexts.
|
||||
|
||||
The activation threshold is configurable (default 0.5) to allow for more or less "selective" aggregations.
|
||||
|
||||
### Sugeno Integral
|
||||
|
||||
The Sugeno integral is the qualitative counterpart to the Choquet integral and provides a theoretically sound way to aggregate qualitative values with respect to a capacity:
|
||||
|
||||
S_γ(f) = max_{i=1}^n min(f_{(i)}, γ(A_{(i)}))
|
||||
|
||||
where f_{(i)} are the sorted values in descending order and A_{(i)} = {w_{(1)}, ..., w_{(i)}}.
|
||||
|
||||
## Applications
|
||||
|
||||
### 1. Evidential Reasoning
|
||||
|
||||
Combine testimonies from different sources using Simple Support Capacities and normalized conjunctive fusion.
|
||||
|
||||
```javascript
|
||||
// Create testimonies as Simple Support Capacities
|
||||
const testimony1 = QualitativeCapacity.createSimpleSupport(
|
||||
stateSpace,
|
||||
['s1'],
|
||||
0.8,
|
||||
scale
|
||||
);
|
||||
|
||||
const testimony2 = QualitativeCapacity.createSimpleSupport(
|
||||
stateSpace,
|
||||
['s2'],
|
||||
0.6,
|
||||
scale
|
||||
);
|
||||
|
||||
// Fuse testimonies
|
||||
const combinedEvidence = QualitativeFusion.normalizedConjunctive([
|
||||
testimony1,
|
||||
testimony2
|
||||
]);
|
||||
```
|
||||
|
||||
### 2. Qualitative Decision Making
|
||||
|
||||
Use Sugeno integrals to evaluate decisions based on qualitative utility functions and uncertainty represented by q-capacities.
|
||||
|
||||
```javascript
|
||||
// Define decision function (utility for each state)
|
||||
const utility = {
|
||||
's1': 0.8, // High utility
|
||||
's2': 0.4, // Medium utility
|
||||
's3': 0.2 // Low utility
|
||||
};
|
||||
|
||||
// Evaluate decision using Sugeno integral
|
||||
const decisionValue = QualitativeFusion.sugenoIntegral(capacity, utility);
|
||||
```
|
||||
|
||||
### 3. Imprecise Possibility
|
||||
|
||||
Represent ill-known possibility measures bounded by lower (q-capacity) and upper (possibility) measures.
|
||||
|
||||
```javascript
|
||||
// Get upper capacity (possibility measure)
|
||||
const upperCapacity = capacity.getUpperCapacity();
|
||||
|
||||
// Get contour function
|
||||
const contour = capacity.getContourFunction();
|
||||
|
||||
// Get conjugate capacity
|
||||
const conjugate = capacity.getConjugate();
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
The current implementation has O(2^|W|) complexity for operations that generate all subsets. This is suitable for small state spaces (|W| < 20) but may not scale to larger ones.
|
||||
|
||||
### Optimizations Implemented
|
||||
|
||||
1. **QualitativeScale Optimizations**:
|
||||
- `contains()`: O(1) average time using Set-based lookup
|
||||
- `indexOf()`: O(log n) time using binary search
|
||||
- These optimizations significantly improve performance for scale operations
|
||||
|
||||
2. **Canonical QMT Optimization**:
|
||||
- `_convertToCanonicalQMT()`: Only checks immediate proper subsets instead of all smaller subsets
|
||||
- Uses the mathematical property: γ#(A) > 0 ⟺ γ(A) > max_{w∈A} γ(A∖{w})
|
||||
- Provides substantial performance improvement for canonicalization
|
||||
|
||||
3. **String Key Robustness**:
|
||||
- All Set objects are converted to canonical string keys for Map operations
|
||||
- Eliminates JavaScript Set reference comparison issues
|
||||
- Ensures consistent and efficient Map key operations
|
||||
|
||||
4. **Canonicalization Consistency**:
|
||||
- All fusion methods return canonical QMTs by default
|
||||
- Ensures minimal representation and consistent behavior
|
||||
- Simplifies subsequent operations and saves memory
|
||||
|
||||
For large state spaces, consider:
|
||||
1. Working with QMTs directly (already implemented)
|
||||
2. Using sparse representations
|
||||
3. Implementing approximation algorithms
|
||||
|
||||
## Future Research Directions
|
||||
|
||||
1. **QMT-based OWA**: Develop more sophisticated OWA-like operators that work directly on QMTs
|
||||
2. **Complexity Optimization**: Implement efficient algorithms for large state spaces
|
||||
3. **Approximation Methods**: Develop approximation algorithms for intractable operations
|
||||
4. **Integration with DSL**: Extend the Evidence DSL to support qualitative capacities
|
||||
|
||||
## References
|
||||
|
||||
This implementation is based on the research paper "Qualitative capacities: basic notions and potential applications" and related work on qualitative uncertainty theory, possibility theory, and evidential reasoning.
|
||||
@@ -0,0 +1,399 @@
|
||||
# Authorization Rules API Specification
|
||||
|
||||
This document defines the standardized API that all authorization rules must implement to ensure consistency, performance, and maintainability across the zanzibar-graph system.
|
||||
|
||||
## Overview
|
||||
|
||||
All authorization rules must extend the `BaseRule` class and implement the required abstract methods. This ensures:
|
||||
|
||||
- **Consistent Interface**: All rules have the same public API
|
||||
- **Performance Tracking**: Built-in performance monitoring
|
||||
- **Error Handling**: Standardized error responses
|
||||
- **Batch Processing**: Optional but encouraged for performance
|
||||
- **Early Exit**: Threshold-based optimization support
|
||||
- **Input Validation**: Automatic parameter validation
|
||||
|
||||
## Core Interface
|
||||
|
||||
### Constructor
|
||||
|
||||
```javascript
|
||||
constructor(arbiter)
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `arbiter` (Arbiter): The main Arbiter instance
|
||||
|
||||
**Requirements:**
|
||||
- Must call `super(arbiter)` first
|
||||
- Should initialize any rule-specific state
|
||||
- Must not throw exceptions
|
||||
|
||||
### Primary Evaluation Method
|
||||
|
||||
```javascript
|
||||
evaluate(userId, userKey, objectId, objectKey, rule, visited, currentRelation, options = {})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `userId` (number): Internal user node ID
|
||||
- `userKey` (string): User key (e.g., "user:alice")
|
||||
- `objectId` (number): Internal object node ID
|
||||
- `objectKey` (string): Object key (e.g., "doc:secret")
|
||||
- `rule` (Object): Rule configuration object
|
||||
- `visited` (Set): Set of visited nodes for cycle detection
|
||||
- `currentRelation` (string): Current relation being evaluated
|
||||
- `options` (Object): Evaluation options
|
||||
|
||||
**Returns:** `RuleEvaluationResult` object
|
||||
|
||||
**Standard Options:**
|
||||
```javascript
|
||||
{
|
||||
// Performance options
|
||||
fastPath: false, // Enable early termination
|
||||
minAllowPossibility: null, // Threshold for allow early exit (0-1)
|
||||
maxDenyPossibility: null, // Threshold for deny early exit (0-1)
|
||||
|
||||
// Inference options
|
||||
noInfer: false, // Disable inference
|
||||
allowInference: true, // Allow inference (opposite of noInfer)
|
||||
|
||||
// Binary mode
|
||||
binary: false, // Return binary allow/deny decisions
|
||||
|
||||
// Evaluation tracking
|
||||
trackEvaluation: true, // Include evaluation metadata
|
||||
|
||||
// Rule-specific options (passed through)
|
||||
// ... additional options specific to rule type
|
||||
}
|
||||
```
|
||||
|
||||
### Batch Evaluation Method (Optional)
|
||||
|
||||
```javascript
|
||||
batchEvaluate(queries, batchOptions = {})
|
||||
```
|
||||
|
||||
**Parameters:**
|
||||
- `queries` (Array): Array of query objects with same structure as `evaluate` parameters
|
||||
- `batchOptions` (Object): Batch-specific options
|
||||
|
||||
**Returns:** Array of `RuleEvaluationResult` objects
|
||||
|
||||
**Query Object Structure:**
|
||||
```javascript
|
||||
{
|
||||
userId: number,
|
||||
userKey: string,
|
||||
objectId: number,
|
||||
objectKey: string,
|
||||
rule: Object,
|
||||
visited: Set,
|
||||
currentRelation: string,
|
||||
options: Object
|
||||
}
|
||||
```
|
||||
|
||||
### Batch Support Check
|
||||
|
||||
```javascript
|
||||
canBatchProcess()
|
||||
```
|
||||
|
||||
**Returns:** `boolean` - Whether this rule supports efficient batch processing
|
||||
|
||||
## Standard Result Structure
|
||||
|
||||
All evaluation methods must return a `RuleEvaluationResult` object:
|
||||
|
||||
```javascript
|
||||
{
|
||||
// Core evaluation results (required)
|
||||
possibility_allow: number, // 0-1, possibility of allowing access
|
||||
possibility_deny: number, // 0-1, possibility of denying access
|
||||
reliability: number, // 0-1, reliability of the evaluation
|
||||
|
||||
// Metadata (required, can be null)
|
||||
meta_allow: Object | null, // Metadata for allow decision
|
||||
meta_deny: Object | null, // Metadata for deny decision
|
||||
|
||||
// Optional fields
|
||||
reason: string, // Reason for the result
|
||||
error: boolean, // Whether this is an error result
|
||||
details: Object, // Additional details
|
||||
evaluation: Object // Evaluation tracking data
|
||||
}
|
||||
```
|
||||
|
||||
### Standard Metadata Structure
|
||||
|
||||
```javascript
|
||||
// meta_allow / meta_deny structure
|
||||
{
|
||||
ruleType: string, // Type of rule (e.g., 'direct', 'tuple_to_userset')
|
||||
reason: string, // Reason for decision
|
||||
rule: Object, // Original rule configuration
|
||||
|
||||
// Performance metadata
|
||||
earlyExit: boolean, // Whether early exit was used
|
||||
earlyExitReason: string, // Reason for early exit
|
||||
|
||||
// Rule-specific metadata
|
||||
// ... additional fields specific to rule type
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Requirements
|
||||
|
||||
### Abstract Methods (Must Implement)
|
||||
|
||||
```javascript
|
||||
// Core evaluation logic
|
||||
_evaluateRule(userId, userKey, objectId, objectKey, rule, visited, currentRelation, options)
|
||||
|
||||
// Batch evaluation logic (optional, only if canBatchProcess() returns true)
|
||||
_batchEvaluateRule(queries, batchOptions)
|
||||
```
|
||||
|
||||
### Performance Tracking
|
||||
|
||||
All rules automatically track:
|
||||
- Individual evaluation count and timing
|
||||
- Batch evaluation count and timing
|
||||
- Average evaluation times
|
||||
|
||||
Access via:
|
||||
```javascript
|
||||
rule.getPerformanceStats()
|
||||
rule.resetPerformanceStats()
|
||||
```
|
||||
|
||||
### Error Handling
|
||||
|
||||
Rules should handle errors gracefully:
|
||||
- Input validation is automatic
|
||||
- Exceptions are caught and converted to error results
|
||||
- Batch operations fall back to individual evaluations on error
|
||||
|
||||
## Rule-Specific Configurations
|
||||
|
||||
### Common Rule Properties
|
||||
|
||||
```javascript
|
||||
{
|
||||
type: string, // Rule type identifier
|
||||
ruleType: string, // 'defeasible', 'defeater', 'strict'
|
||||
priority: number, // Rule priority (higher = more important)
|
||||
weight: number, // Rule weight for aggregation
|
||||
|
||||
// Performance options
|
||||
no_infer: boolean, // Disable inference for this rule
|
||||
allowInference: boolean, // Allow inference
|
||||
|
||||
// Aggregation options
|
||||
owaWeights: Array<number>, // OWA aggregation weights
|
||||
aggregator: string, // Aggregation method ('max', 'min', 'avg', etc.)
|
||||
|
||||
// Rule-specific properties
|
||||
// ... varies by rule type
|
||||
}
|
||||
```
|
||||
|
||||
### DirectRule Configuration
|
||||
|
||||
```javascript
|
||||
{
|
||||
type: 'direct',
|
||||
relation: string, // Relation to check (optional, uses currentRelation)
|
||||
reverse: boolean // Check in reverse direction
|
||||
}
|
||||
```
|
||||
|
||||
### TupleToUsersetRule Configuration
|
||||
|
||||
```javascript
|
||||
{
|
||||
type: 'tuple_to_userset',
|
||||
tuplesetRelation: string, // Relation from object to intermediate
|
||||
computedRelation: string, // Relation from user to intermediate
|
||||
reverse: boolean // Check in reverse direction
|
||||
}
|
||||
```
|
||||
|
||||
### ParentRule Configuration
|
||||
|
||||
```javascript
|
||||
{
|
||||
type: 'parent',
|
||||
parentRelation: string, // Relation defining parent-child
|
||||
relation: string, // Relation to check on parent
|
||||
reverse: boolean // Check in reverse direction
|
||||
}
|
||||
```
|
||||
|
||||
### SimilarityRule Configuration
|
||||
|
||||
```javascript
|
||||
{
|
||||
type: 'similar_to',
|
||||
relation: string, // Relation to find similarities for
|
||||
k: number, // Number of similar entities to consider
|
||||
similarityThreshold: number, // Minimum similarity threshold (0-1)
|
||||
reverse: boolean, // Check in reverse direction
|
||||
fallbackToBasicSimilarity: boolean // Use basic similarity when embeddings unavailable
|
||||
}
|
||||
```
|
||||
|
||||
### MultiHopRule Configuration
|
||||
|
||||
```javascript
|
||||
{
|
||||
type: 'multi_hop',
|
||||
relation: string, // Relation to traverse
|
||||
maxDepth: number, // Maximum path depth
|
||||
minReliability: number, // Minimum path reliability
|
||||
decayFactor: number, // Reliability decay per hop
|
||||
pathAggregation: string, // How to aggregate multiple paths ('max', 'sum', 'owa')
|
||||
reverse: boolean, // Search in reverse direction
|
||||
allowInference: boolean, // Allow inference for missing edges
|
||||
fallbackToBasicPaths: boolean // Use basic path finding as fallback
|
||||
}
|
||||
```
|
||||
|
||||
### ComputedRule Configuration
|
||||
|
||||
```javascript
|
||||
{
|
||||
type: 'computed',
|
||||
relation: string // Relation to recursively evaluate
|
||||
}
|
||||
```
|
||||
|
||||
## Logical Operators
|
||||
|
||||
### Union (OR) Configuration
|
||||
|
||||
```javascript
|
||||
{
|
||||
union: Array<RuleConfig>, // Array of rules to OR together
|
||||
aggregator: string, // Aggregation method
|
||||
owaWeights: Array<number>, // OWA weights for fusion
|
||||
reliabilityWeighting: boolean // Weight by reliability
|
||||
}
|
||||
```
|
||||
|
||||
### Intersection (AND) Configuration
|
||||
|
||||
```javascript
|
||||
{
|
||||
intersection: Array<RuleConfig>, // Array of rules to AND together
|
||||
aggregator: string, // Aggregation method (defaults to 'min')
|
||||
owaWeights: Array<number> // OWA weights for fusion
|
||||
}
|
||||
```
|
||||
|
||||
### Exclusion (A AND NOT B) Configuration
|
||||
|
||||
```javascript
|
||||
{
|
||||
exclusion: [RuleConfig, RuleConfig] // [base rule, exclusion rule]
|
||||
}
|
||||
```
|
||||
|
||||
## Performance Optimization Guidelines
|
||||
|
||||
### Early Exit Support
|
||||
|
||||
Rules should support early termination when:
|
||||
- `options.fastPath` is true
|
||||
- `options.minAllowPossibility` threshold is met
|
||||
- `options.maxDenyPossibility` threshold is met
|
||||
|
||||
### Batch Processing
|
||||
|
||||
Rules that can benefit from batch processing should:
|
||||
- Override `canBatchProcess()` to return `true`
|
||||
- Implement `_batchEvaluateRule()` method
|
||||
- Group similar operations for efficiency
|
||||
- Use indexed lookups instead of linear scans
|
||||
|
||||
### Caching
|
||||
|
||||
Rules should leverage:
|
||||
- Arbiter's built-in caching mechanisms
|
||||
- Batch caches for intermediate results
|
||||
- Embedding caches for similarity calculations
|
||||
|
||||
## Testing Requirements
|
||||
|
||||
All rules must include:
|
||||
- Unit tests for individual evaluation
|
||||
- Batch evaluation tests (if supported)
|
||||
- Performance benchmarks
|
||||
- Error handling tests
|
||||
- Edge case coverage
|
||||
|
||||
## Migration Guide
|
||||
|
||||
To migrate existing rules to the new API:
|
||||
|
||||
1. **Extend BaseRule**: Change `export class MyRule` to `export class MyRule extends BaseRule`
|
||||
2. **Rename evaluate**: Rename `evaluate()` to `_evaluateRule()`
|
||||
3. **Update constructor**: Call `super(arbiter)` first
|
||||
4. **Handle options**: Use normalized options parameter
|
||||
5. **Update tests**: Test against new interface
|
||||
|
||||
## Example Implementation
|
||||
|
||||
```javascript
|
||||
import { BaseRule } from './BaseRule.js';
|
||||
|
||||
export class ExampleRule extends BaseRule {
|
||||
constructor(arbiter) {
|
||||
super(arbiter);
|
||||
// Rule-specific initialization
|
||||
}
|
||||
|
||||
canBatchProcess() {
|
||||
return true; // This rule supports batching
|
||||
}
|
||||
|
||||
_evaluateRule(userId, userKey, objectId, objectKey, rule, visited, currentRelation, options) {
|
||||
// Implement rule-specific logic
|
||||
const result = {
|
||||
possibility_allow: 0.8,
|
||||
possibility_deny: 0.1,
|
||||
reliability: 0.9,
|
||||
meta_allow: {
|
||||
ruleType: 'example',
|
||||
reason: 'example_evaluation',
|
||||
rule
|
||||
},
|
||||
meta_deny: null,
|
||||
reason: 'example_result'
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
_batchEvaluateRule(queries, batchOptions) {
|
||||
// Implement efficient batch processing
|
||||
return queries.map(query =>
|
||||
this._evaluateRule(
|
||||
query.userId,
|
||||
query.userKey,
|
||||
query.objectId,
|
||||
query.objectKey,
|
||||
query.rule,
|
||||
query.visited,
|
||||
query.currentRelation,
|
||||
query.options
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
This standardized API ensures all rules work consistently while allowing for rule-specific optimizations and features.
|
||||
Reference in New Issue
Block a user