Skip to content

Commit

Permalink
Merge pull request #11 from phr3nzy/release/v3.2.0
Browse files Browse the repository at this point in the history
chore(release): prepare v3.2.0
  • Loading branch information
phr3nzy authored Feb 16, 2025
2 parents 7e45c9c + dfbc5ba commit ed5c18c
Show file tree
Hide file tree
Showing 18 changed files with 1,583 additions and 38 deletions.
66 changes: 66 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,72 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [3.2.0] - 2025-02-16

### Added

- New interface-agnostic component system that can be used across different interfaces (web, terminal, etc.)
- Data analysis system that automatically suggests appropriate components based on data characteristics
- Generic adapter system for converting components to different interface implementations
- Smart component suggestion based on data type and statistics
- Comprehensive statistics generation for numeric and categorical data
- Support for range, choice, multi-select, and input components
- Type-safe component handling with proper TypeScript support
- Backward compatibility with existing UI configuration through converters

### Changed

- Refactored UI subsystem to be interface-agnostic
- Improved type safety across the entire codebase
- Enhanced rule conversion with support for more comparison operators
- Better handling of numeric ranges and categorical data
- More flexible component constraints and validation

### Deprecated

- UI-specific components (UIConditionType, UIComponentType, etc.)
- UI configuration converter
- UI example implementation

### Migration Note

The old UI-specific components are now deprecated in favor of the new interface-agnostic system. While the old components will continue to work in this version, they will be removed in a future major release. We recommend migrating to the new system which provides better type safety and more flexibility.

Example migration:

```typescript
// Before
import { UIComponentType, UIConditionType } from '@phr3nzy/rulekit';

const component = {
type: UIComponentType.RANGE,
field: 'price',
value: [0, 1000],
};

// After
import { ComponentType } from '@phr3nzy/rulekit';

const component = {
type: ComponentType.RANGE,
identifier: 'price',
value: 500,
constraints: {
min: 0,
max: 1000,
step: 1,
},
};
```

The new system provides:

- Better type safety with proper TypeScript support
- More flexible component constraints
- Automatic component suggestions based on data analysis
- Support for different interface implementations
- Enhanced validation and error handling

## [3.1.0] - 2025-02-16

### Changed
Expand Down
2 changes: 1 addition & 1 deletion eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const typescriptPluginConfig = {
},
};

/** @type {Array<import('eslint').Linter.FlatConfig>} */
/** @type {Array<import('eslint').Linter.Config>} */
export default [
// Base JS config with globals
{
Expand Down
32 changes: 32 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# RuleKit Examples

This directory contains examples demonstrating how to use RuleKit in different scenarios.

## Interface-Agnostic Components

The `interface-agnostic` directory shows how to use RuleKit's new interface-agnostic component system, which provides:

- Automatic component suggestion based on data analysis
- Smart statistics generation for fields
- Type-safe component handling
- Flexible component customization

### Running the Example

```bash
# Run with ts-node
npx ts-node examples/interface-agnostic/smart-filtering.ts

# Or compile and run
tsc examples/interface-agnostic/smart-filtering.ts
node examples/interface-agnostic/smart-filtering.js
```

The example demonstrates:

1. Analyzing data to get smart component suggestions
2. Viewing data statistics and insights
3. Creating a filtering interface with suggested components
4. Converting components to rules
5. Finding matching products
6. Customizing component suggestions with rules
171 changes: 171 additions & 0 deletions examples/interface-agnostic/smart-filtering.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
import { RuleEngine } from '../../src';
import { DataAnalyzer } from '../../src/core/analysis/analyzer';
import { RuleConverter } from '../../src/core/interface/converters/rule-converter';
import { ComponentType } from '../../src/core/interface/components/types';
import type { Entity } from '../../src/core/models/types';
import type { Component } from '../../src/core/interface/components/types';

// Use Record<string, unknown> to make it compatible with DynamicAttributes
type ProductAttributes = {
category: string;
price: number;
tags: string[];
inStock: boolean;
rating: number;
releaseDate: string;
__validated: boolean;
};

// Sample product data
const products: Entity[] = [
{
id: '1',
name: 'Laptop',
attributes: {
category: 'electronics',
price: 999,
tags: ['new', 'featured'],
inStock: true,
rating: 4.5,
releaseDate: '2024-01-15',
__validated: true,
},
},
{
id: '2',
name: 'Office Chair',
attributes: {
category: 'furniture',
price: 299,
tags: ['sale'],
inStock: true,
rating: 4.0,
releaseDate: '2023-12-01',
__validated: true,
},
},
{
id: '3',
name: 'Smartphone',
attributes: {
category: 'electronics',
price: 699,
tags: ['new', 'premium'],
inStock: false,
rating: 4.8,
releaseDate: '2024-02-01',
__validated: true,
},
},
];

// Step 1: Analyze data to get smart component suggestions
const analyzer = new DataAnalyzer();
const analysis = analyzer.analyze(products.map(p => p.attributes));

console.log('\n=== Data Analysis Results ===');
Object.entries(analysis).forEach(([field, fieldAnalysis]) => {
console.log(`\nField: ${field}`);
console.log('Data Type:', fieldAnalysis.dataType);
console.log('Suggested Component:', fieldAnalysis.suggestedComponent.type);

if (fieldAnalysis.statistics.numeric) {
console.log('Numeric Statistics:', {
min: fieldAnalysis.statistics.numeric.min,
max: fieldAnalysis.statistics.numeric.max,
average: fieldAnalysis.statistics.numeric.average,
median: fieldAnalysis.statistics.numeric.median,
});
}

if (fieldAnalysis.statistics.categorical) {
console.log(
'Categories:',
fieldAnalysis.statistics.categorical.categories.map(c => ({
value: c.value,
count: c.count,
percentage: c.percentage,
})),
);
}
});

// Step 2: Use the suggested components to create a filtering interface
const allComponents = [
{
field: 'price',
component: analysis['price']?.suggestedComponent, // Range component
},
{
field: 'category',
component: analysis['category']?.suggestedComponent, // Choice component
},
{
field: 'tags',
component: analysis['tags']?.suggestedComponent, // Multi component
},
{
field: 'inStock',
component: analysis['inStock']?.suggestedComponent, // Choice component
},
];

// Filter out any undefined components and ensure type safety
const components: Array<{ field: string; component: Component }> = allComponents.filter(
(c): c is { field: string; component: Component } => c.component !== undefined,
);

// Step 3: Convert components to rules
const converter = new RuleConverter();
const rule = converter.convertComponentsToRule(components);

// Step 4: Find matching products
const engine = new RuleEngine();
const matches = engine.findMatchingFrom(products, [rule]);

console.log('\n=== Matching Products ===');
matches.forEach(match => {
const attrs = match.attributes as ProductAttributes;
console.log(`- ${match.name} (${attrs.category})`);
console.log(' Price:', attrs.price);
console.log(' Tags:', attrs.tags.join(', '));
console.log(' In Stock:', attrs.inStock);
console.log();
});

// Example of customizing component suggestions
const analyzerWithCustomRules = new DataAnalyzer({
maxChoiceOptions: 5, // Suggest input component if more than 5 options
componentSuggestionRules: [
{
// Always use input component for price
condition: analysis => analysis.fieldName === 'price',
suggest: analysis => ({
type: ComponentType.INPUT,
identifier: analysis.fieldName,
value: '',
format: 'number',
}),
},
{
// Use multi component for any array data
condition: analysis => Array.isArray(analysis.statistics.categorical?.categories[0]?.value),
suggest: analysis => ({
type: ComponentType.MULTI,
identifier: analysis.fieldName,
value: [],
options: analysis.statistics.categorical!.categories.map(c => ({
identifier: c.value,
value: c.value,
})),
}),
},
],
});

const customAnalysis = analyzerWithCustomRules.analyze(products.map(p => p.attributes));
console.log('\n=== Custom Analysis Results ===');
Object.entries(customAnalysis).forEach(([field, fieldAnalysis]) => {
console.log(`\nField: ${field}`);
console.log('Suggested Component:', fieldAnalysis.suggestedComponent.type);
});
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@phr3nzy/rulekit",
"version": "3.1.0",
"version": "3.2.0",
"description": "A powerful and flexible toolkit for building rule-based matching and filtering systems",
"main": "dist/index.js",
"module": "dist/index.mjs",
Expand Down
Loading

0 comments on commit ed5c18c

Please sign in to comment.