@praxisui/visual-builder
Version:
Visual rule and expression builder for Praxis UI with mini-DSL support, validation and context variables.
1,574 lines (1,557 loc) • 121 kB
TypeScript
import * as i0 from '@angular/core';
import { EventEmitter, OnInit, OnDestroy, OnChanges, SimpleChanges, ElementRef, ChangeDetectorRef } from '@angular/core';
import { ContextProvider, FunctionRegistry, ExportOptions as ExportOptions$1, ValidationIssue as ValidationIssue$1, ContextualSpecification } from '@praxisui/specification';
import { SpecificationMetadata, Specification } from '@praxisui/specification-core';
export { SpecificationMetadata } from '@praxisui/specification-core';
import { Observable } from 'rxjs';
import { FormBuilder, FormGroup, FormArray } from '@angular/forms';
import { MatSnackBar } from '@angular/material/snack-bar';
import { MatButtonToggleChange } from '@angular/material/button-toggle';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { MatSelectChange } from '@angular/material/select';
import { MatSlideToggleChange } from '@angular/material/slide-toggle';
import { MatChipInputEvent } from '@angular/material/chips';
/**
* Models for the Visual Rule Builder
*/
/**
* Value types for rule configuration
*/
type ValueType = 'literal' | 'field' | 'context' | 'function';
/**
* Valid comparison operators (aligned with @praxisui/specification)
*/
type ValidComparisonOperator = 'eq' | 'neq' | 'lt' | 'lte' | 'gt' | 'gte' | 'contains' | 'startsWith' | 'endsWith' | 'in';
interface RuleNode {
/** Unique identifier for this rule node */
id: string;
/** Type of rule node */
type: RuleNodeType | RuleNodeTypeString;
/** Human-readable label for this rule */
label?: string;
/** Rule metadata */
metadata?: SpecificationMetadata;
/** Whether this node is currently selected */
selected?: boolean;
/** Whether this node is expanded (for groups) */
expanded?: boolean;
/** Parent node ID */
parentId?: string;
/** Child node IDs (for groups) */
children?: string[];
/** Rule-specific configuration */
config?: RuleNodeConfig;
}
declare enum RuleNodeType {
FIELD_CONDITION = "fieldCondition",
AND_GROUP = "andGroup",
OR_GROUP = "orGroup",
NOT_GROUP = "notGroup",
XOR_GROUP = "xorGroup",
IMPLIES_GROUP = "impliesGroup",
REQUIRED_IF = "requiredIf",
VISIBLE_IF = "visibleIf",
DISABLED_IF = "disabledIf",
READONLY_IF = "readonlyIf",
FOR_EACH = "forEach",
UNIQUE_BY = "uniqueBy",
MIN_LENGTH = "minLength",
MAX_LENGTH = "maxLength",
IF_DEFINED = "ifDefined",
IF_NOT_NULL = "ifNotNull",
IF_EXISTS = "ifExists",
WITH_DEFAULT = "withDefault",
FUNCTION_CALL = "functionCall",
FIELD_TO_FIELD = "fieldToField",
CONTEXTUAL = "contextual",
AT_LEAST = "atLeast",
EXACTLY = "exactly",
EXPRESSION = "expression",
CONTEXTUAL_TEMPLATE = "contextualTemplate",
CUSTOM = "custom"
}
/**
* String literal type for rule node types (for flexibility)
*/
type RuleNodeTypeString = 'fieldCondition' | 'andGroup' | 'orGroup' | 'notGroup' | 'xorGroup' | 'impliesGroup' | 'requiredIf' | 'visibleIf' | 'disabledIf' | 'readonlyIf' | 'forEach' | 'uniqueBy' | 'minLength' | 'maxLength' | 'ifDefined' | 'ifNotNull' | 'ifExists' | 'withDefault' | 'functionCall' | 'fieldToField' | 'contextual' | 'atLeast' | 'exactly' | 'expression' | 'contextualTemplate' | 'custom';
type RuleNodeConfig = FieldConditionConfig | BooleanGroupConfig | ConditionalValidatorConfig | CollectionValidationConfig | CollectionValidatorConfig | OptionalFieldConfig | FunctionCallConfig | FieldToFieldConfig | ContextualConfig$1 | CardinalityConfig | ExpressionConfig | ContextualTemplateConfig | CustomConfig;
interface FieldConditionConfig {
type: 'fieldCondition';
/** Primary field name */
fieldName: string;
/** Comparison operator aligned with @praxisui/specification */
operator: ValidComparisonOperator | string;
/** Comparison value */
value: unknown;
/** Type of value for proper handling */
valueType?: ValueType;
/** Field to compare against (for field-to-field comparisons) */
compareToField?: string;
/** Context variable to use as value */
contextVariable?: string;
/** Optional metadata for error messages and UI hints */
metadata?: SpecificationMetadata;
/** Legacy field alias for backward compatibility */
field?: string;
}
interface BooleanGroupConfig {
type: 'booleanGroup' | 'andGroup' | 'orGroup' | 'notGroup' | 'xorGroup' | 'impliesGroup';
/** Boolean operator type */
operator: 'and' | 'or' | 'not' | 'xor' | 'implies';
/** Minimum required true conditions (for atLeast scenarios) */
minimumRequired?: number;
/** Exact required true conditions (for exactly scenarios) */
exactRequired?: number;
/** Optional metadata for group validation */
metadata?: SpecificationMetadata;
}
interface ConditionalValidatorConfig {
type: 'requiredIf' | 'visibleIf' | 'disabledIf' | 'readonlyIf';
/** Specific validator type (mirrors type for backward compatibility) */
validatorType: 'requiredIf' | 'visibleIf' | 'disabledIf' | 'readonlyIf';
/** Target field to apply conditional logic */
targetField: string;
/** Optional single condition (legacy support) */
condition?: RuleNode;
/** Multiple conditions for advanced mode */
conditions?: RuleNode[];
/** Reference to condition rule node ID (for backward compatibility) */
conditionNodeId?: string;
/** Whether to invert the condition result */
inverse?: boolean;
/** Logic operator to combine multiple conditions */
logicOperator?: 'and' | 'or';
/** Custom error message */
errorMessage?: string;
/** Validate on value change */
validateOnChange?: boolean;
/** Validate on blur */
validateOnBlur?: boolean;
/** Show error immediately */
showErrorImmediately?: boolean;
/** Animation type */
animation?: string;
/** Hide field label */
hideLabel?: boolean;
/** Preserve space when hidden */
preserveSpace?: boolean;
/** Style when disabled */
disabledStyle?: string;
/** Clear value when disabled */
clearOnDisable?: boolean;
/** Show disabled message */
showDisabledMessage?: boolean;
/** Custom disabled message */
disabledMessage?: string;
/** Readonly style */
readonlyStyle?: string;
/** Show readonly indicator */
showReadonlyIndicator?: boolean;
/** Metadata aligned with @praxisui/specification */
metadata?: SpecificationMetadata;
}
interface CollectionValidationConfig {
type: 'collectionValidation';
/** Type of collection validation */
validationType: 'forEach' | 'uniqueBy' | 'minLength' | 'maxLength';
/** Array field to validate */
arrayField: string;
/** Reference to rule node ID for forEach validation */
itemCondition?: string;
/** Property name for uniqueBy validation */
uniqueKey?: string;
/** Length value for min/max length validation */
lengthValue?: number;
/** Optional metadata for validation */
metadata?: SpecificationMetadata;
}
/**
* Enhanced collection validator configuration (Phase 2 Implementation)
* Aligned with @praxisui/specification collection validation patterns
*/
interface CollectionValidatorConfig {
type: 'forEach' | 'uniqueBy' | 'minLength' | 'maxLength';
/** Target collection field name */
targetCollection: string;
/** Variable name for current item in forEach */
itemVariable?: string;
/** Variable name for current index in forEach */
indexVariable?: string;
/** Validation rules applied to each item */
itemValidationRules?: {
ruleType: string;
fieldPath: string;
errorMessage?: string;
}[];
/** Fields to check uniqueness by */
uniqueByFields?: string[];
/** Case-sensitive uniqueness check */
caseSensitive?: boolean;
/** Ignore empty values in uniqueness check */
ignoreEmpty?: boolean;
/** Custom error message for duplicates */
duplicateErrorMessage?: string;
/** Minimum number of items */
minItems?: number;
/** Maximum number of items */
maxItems?: number;
/** Custom error message for length validation */
lengthErrorMessage?: string;
/** Show current item count in UI */
showItemCount?: boolean;
/** Prevent adding items beyond maxItems */
preventExcess?: boolean;
/** Validate when items are added */
validateOnAdd?: boolean;
/** Validate when items are removed */
validateOnRemove?: boolean;
/** Validate when items are changed */
validateOnChange?: boolean;
/** Validate on form submit */
validateOnSubmit?: boolean;
/** Error display strategy */
errorStrategy?: 'summary' | 'inline' | 'both';
/** Stop validation on first error */
stopOnFirstError?: boolean;
/** Highlight items with errors */
highlightErrorItems?: boolean;
/** Batch size for large collections */
batchSize?: number;
/** Debounce validation for performance */
debounceValidation?: boolean;
/** Debounce delay in milliseconds */
debounceDelay?: number;
/** Optional metadata for validation messages */
metadata?: SpecificationMetadata;
}
interface FunctionCallConfig {
type: 'functionCall';
/** Name of the function to call */
functionName: string;
/** Function parameters with type information */
parameters: FunctionParameter[];
/** Optional metadata for validation */
metadata?: SpecificationMetadata;
}
interface FunctionParameter {
/** Parameter name */
name: string;
/** Parameter value */
value: unknown;
/** Type of parameter value */
valueType: ValueType;
/** Field name if valueType is 'field' */
fieldName?: string;
/** Context variable name if valueType is 'context' */
contextVariable?: string;
}
interface FieldToFieldConfig {
type: 'fieldToField';
/** Left side field name */
leftField: string;
/** Comparison operator */
operator: ValidComparisonOperator | string;
/** Right side field name */
rightField: string;
/** Transform functions applied to left field */
leftTransforms?: string[];
/** Transform functions applied to right field */
rightTransforms?: string[];
/** Optional metadata for validation */
metadata?: SpecificationMetadata;
}
interface ContextualConfig$1 {
type: 'contextual';
/** Template string with context placeholders */
template: string;
/** Available context variables */
contextVariables: Record<string, unknown>;
/** Optional context provider for dynamic values */
contextProvider?: ContextProvider;
/** Strict validation of context tokens */
strictContextValidation?: boolean;
/** Optional metadata */
metadata?: SpecificationMetadata;
}
interface CardinalityConfig {
type: 'cardinality';
/** Type of cardinality check */
cardinalityType: 'atLeast' | 'exactly';
/** Required count of true conditions */
count: number;
/** References to rule node IDs to evaluate */
conditions: string[];
/** Optional metadata for validation */
metadata?: SpecificationMetadata;
}
interface CustomConfig {
type: 'custom';
/** Custom configuration type identifier */
customType: string;
/** Custom properties specific to the type */
properties: Record<string, unknown>;
/** Optional metadata for validation */
metadata?: SpecificationMetadata;
}
/**
* Validator types for conditional validation
*/
declare enum ConditionalValidatorType {
REQUIRED_IF = "requiredIf",
VISIBLE_IF = "visibleIf",
DISABLED_IF = "disabledIf",
READONLY_IF = "readonlyIf"
}
/**
* Preview data for conditional validator simulation
*/
interface ConditionalValidatorPreview {
targetField: string;
currentValue: unknown;
conditionResult: boolean;
validatorType: ConditionalValidatorType;
resultingState: {
isRequired?: boolean;
isVisible?: boolean;
isDisabled?: boolean;
isReadonly?: boolean;
};
example: string;
}
/**
* Rule building session state
*/
interface RuleBuilderState {
/** All rule nodes in the current session */
nodes: Record<string, RuleNode>;
/** Root node IDs (top-level rules) */
rootNodes: string[];
/** Currently selected node ID */
selectedNodeId?: string;
/** Current DSL representation */
currentDSL?: string;
/** Current JSON representation */
currentJSON?: unknown;
/** Validation errors */
validationErrors: ValidationError$1[];
/** Build mode */
mode: 'visual' | 'dsl' | 'json';
/** Whether the rule is dirty (has unsaved changes) */
isDirty: boolean;
/** Undo/redo history */
history: RuleBuilderSnapshot[];
/** Current history position */
historyPosition: number;
}
interface ValidationError$1 {
/** Error ID */
id: string;
/** Error message */
message: string;
/** Error severity */
severity: 'error' | 'warning' | 'info';
/** Associated node ID */
nodeId?: string;
/** Error code for programmatic handling */
code?: string;
/** Suggested fix */
suggestion?: string;
}
interface RuleBuilderSnapshot {
/** Timestamp of this snapshot */
timestamp: number;
/** Description of the change */
description: string;
/** Complete state at this point */
state: {
nodes: Record<string, RuleNode>;
rootNodes: string[];
selectedNodeId?: string;
};
}
/**
* Rule template for common scenarios
*/
interface RuleTemplate {
/** Template ID */
id: string;
/** Template name */
name: string;
/** Template description */
description: string;
/** Template category */
category: string;
/** Template tags for search */
tags: string[];
/** Rule nodes that make up this template */
nodes: RuleNode[];
/** Root node IDs */
rootNodes: string[];
/** Required field schemas for this template */
requiredFields?: string[];
/** Example usage */
example?: string;
/** Template preview image/icon */
icon?: string;
/** Template metadata */
metadata?: TemplateMetadata;
}
/**
* Template metadata for tracking and management
*/
interface TemplateMetadata {
/** Creation date */
createdAt?: Date;
/** Last update date */
updatedAt?: Date;
/** Last used date */
lastUsed?: Date;
/** Import date (if imported) */
importedAt?: Date;
/** Template version */
version?: string;
/** Usage count */
usageCount?: number;
/** Template complexity */
complexity?: 'simple' | 'medium' | 'complex';
/** Original template ID (for imports/copies) */
originalId?: string;
/** Author information */
author?: {
name?: string;
email?: string;
organization?: string;
};
/** Template size metrics */
metrics?: {
nodeCount?: number;
maxDepth?: number;
fieldCount?: number;
};
}
/**
* Export options for rules
*/
interface ExportOptions {
/** Export format */
format: 'json' | 'dsl' | 'typescript' | 'form-config';
/** Include metadata in export */
includeMetadata?: boolean;
/** Pretty print JSON/DSL */
prettyPrint?: boolean;
/** Include comments in DSL */
includeComments?: boolean;
/** Metadata position in DSL */
metadataPosition?: 'before' | 'after' | 'inline';
/** TypeScript interface name (for TS export) */
interfaceName?: string;
/** Additional export configuration */
config?: Record<string, unknown>;
}
/**
* Import options for rules
*/
interface ImportOptions {
/** Source format */
format: 'json' | 'dsl' | 'form-config';
/** Whether to merge with existing rules */
merge?: boolean;
/** Whether to preserve existing metadata */
preserveMetadata?: boolean;
/** Field schema mapping for validation */
fieldSchemas?: Record<string, unknown>;
}
/**
* Rule builder configuration
*/
interface RuleBuilderConfig {
/** Available field schemas */
fieldSchemas: Record<string, unknown>;
/** Context variables */
contextVariables?: unknown[];
/** Custom functions */
customFunctions?: unknown[];
/** Available rule templates */
templates?: RuleTemplate[];
/** UI configuration */
ui?: {
/** Theme */
theme?: 'light' | 'dark' | 'auto';
/** Show advanced features */
showAdvanced?: boolean;
/** Enable drag and drop */
enableDragDrop?: boolean;
/** Show DSL preview */
showDSLPreview?: boolean;
/** Show validation errors inline */
showInlineErrors?: boolean;
/** Auto-save interval (ms) */
autoSaveInterval?: number;
};
/** Validation configuration */
validation?: {
/** Enable real-time validation */
realTime?: boolean;
/** Validation strictness */
strictness?: 'strict' | 'normal' | 'loose';
/** Custom validation rules */
customRules?: unknown[];
};
/** Export/import configuration */
exportImport?: {
/** Default export format */
defaultExportFormat?: 'json' | 'dsl' | 'typescript';
/** Supported formats */
supportedFormats?: string[];
/** Include metadata by default */
includeMetadataByDefault?: boolean;
};
}
/**
* Configuration for optional field handling
*/
interface OptionalFieldConfig {
type: 'optionalField';
/** Type of optional field validation */
validationType: 'ifDefined' | 'ifNotNull' | 'ifExists' | 'withDefault';
/** Target field name */
fieldName: string;
/** Default value when field is undefined/null */
defaultValue?: unknown;
/** Reference to condition rule node ID */
conditionNodeId?: string;
/** Optional metadata for validation */
metadata?: SpecificationMetadata;
}
/**
* Configuration for expression specifications (aligned with @praxisui/specification)
*/
interface ExpressionConfig {
type: 'expression';
/** DSL expression string */
expression: string;
/** Function registry for validation */
functionRegistry?: FunctionRegistry<unknown>;
/** Context provider for variable resolution */
contextProvider?: ContextProvider;
/** Known field names for validation */
knownFields?: string[];
/** Enable performance warnings */
enablePerformanceWarnings?: boolean;
/** Maximum expression complexity */
maxComplexity?: number;
/** Metadata aligned with @praxisui/specification */
metadata?: SpecificationMetadata;
}
/**
* Configuration for contextual template specifications (aligned with @praxisui/specification)
*/
interface ContextualTemplateConfig {
type: 'contextualTemplate';
/** Template string with context tokens */
template: string;
/** Available context variables */
contextVariables?: Record<string, unknown>;
/** Context provider instance */
contextProvider?: ContextProvider;
/** Enable strict validation of context tokens */
strictContextValidation?: boolean;
/** Metadata aligned with @praxisui/specification */
metadata?: SpecificationMetadata;
}
declare class PraxisVisualBuilder {
config: RuleBuilderConfig | null;
initialRules: any;
rulesChanged: EventEmitter<any>;
exportRequested: EventEmitter<ExportOptions>;
importRequested: EventEmitter<ImportOptions>;
onRulesChanged(rules: any): void;
onExportRequested(options: ExportOptions): void;
onImportRequested(options: ImportOptions): void;
static ɵfac: i0.ɵɵFactoryDeclaration<PraxisVisualBuilder, never>;
static ɵcmp: i0.ɵɵComponentDeclaration<PraxisVisualBuilder, "praxis-visual-builder", never, { "config": { "alias": "config"; "required": false; }; "initialRules": { "alias": "initialRules"; "required": false; }; }, { "rulesChanged": "rulesChanged"; "exportRequested": "exportRequested"; "importRequested": "importRequested"; }, never, never, true, never>;
}
/**
* Field schema model for dynamic field configuration in the Visual Builder
*/
interface FieldSchema {
/** Unique field identifier */
name: string;
/** Human-readable field label */
label: string;
/** Field data type */
type: FieldType;
/** Optional description or help text */
description?: string;
/** Whether this field is required */
required?: boolean;
/** Allowed values for enum/select fields */
allowedValues?: FieldOption[];
/** Format constraints for the field */
format?: FieldFormat;
/** UI configuration for field display */
uiConfig?: FieldUIConfig;
/** Nested fields for object types */
properties?: Record<string, FieldSchema>;
/** Item schema for array types */
items?: FieldSchema;
}
interface FieldOption {
/** Option value */
value: any;
/** Option display label */
label: string;
/** Optional description */
description?: string;
/** Whether this option is disabled */
disabled?: boolean;
}
interface FieldFormat {
/** Minimum value (for numbers) or length (for strings/arrays) */
minimum?: number;
/** Maximum value (for numbers) or length (for strings/arrays) */
maximum?: number;
/** Regular expression pattern for string validation */
pattern?: string;
/** Date format for date fields */
dateFormat?: string;
/** Number format options */
numberFormat?: {
decimals?: number;
currency?: string;
percentage?: boolean;
};
}
interface FieldUIConfig {
/** Icon to display with the field */
icon?: string;
/** Color theme for the field */
color?: string;
/** Field category for grouping */
category?: string;
/** Field priority for sorting */
priority?: number;
/** Whether to show this field in simple mode */
showInSimpleMode?: boolean;
/** Custom CSS classes */
cssClass?: string;
}
declare enum FieldType {
STRING = "string",
NUMBER = "number",
INTEGER = "integer",
BOOLEAN = "boolean",
DATE = "date",
DATETIME = "datetime",
TIME = "time",
EMAIL = "email",
URL = "url",
PHONE = "phone",
ARRAY = "array",
OBJECT = "object",
ENUM = "enum",
UUID = "uuid",
JSON = "json"
}
/**
* Available comparison operators for each field type
*/
declare const FIELD_TYPE_OPERATORS: Record<FieldType, string[]>;
/**
* Operator display labels for UI
*/
declare const OPERATOR_LABELS: Record<string, string>;
/**
* Context for field schema interpretation
*/
interface FieldSchemaContext {
/** Available context variables (e.g., ${user.role}, ${now}) */
contextVariables?: ContextVariable[];
/** Available custom functions */
customFunctions?: CustomFunction[];
/** Global configuration */
config?: {
/** Whether to show advanced features */
showAdvanced?: boolean;
/** Default locale for formatting */
locale?: string;
/** Theme configuration */
theme?: 'light' | 'dark' | 'auto';
};
}
interface ContextVariable {
/** Variable name (without ${} wrapper) */
name: string;
/** Display label */
label: string;
/** Variable type */
type: FieldType;
/** Example value for preview */
example?: any;
/** Description */
description?: string;
}
interface CustomFunction {
/** Function name */
name: string;
/** Display label */
label: string;
/** Function description */
description?: string;
/** Expected parameter types */
parameters: {
name: string;
type: FieldType;
required?: boolean;
description?: string;
}[];
/** Return type */
returnType: FieldType;
/** Example usage */
example?: string;
}
/**
* Array Field Schema Support for Collection Validators
* Phase 2 Implementation
*/
/**
* Extended field schema for array types
*/
interface ArrayFieldSchema extends FieldSchema {
type: FieldType.ARRAY;
/** Schema for individual items in the array */
itemSchema?: FieldSchema;
/** Minimum number of items */
minItems?: number;
/** Maximum number of items */
maxItems?: number;
/** Whether items must be unique */
uniqueItems?: boolean;
/** Fields to check for uniqueness */
uniqueBy?: string[];
/** Default value for new items */
defaultItem?: any;
/** Whether to allow adding items */
allowAdd?: boolean;
/** Whether to allow removing items */
allowRemove?: boolean;
/** Whether to allow reordering items */
allowReorder?: boolean;
/** Custom validation rules for the array */
arrayValidation?: {
forEach?: {
rules: any[];
stopOnFirstError?: boolean;
};
uniqueBy?: {
fields: string[];
caseSensitive?: boolean;
ignoreEmpty?: boolean;
};
length?: {
min?: number;
max?: number;
errorMessage?: string;
};
};
/** UI configuration specific to arrays */
arrayUiConfig?: {
/** How to display the array */
displayMode?: 'table' | 'cards' | 'list' | 'accordion';
/** Whether to show item count */
showCount?: boolean;
/** Custom add button text */
addButtonText?: string;
/** Custom remove button text */
removeButtonText?: string;
/** Whether to confirm before removing */
confirmRemove?: boolean;
/** Message to show when array is empty */
emptyMessage?: string;
/** Whether to collapse items by default */
collapsedByDefault?: boolean;
/** Maximum items to show before pagination */
pageSize?: number;
};
}
/**
* Utility to check if a field schema is an array
*/
declare function isArrayFieldSchema(schema: FieldSchema): schema is ArrayFieldSchema;
/**
* Utility to get nested field paths from an array schema
*/
declare function getArrayItemFieldPaths(schema: ArrayFieldSchema, prefix?: string): string[];
/**
* Array validation context for runtime validation
*/
interface ArrayValidationContext {
/** The array being validated */
array: any[];
/** Current item being validated (for forEach) */
currentItem?: any;
/** Current item index (for forEach) */
currentIndex?: number;
/** Parent context */
parentContext?: any;
/** Field schema */
schema: ArrayFieldSchema;
/** Accumulated errors */
errors: ArrayValidationError[];
}
/**
* Array validation error
*/
interface ArrayValidationError {
/** Error type */
type: 'forEach' | 'uniqueBy' | 'minLength' | 'maxLength' | 'other';
/** Error message */
message: string;
/** Item index (if applicable) */
itemIndex?: number;
/** Field path within item (if applicable) */
fieldPath?: string;
/** Duplicate indices (for uniqueBy) */
duplicateIndices?: number[];
/** Expected value */
expected?: any;
/** Actual value */
actual?: any;
}
/**
* Array field analyzer for detecting array fields in schemas
*/
declare class ArrayFieldAnalyzer {
/**
* Analyze a schema tree and find all array fields
*/
static findArrayFields(schemas: Record<string, FieldSchema>): Record<string, ArrayFieldSchema>;
/**
* Get validation rules for an array field
*/
static getValidationRules(schema: ArrayFieldSchema): ArrayCollectionValidationRule[];
}
/**
* Array collection validation rule
*/
interface ArrayCollectionValidationRule {
type: 'forEach' | 'uniqueBy' | 'minLength' | 'maxLength';
value?: any;
fields?: string[];
rules?: any[];
message: string;
}
declare class FieldSchemaService {
private readonly _fieldSchemas;
private readonly _context;
readonly fieldSchemas$: Observable<Record<string, FieldSchema>>;
readonly context$: Observable<FieldSchemaContext>;
constructor();
/**
* Set field schemas for the visual builder
*/
setFieldSchemas(schemas: Record<string, FieldSchema>): void;
/**
* Add a single field schema
*/
addFieldSchema(name: string, schema: FieldSchema): void;
/**
* Remove a field schema
*/
removeFieldSchema(name: string): void;
/**
* Get field schema by name
*/
getFieldSchema(name: string): FieldSchema | undefined;
/**
* Get all field schemas
*/
getAllFieldSchemas(): Record<string, FieldSchema>;
/**
* Get field schemas as array with enhanced info
*/
getFieldSchemasArray(): Observable<EnhancedFieldSchema[]>;
/**
* Set context for field schemas
*/
setContext(context: FieldSchemaContext): void;
/**
* Get available operators for a field type
*/
getAvailableOperators(fieldType: FieldType): string[];
/**
* Get operator labels for a field type
*/
getOperatorLabels(fieldType: FieldType): Record<string, string>;
/**
* Validate field value against schema
*/
validateFieldValue(fieldName: string, value: any): ValidationResult;
/**
* Get field suggestions based on partial input
*/
getFieldSuggestions(partial: string, category?: string): FieldSchema[];
/**
* Create field schema from JSON Schema
*/
createFromJsonSchema(jsonSchema: any): Record<string, FieldSchema>;
/**
* Create field schema from form metadata
*/
createFromFormMetadata(formFields: any[]): Record<string, FieldSchema>;
/**
* Get context variables
*/
getContextVariables(): Observable<ContextVariable[]>;
/**
* Get custom functions
*/
getCustomFunctions(): Observable<CustomFunction[]>;
/**
* Group field schemas by category
*/
getFieldSchemasByCategory(): Observable<Record<string, FieldSchema[]>>;
private isValidType;
private validateFormat;
private convertJsonSchemaProperty;
private mapJsonSchemaType;
private mapFormFieldType;
private extractFormatFromField;
static ɵfac: i0.ɵɵFactoryDeclaration<FieldSchemaService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<FieldSchemaService>;
}
interface EnhancedFieldSchema extends FieldSchema {
operators: string[];
operatorLabels: Record<string, string>;
}
interface ValidationResult {
valid: boolean;
errors: string[];
}
interface DslContextVariable {
/** Variable name */
name: string;
/** Variable type */
type: 'string' | 'number' | 'boolean' | 'date' | 'object' | 'array';
/** Variable scope */
scope: 'user' | 'session' | 'env' | 'global';
/** Description */
description?: string;
/** Example value */
example?: string;
}
/**
* Registry service for managing RuleNode instances and their relationships.
* Solves the core problem of resolving string IDs to actual RuleNode objects.
*/
declare class RuleNodeRegistryService {
private nodes;
private nodesSubject;
/**
* Observable stream of all registered nodes
*/
nodes$: Observable<Map<string, RuleNode>>;
/**
* Register a node in the registry
*/
register(node: RuleNode): void;
/**
* Register multiple nodes at once
*/
registerAll(nodes: RuleNode[]): void;
/**
* Unregister a node from the registry
*/
unregister(nodeId: string): boolean;
/**
* Resolve a node by its ID
*/
resolve(nodeId: string): RuleNode | null;
/**
* Retrieve a node synchronously by its ID.
*
* Provided for backwards compatibility with code that expected a
* synchronous `getNode` API. Internally this simply delegates to
* {@link resolve}.
*/
getNode(nodeId: string): RuleNode | null;
/**
* Resolve multiple nodes by their IDs
*/
resolveMultiple(nodeIds: string[]): RuleNode[];
/**
* Resolve children nodes for a given node
*/
resolveChildren(node: RuleNode): RuleNode[];
/**
* Get all nodes that have the specified parent ID
*/
getChildrenOf(parentId: string): RuleNode[];
/**
* Get the parent node of a given node
*/
getParent(node: RuleNode): RuleNode | null;
/**
* Get all root nodes (nodes without parents)
*/
getRootNodes(): RuleNode[];
/**
* Check if a node exists in the registry
*/
exists(nodeId: string): boolean;
/**
* Get all registered node IDs
*/
getAllIds(): string[];
/**
* Get all registered nodes
*/
getAllNodes(): RuleNode[];
/**
* Clear all nodes from the registry
*/
clear(): void;
/**
* Get the size of the registry
*/
size(): number;
/**
* Remove orphaned nodes (nodes without parents and not referenced by others)
*/
cleanupOrphanedNodes(): string[];
/**
* Detect circular references in the registry
*/
detectCircularReferences(): CircularReference[];
/**
* Get memory usage statistics
*/
getMemoryStats(): MemoryStats;
/**
* Validate registry integrity
*/
validateIntegrity(): RegistryIntegrityResult;
/**
* Perform automatic cleanup operations
*/
performCleanup(): CleanupResult;
/**
* Build a tree structure starting from root nodes
*/
buildTree(): RuleNodeTree[];
/**
* Build tree structure for a specific node
*/
buildNodeTree(node: RuleNode): RuleNodeTree;
/**
* Find nodes by a predicate function
*/
findNodes(predicate: (node: RuleNode) => boolean): RuleNode[];
/**
* Find nodes by type
*/
findNodesByType(type: string): RuleNode[];
/**
* Validate the graph structure integrity (legacy method for backward compatibility)
*/
validateGraphIntegrity(): RegistryValidationResult;
/**
* Check if a node has circular references
*/
private hasCircularReference;
/**
* Get observable for a specific node
*/
getNode$(nodeId: string): Observable<RuleNode | null>;
/**
* Get observable for children of a node
*/
getChildren$(nodeId: string): Observable<RuleNode[]>;
private notifyChange;
static ɵfac: i0.ɵɵFactoryDeclaration<RuleNodeRegistryService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<RuleNodeRegistryService>;
}
/**
* Tree structure for representing node hierarchies
*/
interface RuleNodeTree {
node: RuleNode;
children: RuleNodeTree[];
}
/**
* Result of registry integrity validation
*/
interface RegistryValidationResult {
isValid: boolean;
errors: string[];
warnings: string[];
}
/**
* Circular reference information
*/
interface CircularReference {
cycle: string[];
affectedNodes: string[];
}
/**
* Memory usage statistics
*/
interface MemoryStats {
totalNodes: number;
estimatedSizeBytes: number;
breakdown: {
config: number;
children: number;
metadata: number;
other: number;
};
}
/**
* Registry integrity validation result
*/
interface RegistryIntegrityResult {
isValid: boolean;
issues: string[];
warnings: string[];
circularReferences: CircularReference[];
memoryStats: MemoryStats;
}
/**
* Cleanup operation result
*/
interface CleanupResult {
orphanedNodesRemoved: string[];
circularReferencesDetected: CircularReference[];
memoryFreed: number;
}
/**
* Context interface for rule conversion operations
* Eliminates circular dependencies between converters and factory
*/
interface ConversionContext {
/**
* Convert a child node to a specification
* This method is provided by the factory to avoid circular dependencies
*/
convertChild<T extends object = any>(node: RuleNode): Specification<T>;
/**
* Convert multiple child nodes to specifications
*/
convertChildren<T extends object = any>(nodes: RuleNode[]): Specification<T>[];
/**
* Check if a node type is supported
*/
isSupported(nodeType: string): boolean;
/**
* Validate that a node can be converted
*/
validateNode(node: RuleNode): {
isValid: boolean;
errors: string[];
};
}
/**
* Interface for rule converters that transform RuleNodes to Specifications
*/
interface RuleConverter {
/**
* Convert a RuleNode to a Specification
* @param node The node to convert
* @param context Context providing access to child conversion capabilities
*/
convert<T extends object = any>(node: RuleNode, context?: ConversionContext): Specification<T>;
/**
* Check if this converter can handle the given node type
*/
canConvert(nodeType: string): boolean;
/**
* Get the supported node types
*/
getSupportedTypes(): string[];
/**
* Set the conversion context (called by factory during initialization)
*/
setContext?(context: ConversionContext): void;
}
/**
* Base abstract class for rule converters
*/
declare abstract class BaseRuleConverter implements RuleConverter {
protected abstract supportedTypes: string[];
protected context?: ConversionContext;
abstract convert<T extends object = any>(node: RuleNode, context?: ConversionContext): Specification<T>;
/**
* Set the conversion context
*/
setContext(context: ConversionContext): void;
canConvert(nodeType: string): boolean;
getSupportedTypes(): string[];
protected validateNode(node: RuleNode, expectedType?: string): void;
}
/**
* Converter for field condition rules to field specifications
*/
declare class FieldConditionConverter extends BaseRuleConverter {
protected supportedTypes: string[];
convert<T extends object>(node: RuleNode, context?: ConversionContext): Specification<T>;
private mapOperator;
private convertValue;
private inferAndConvertValue;
static ɵfac: i0.ɵɵFactoryDeclaration<FieldConditionConverter, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<FieldConditionConverter>;
}
/**
* Converter for boolean group rules (AND, OR, NOT, XOR, IMPLIES)
*/
declare class BooleanGroupConverter extends BaseRuleConverter {
private nodeRegistry;
protected supportedTypes: string[];
constructor(nodeRegistry: RuleNodeRegistryService);
convert<T extends object>(node: RuleNode, context?: ConversionContext): Specification<T>;
static ɵfac: i0.ɵɵFactoryDeclaration<BooleanGroupConverter, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<BooleanGroupConverter>;
}
/**
* Converter for cardinality rules (atLeast, exactly)
*/
declare class CardinalityConverter extends BaseRuleConverter {
private nodeRegistry;
protected supportedTypes: string[];
constructor(nodeRegistry: RuleNodeRegistryService);
convert<T extends object>(node: RuleNode, context?: ConversionContext): Specification<T>;
static ɵfac: i0.ɵɵFactoryDeclaration<CardinalityConverter, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<CardinalityConverter>;
}
/**
* Factory service for converting RuleNodes to Specifications
* Uses Strategy pattern to delegate to appropriate converters
*/
declare class ConverterFactoryService {
private fieldConditionConverter;
private booleanGroupConverter;
private cardinalityConverter;
private converters;
private context;
constructor(fieldConditionConverter: FieldConditionConverter, booleanGroupConverter: BooleanGroupConverter, cardinalityConverter: CardinalityConverter);
/**
* Convert a RuleNode to a Specification
*/
convert<T extends object = any>(node: RuleNode): Specification<T>;
/**
* Get converter for a specific node type
*/
getConverter(nodeType: string): RuleConverter | undefined;
/**
* Register a new converter
*/
registerConverter(name: string, converter: RuleConverter): void;
/**
* Unregister a converter
*/
unregisterConverter(name: string): boolean;
/**
* Get all supported node types
*/
getSupportedTypes(): string[];
/**
* Check if a node type is supported
*/
isSupported(nodeType: string): boolean;
private initializeContext;
/**
* Internal convert method that bypasses context to avoid recursion
*/
private convertInternal;
private initializeConverters;
/**
* Convert multiple nodes to specifications
*/
convertMultiple<T extends object = any>(nodes: RuleNode[]): Specification<T>[];
/**
* Validate that a node can be converted
*/
validateNode(node: RuleNode): {
isValid: boolean;
errors: string[];
};
/**
* Get statistics about converter usage
*/
getStatistics(): ConverterStatistics;
static ɵfac: i0.ɵɵFactoryDeclaration<ConverterFactoryService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<ConverterFactoryService>;
}
/**
* Statistics about the converter factory
*/
interface ConverterStatistics {
converterCount: number;
supportedTypeCount: number;
supportedTypes: string[];
converterNames: string[];
}
/**
* Configuration for parsing DSL expressions
*/
interface DslParsingConfig$1 {
/** Available function registry */
functionRegistry?: FunctionRegistry<any>;
/** Context provider for variable resolution */
contextProvider?: ContextProvider;
/** Known field names for validation */
knownFields?: string[];
/** Enable performance warnings */
enablePerformanceWarnings?: boolean;
/** Maximum expression complexity */
maxComplexity?: number;
}
/**
* Result of parsing a DSL expression
*/
interface DslParsingResult$1<T extends object = any> {
/** Whether parsing was successful */
success: boolean;
/** Parsed specification (if successful) */
specification?: Specification<T>;
/** Validation issues found */
issues: ValidationIssue$1[];
/** Performance metrics */
metrics?: {
parseTime: number;
complexity: number;
};
}
/**
* Configuration for contextual specification support
*/
interface SpecificationContextualConfig {
/** Context variables available for token resolution */
contextVariables?: DslContextVariable[];
/** Context provider instance */
contextProvider?: ContextProvider;
/** Enable strict validation of context tokens */
strictContextValidation?: boolean;
}
declare class SpecificationBridgeService {
private nodeRegistry;
private converterFactory;
private dslExporter;
private dslParser;
private dslValidator;
private contextProvider?;
constructor(nodeRegistry: RuleNodeRegistryService, converterFactory: ConverterFactoryService);
/**
* Converts a RuleNode tree to a Specification instance
*/
ruleNodeToSpecification<T extends object = any>(node: RuleNode): Specification<T>;
/**
* Converts a Specification instance to a RuleNode tree
*/
specificationToRuleNode<T extends object = any>(spec: Specification<T>): RuleNode;
/**
* Exports a RuleNode tree to DSL format
*/
exportToDsl<T extends object = any>(node: RuleNode, options?: Partial<ExportOptions$1>): string;
/**
* Exports a RuleNode tree to DSL format with metadata
*/
exportToDslWithMetadata<T extends object = any>(node: RuleNode): string;
/**
* Validates that a RuleNode can be successfully round-tripped
*/
validateRoundTrip<T extends object = any>(node: RuleNode): {
success: boolean;
errors: string[];
warnings: string[];
};
/**
* Performs deep validation between original and reconstructed nodes
*/
private deepValidateRoundTrip;
/**
* Compares two configuration objects
*/
private compareConfigs;
/**
* Validates DSL round-trip conversion
*/
private validateDslRoundTrip;
/**
* Parses a DSL expression string into a Specification
*/
parseDslExpression<T extends object = any>(expression: string, config?: DslParsingConfig$1): DslParsingResult$1<T>;
/**
* Creates a ContextualSpecification with token resolution
*/
createContextualSpecification<T extends object = any>(template: string, config?: SpecificationContextualConfig): ContextualSpecification<T>;
/**
* Resolves context tokens in a template using provided variables
*/
resolveContextTokens(template: string, contextVariables: DslContextVariable[]): string;
/**
* Extracts all context tokens from a template
*/
extractContextTokens(template: string): string[];
/**
* Validates that all context tokens in a template have corresponding variables
*/
validateContextTokens(template: string, contextVariables: DslContextVariable[]): ValidationIssue$1[];
/**
* Converts a DSL expression to a ContextualSpecification
*/
dslToContextualSpecification<T extends object = any>(dslExpression: string, config?: SpecificationContextualConfig): ContextualSpecification<T>;
/**
* Converts a ContextualSpecification back to DSL template
*/
contextualSpecificationToDsl<T extends object = any>(spec: ContextualSpecification<T>): string;
/**
* Performs round-trip validation for expression specifications
*/
validateExpressionRoundTrip<T extends object = any>(originalExpression: string, config?: DslParsingConfig$1): {
success: boolean;
errors: string[];
warnings: string[];
reconstructedExpression?: string;
};
/**
* Updates the context provider for contextual specifications
*/
updateContextProvider(contextProvider: ContextProvider): void;
/**
* Gets the current context provider
*/
getContextProvider(): ContextProvider | undefined;
/**
* Gets expected DSL keywords for a given node type
*/
private getExpectedDslKeywords;
private createFieldSpecification;
private createBooleanGroupSpecification;
private createFunctionSpecification;
private createFieldToFieldSpecification;
private createCardinalitySpecification;
/**
* Creates conditional validator specifications (Phase 1 implementation)
*/
private createConditionalValidatorSpecification;
/**
* Creates collection validator specifications (Phase 2 implementation)
*/
private createCollectionValidatorSpecification;
/**
* Creates expression specification from DSL (Phase 4 implementation)
*/
private createExpressionSpecification;
/**
* Creates contextual specification (Phase 4 implementation)
*/
private createContextualSpecificationFromNode;
private convertToComparisonOperator;
private convertValue;
private jsonToRuleNode;
private mapSpecificationTypeToNodeType;
private mapComparisonOperator;
private inferValueType;
private generateNodeId;
private generateNodeLabel;
/**
* Creates a context provider from context variables
*/
private createContextProviderFromVariables;
/**
* Calculates complexity of a DSL expression
*/
private calculateComplexity;
/**
* Finds the position of a token in a template
*/
private findTokenPosition;
/**
* Suggests similar variable names using Levenshtein distance
*/
private suggestSimilarVariable;
/**
* Calculates Levenshtein distance between two strings
*/
private levenshteinDistance;
/**
* Parses a value expression. Phase 1 shim: accepts strings starting with '=' or { expr } objects.
* Returns success when the shape is acceptable; full DSL parsing will be integrated in Phase 2.
*/
parseDslExpressionValue(expression: string | {
expr: string;
} | null | undefined, _config?: DslParsingConfig$1): {
success: boolean;
issues: string[];
};
/**
* Evaluates a value expression with a row context. Phase 1 shim: executes simple '=' expressions in a sandboxed Function.
* In Phase 2, this will use the real DSL evaluator from @praxisui/specification.
*/
evaluateValue(expression: string | {
expr: string;
}, ctx: {
row: any;
}): any;
static ɵfac: i0.ɵɵFactoryDeclaration<SpecificationBridgeService, never>;
static ɵprov: i0.ɵɵInjectableDeclaration<SpecificationBridgeService>;
}
interface RoundTripValidationResult {
success: boolean;
errors: ValidationError$1[];
warnings: ValidationError$1[];
stages: {
visualToSpecification: {
success: boolean;
error?: string;
};
specificationToDsl: {
success: boolean;
error?: string;
dsl?: string;
};
dslToSpecification: {
success: boolean;
error?: string;
};
specificationToVisual: {
success: boolean;
error?: string;
};
};
dataIntegrity: {
nodeCountMatch: boolean;
structureMatch: boolean;
metadataPreserved: boolean;
logicPreserved: boolean;
};
performance: {
totalTime: number;
stageTimings: Record<string, number>;
};
}
interface RoundTripTestCase {
id: string;
name: string;
description: string;
visualRule: RuleNode;
expectedDsl?: string;
expectedValidation?: {
shouldSucceed: boolean;
expectedErrors?: string[];
expectedWarnings?: string[];
};
}
declare class RoundTripValidatorService {
private specificationBridge;
private dslParser;
constructor(specificationBridge: SpecificationBridgeService);
/**
* Validates complete round-trip conversion: Visual → DSL → Visual
*/
validateRoundTrip(visualRule: RuleNode): RoundTripValidationResult;
/**
* Validates data integrity between original and reconstructed visual rules
*/
private valida