@praxisui/visual-builder 3.0.0-beta.9 → 5.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,34 +1,26 @@
1
1
  # @praxisui/visual-builder
2
2
 
3
- Visual rule and expression builder for Praxis UI with mini-DSL support, validation, contextual variables and round-trip editing between visual and textual representations.
3
+ Visual rule builder for Praxis UI with JSON Logic as the canonical contract, contextual variables and governed visual/manual authoring.
4
4
 
5
5
  ## Documentation
6
6
 
7
7
  - Official documentation: https://praxisui.dev
8
8
  - Quickstart reference app: https://github.com/codexrodrigues/praxis-ui-quickstart
9
- - Recommended for: teams that need a visual rule editor with DSL interoperability, validation and contextual variable management
9
+ - Recommended for: teams that need a visual rule editor with JSON Logic authoring, validation and contextual variable management
10
10
 
11
11
  ## When to use
12
12
 
13
- - Build visual rule editors without losing a textual DSL representation
14
- - Validate and round-trip expressions between UI and code-like syntax
13
+ - Build visual rule editors over the same JSON Logic contract used by Praxis runtimes
14
+ - Validate and review rules without inventing a second canonical syntax
15
15
  - Support complex contextual variables in low-code or admin experiences
16
16
 
17
17
  ## Features
18
18
 
19
- ### 🎯 **Core Components**
20
-
21
- - **ExpressionEditorComponent**: Advanced DSL expression editor with syntax highlighting and autocomplete
22
- - **ContextVariableManagerComponent**: Complete context variable management system
23
- - **SpecificationBridgeService**: Bidirectional conversion between visual rules and DSL expressions
24
-
25
- ### ⚡ **Advanced Capabilities**
26
-
27
- - **Mini-DSL Support**: Full DSL parsing, validation, and round-trip conversion
28
- - **Context Variables**: Dynamic variable resolution with scoped contexts
29
- - **Real-time Validation**: Live syntax checking with performance metrics
30
- - **Autocomplete**: Intelligent suggestions for fields, functions, operators, and variables
31
- - **Round-trip Integrity**: Seamless conversion between visual and textual representations
19
+ - `RuleEditorComponent`: canonical visual editor for target, condition and effect authoring
20
+ - `ContextVariableManagerComponent`: contextual variable management for builder-driven flows
21
+ - `RuleBuilderService`: canonical state and conversion flow for visual authoring and JSON Logic output
22
+ - JSON Logic authoring: visual and manual JSON editing over the canonical runtime contract, without textual DSL fallback
23
+ - Governed authoring: one persisted structure across builder, AI and runtime
32
24
 
33
25
  ## Installation
34
26
 
@@ -38,625 +30,66 @@ npm install @praxisui/visual-builder
38
30
 
39
31
  ## Quick Start
40
32
 
41
- ### Basic Expression Editor
42
-
43
- ```typescript
44
- import { ExpressionEditorComponent } from "@praxisui/visual-builder";
45
-
46
- @Component({
47
- template: ` <praxis-expression-editor [fieldSchemas]="fieldSchemas" [contextVariables]="contextVariables" [(expression)]="expression" (validationChange)="onValidationChange($event)"> </praxis-expression-editor> `,
48
- })
49
- export class MyComponent {
50
- fieldSchemas = [
51
- { name: "age", type: "number", label: "Age" },
52
- { name: "name", type: "string", label: "Name" },
53
- { name: "active", type: "boolean", label: "Active" },
54
- ];
55
-
56
- contextVariables = [{ name: "user.minAge", type: "number", scope: "user", example: "18" }];
57
-
58
- expression = "age > ${user.minAge} && active == true";
59
-
60
- onValidationChange(result: ExpressionValidationResult) {
61
- console.log("Expression valid:", result.isValid);
62
- console.log("Issues:", result.issues);
63
- }
64
- }
65
- ```
66
-
67
33
  ### Context Variable Management
68
34
 
69
35
  ```typescript
70
- import { ContextVariableManagerComponent } from "@praxisui/visual-builder";
36
+ import { ContextVariableManagerComponent } from '@praxisui/visual-builder';
71
37
 
72
38
  @Component({
73
- template: ` <praxis-context-variable-manager [(contextVariables)]="contextVariables" [allowedScopes]="allowedScopes" (variableAdd)="onVariableAdd($event)" (variableUpdate)="onVariableUpdate($event)"> </praxis-context-variable-manager> `,
39
+ template: `
40
+ <praxis-context-variable-manager
41
+ [(contextVariables)]="contextVariables"
42
+ [allowedScopes]="allowedScopes">
43
+ </praxis-context-variable-manager>
44
+ `,
74
45
  })
75
46
  export class VariableManagementComponent {
76
- contextVariables: EnhancedContextVariable[] = [];
77
- allowedScopes = ["user", "session", "global"];
78
-
79
- onVariableAdd(variable: EnhancedContextVariable) {
80
- console.log("New variable added:", variable);
81
- }
47
+ contextVariables = [];
48
+ allowedScopes = ['user', 'session', 'global'];
82
49
  }
83
50
  ```
84
51
 
85
- ### Visual Rule Editor in Embedded Mode
52
+ ### Visual Rule Editor
86
53
 
87
54
  ```typescript
88
- import { RuleEditorComponent } from "@praxisui/visual-builder";
55
+ import { RuleEditorComponent } from '@praxisui/visual-builder';
89
56
 
90
57
  @Component({
91
- template: ` <praxis-rule-editor [config]="builderConfig" [embedded]="true" (rulesChanged)="onRulesChanged($event)"> </praxis-rule-editor> `,
58
+ template: `
59
+ <praxis-rule-editor
60
+ [config]="builderConfig"
61
+ [embedded]="true"
62
+ (rulesChanged)="onRulesChanged($event)">
63
+ </praxis-rule-editor>
64
+ `,
92
65
  })
93
66
  export class EmbeddedRuleEditor {
94
67
  builderConfig = {
95
- /* ... */
68
+ fieldSchemas: {},
96
69
  };
97
70
 
98
- onRulesChanged(rules: any) {
99
- console.log("Updated rules:", rules);
71
+ onRulesChanged(rules: unknown) {
72
+ console.log('Updated rules:', rules);
100
73
  }
101
74
  }
102
75
  ```
103
76
 
104
- `embedded` mode allows the editor to fit within existing layouts without spawning nested scrollbars, making it suitable for corporate dashboards and configuration panels where space is constrained.
105
-
106
- ## Mini-DSL Language Guide
107
-
108
- ### Basic Syntax
109
-
110
- The mini-DSL supports a rich expression language for building validation rules and conditions.
111
-
112
- #### Field References
113
-
114
- ```dsl
115
- age > 18
116
- name == "John Doe"
117
- active == true
118
- score >= 80
119
- ```
120
-
121
- #### Context Variables
122
-
123
- Context variables are referenced using `${scope.variable}` syntax:
124
-
125
- ```dsl
126
- age > ${user.minAge}
127
- department == "${user.department}"
128
- level >= ${policy.requiredLevel}
129
- ```
130
-
131
- #### Operators
132
-
133
- **Comparison Operators:**
134
-
135
- ```dsl
136
- age == 25 # Equality
137
- age != 30 # Inequality
138
- age > 18 # Greater than
139
- age >= 21 # Greater than or equal
140
- age < 65 # Less than
141
- age <= 64 # Less than or equal
142
- ```
143
-
144
- **Logical Operators:**
145
-
146
- ```dsl
147
- age > 18 && active == true # AND
148
- priority == "high" || urgent == true # OR
149
- !(deleted == true) # NOT
150
- status == "A" ^ backup == true # XOR (exclusive or)
151
- qualified == true => approved == true # IMPLIES
152
- ```
153
-
154
- **Membership Operators:**
155
-
156
- ```dsl
157
- category in ["tech", "science", "math"] # IN (array membership)
158
- role not in ["guest", "anonymous"] # NOT IN
159
- ```
160
-
161
- **Null Checks:**
162
-
163
- ```dsl
164
- description != null # Not null
165
- optional == null # Is null
166
- ```
167
-
168
- #### Functions
169
-
170
- **String Functions:**
171
-
172
- ```dsl
173
- contains(tags, "important") # Check if string/array contains value
174
- startsWith(email, "admin") # Check if string starts with value
175
- endsWith(filename, ".pdf") # Check if string ends with value
176
- length(description) > 10 # Get string/array length
177
- upper(category) == "TECHNOLOGY" # Convert to uppercase
178
- lower(name) == "john" # Convert to lowercase
179
- ```
180
-
181
- **Collection Functions:**
182
-
183
- ```dsl
184
- atLeast(2, [condition1, condition2, condition3]) # At least N conditions true
185
- exactly(1, [optionA, optionB, optionC]) # Exactly N conditions true
186
- forEach(items, itemCondition) # All items satisfy condition
187
- uniqueBy(collection, ["field1", "field2"]) # Unique by specified fields
188
- minLength(array, 3) # Minimum array length
189
- maxLength(array, 10) # Maximum array length
190
- ```
191
-
192
- **Conditional Functions:**
193
-
194
- ```dsl
195
- requiredIf(field, condition) # Field required if condition true
196
- visibleIf(field, condition) # Field visible if condition true
197
- disabledIf(field, condition) # Field disabled if condition true
198
- readonlyIf(field, condition) # Field readonly if condition true
199
- ```
200
-
201
- **Optional Handling:**
202
-
203
- ```dsl
204
- ifDefined(optionalField, condition) # Apply condition only if field defined
205
- ifNotNull(field, condition) # Apply condition only if field not null
206
- ifExists(field, condition) # Apply condition only if field exists
207
- withDefault(field, defaultValue, condition) # Use default if field undefined
208
- ```
209
-
210
- ### Complex Expressions
211
-
212
- #### Nested Conditions
213
-
214
- ```dsl
215
- ((age >= 18 && age <= 65) || experience > 10) &&
216
- (department == "Engineering" || department == "Product") &&
217
- !(archived == true || deleted == true)
218
- ```
219
-
220
- #### Mixed Context and Fields
221
-
222
- ```dsl
223
- salary >= ${policy.minSalary} &&
224
- salary <= ${policy.maxSalary} &&
225
- location in ${user.allowedLocations} &&
226
- startDate >= "${session.currentDate}"
227
- ```
228
-
229
- #### Function Composition
230
-
231
- ```dsl
232
- contains(upper(department), "ENG") &&
233
- length(trim(description)) > ${config.minDescLength} &&
234
- endsWith(lower(email), "@company.com")
235
- ```
236
-
237
- ### Context Variables
238
-
239
- Context variables provide dynamic values that can be resolved at runtime based on different scopes.
240
-
241
- #### Scopes
242
-
243
- **User Scope** (`user.*`):
244
-
245
- - User-specific values (preferences, profile data)
246
- - Example: `${user.department}`, `${user.role}`, `${user.level}`
247
-
248
- **Session Scope** (`session.*`):
249
-
250
- - Session-specific values (temporary data, current state)
251
- - Example: `${session.currentDate}`, `${session.userId}`, `${session.locale}`
252
-
253
- **Environment Scope** (`env.*`):
254
-
255
- - Environment configuration (debug flags, feature toggles)
256
- - Example: `${env.debug}`, `${env.features.newUI}`, `${env.apiVersion}`
257
-
258
- **Global Scope** (`global.*`):
259
-
260
- - Application-wide constants (policies, configurations)
261
- - Example: `${global.minAge}`, `${global.maxFileSize}`, `${global.supportedFormats}`
262
-
263
- #### Variable Types
264
-
265
- **String Variables:**
266
-
267
- ```dsl
268
- name == "${user.fullName}"
269
- department == "${user.department}"
270
- ```
271
-
272
- **Number Variables:**
273
-
274
- ```dsl
275
- age > ${user.minAge}
276
- salary >= ${policy.baseSalary}
277
- ```
77
+ ## Canonical Authoring Services
278
78
 
279
- **Boolean Variables:**
79
+ Use the canonical public services:
280
80
 
281
- ```dsl
282
- active == ${user.isActive}
283
- debug == ${env.debugMode}
284
- ```
285
-
286
- **Array Variables:**
287
-
288
- ```dsl
289
- category in ${user.allowedCategories}
290
- format in ${global.supportedFormats}
291
- ```
81
+ - `RuleBuilderService` for state, authoring flow and JSON Logic output
82
+ - `RuleValidationService` for builder-facing validation
83
+ - `ContextManagementService` for scoped contextual variables
292
84
 
293
- **Date Variables:**
294
-
295
- ```dsl
296
- createdDate >= "${session.startDate}"
297
- expirationDate <= "${policy.maxExpirationDate}"
298
- ```
299
-
300
- ### Validation and Error Handling
301
-
302
- #### Syntax Errors
303
-
304
- The DSL parser provides detailed error messages for syntax issues:
305
-
306
- ```typescript
307
- // Invalid syntax examples and their error messages:
308
-
309
- // "age >" -> "Missing operand after comparison operator"
310
- // "age >> 18" -> "Unknown operator '>>'"
311
- // "(age > 18" -> "Unclosed parentheses"
312
- // "unknownFunc(age)" -> "Unknown function 'unknownFunc'"
313
- ```
314
-
315
- #### Field Validation
316
-
317
- ```typescript
318
- // Unknown field warnings:
319
- // "unknownField == 'test'" -> "Unknown field: unknownField. Did you mean 'knownField'?"
320
- ```
321
-
322
- #### Context Variable Validation
323
-
324
- ```typescript
325
- // Missing context variable warnings:
326
- // "age > ${missing.variable}" -> "Unknown context variable: missing.variable"
327
- // With suggestions: "Did you mean 'existing.variable'?"
328
- ```
329
-
330
- #### Performance Warnings
331
-
332
- ```typescript
333
- // Complex expression warnings:
334
- // For expressions with >50 operators:
335
- // "Expression complexity is high (67 operators). Consider breaking into smaller expressions."
336
- ```
337
-
338
- ## API Reference
339
-
340
- ### Exports
341
-
342
- Main exports available from `@praxisui/visual-builder` (see `projects/praxis-visual-builder/src/public-api.ts`):
343
-
344
- - Components: `RuleEditorComponent`, `RuleCanvasComponent`, `RuleNodeComponent`, `FieldConditionEditorComponent`, `ConditionalValidatorEditorComponent`, `CollectionValidatorEditorComponent`, `MetadataEditorComponent`, `DslViewerComponent`, `JsonViewerComponent`, `RoundTripTesterComponent`, `ExportDialogComponent`, `DslLinterComponent`, `VisualRuleBuilderComponent`, `TemplateGalleryComponent`, `TemplateEditorDialogComponent`, `TemplatePreviewDialogComponent`
345
- - Services: `SpecificationBridgeService`, `RuleBuilderService`, `RoundTripValidatorService`, `ExportIntegrationService`, `WebhookIntegrationService`, `RuleTemplateService`, `RuleValidationService`, `RuleNodeRegistryService`, `ContextManagementService`, `FieldSchemaService`, `RuleConversionService`, `DslParsingService`
346
- - Models/Types: `FieldSchema`, `RuleBuilderConfig`, `ArrayFieldSchema`, `ContextScope`, `ContextEntry`, `ContextValue`, `SpecificationContextualConfig`
347
- - Errors: `VisualBuilderError`, `VBValidationError`, `ConversionError`, `RegistryError`, `DslError`, `ContextError`, `ConfigurationError`, `InternalError`, `ErrorCategory`, `ErrorSeverity`, `ErrorHandler`, `globalErrorHandler`, `createError`, `ErrorInfo`, `ErrorStatistics`
348
-
349
- ### ExpressionEditorComponent
350
-
351
- #### Inputs
352
-
353
- ```typescript
354
- @Input() expression: string = ''; // Current DSL expression
355
- @Input() fieldSchemas: FieldSchema[] = []; // Available fields for autocomplete
356
- @Input() contextVariables: ContextVariable[] = []; // Available context variables
357
- @Input() functionRegistry?: FunctionRegistry; // Custom function registry
358
- @Input() validationConfig?: ValidationConfig; // Validation configuration
359
- @Input() editorOptions?: EditorOptions; // Editor display options
360
- ```
361
-
362
- #### Outputs
363
-
364
- ```typescript
365
- @Output() expressionChange = new EventEmitter<string>(); // Expression changes
366
- @Output() validationChange = new EventEmitter<ExpressionValidationResult>(); // Validation updates
367
- @Output() suggestionRequest = new EventEmitter<SuggestionContext>(); // Autocomplete requests
368
- @Output() focusChange = new EventEmitter<boolean>(); // Focus state changes
369
- ```
370
-
371
- #### Methods
372
-
373
- ```typescript
374
- // Get autocomplete suggestions
375
- getSuggestions(text: string, position: number): Promise<DslSuggestion[]>
376
-
377
- // Validate current expression
378
- validateExpression(): Promise<ExpressionValidationResult>
379
-
380
- // Insert text at cursor position
381
- insertTextAtCursor(text: string): void
382
-
383
- // Format expression
384
- formatExpression(): void
385
-
386
- // Clear expression
387
- clearExpression(): void
388
- ```
389
-
390
- ### ContextVariableManagerComponent
391
-
392
- #### Inputs
393
-
394
- ```typescript
395
- @Input() contextVariables: EnhancedContextVariable[] = []; // Current variables
396
- @Input() allowedScopes: ContextScope[] = []; // Allowed variable scopes
397
- @Input() readOnly: boolean = false; // Read-only mode
398
- @Input() showCategories: boolean = true; // Show category grouping
399
- @Input() allowImportExport: boolean = true; // Enable import/export
400
- ```
401
-
402
- #### Outputs
403
-
404
- ```typescript
405
- @Output() contextVariablesChange = new EventEmitter<EnhancedContextVariable[]>(); // Variables updated
406
- @Output() variableAdd = new EventEmitter<EnhancedContextVariable>(); // Variable added
407
- @Output() variableUpdate = new EventEmitter<EnhancedContextVariable>(); // Variable updated
408
- @Output() variableDelete = new EventEmitter<string>(); // Variable deleted
409
- @Output() importComplete = new EventEmitter<ImportResult>(); // Import completed
410
- ```
411
-
412
- #### Methods
413
-
414
- ```typescript
415
- // Add new variable
416
- addVariable(variable: Partial<EnhancedContextVariable>): void
417
-
418
- // Update existing variable
419
- updateVariable(id: string, updates: Partial<EnhancedContextVariable>): void
420
-
421
- // Delete variable
422
- deleteVariable(id: string): void
423
-
424
- // Get variables by scope
425
- getVariablesByScope(scope: string): EnhancedContextVariable[]
426
-
427
- // Export variables
428
- exportVariables(format: 'json' | 'csv'): string
429
-
430
- // Import variables
431
- importVariables(data: string, format: 'json' | 'csv'): Promise<ImportResult>
432
-
433
- // Validate variable name
434
- isValidVariableName(name: string): boolean
435
- ```
436
-
437
- ### SpecificationBridgeService
438
-
439
- #### DSL Expression Methods
440
-
441
- ```typescript
442
- // Parse DSL expression to specification
443
- parseDslExpression<T>(expression: string, config?: DslParsingConfig): DslParsingResult<T>
444
-
445
- // Validate expression round-trip integrity
446
- validateExpressionRoundTrip<T>(expression: string, config?: DslParsingConfig): RoundTripResult
447
-
448
- // Create contextual specification
449
- createContextualSpecification<T>(template: string, config?: ContextualConfig): ContextualSpecification<T>
450
-
451
- // Resolve context tokens in template
452
- resolveContextTokens(template: string, variables: ContextVariable[]): string
453
-
454
- // Extract context tokens from template
455
- extractContextTokens(template: string): string[]
456
-
457
- // Validate context tokens
458
- validateContextTokens(template: string, variables: ContextVariable[]): ValidationIssue[]
459
- ```
460
-
461
- #### Rule Node Conversion Methods
462
-
463
- ```typescript
464
- // Convert rule node to specification
465
- ruleNodeToSpecification<T>(node: RuleNode): Specification<T>
466
-
467
- // Convert specification to rule node
468
- specificationToRuleNode<T>(spec: Specification<T>): RuleNode
469
-
470
- // Export rule node to DSL
471
- exportToDsl<T>(node: RuleNode, options?: ExportOptions): string
472
-
473
- // Validate round-trip through rule nodes
474
- validateRoundTrip<T>(node: RuleNode): RoundTripValidationResult
475
- ```
476
-
477
- #### Context Provider Methods
478
-
479
- ```typescript
480
- // Update context provider
481
- updateContextProvider(provider: ContextProvider): void
482
-
483
- // Get current context provider
484
- getContextProvider(): ContextProvider | undefined
485
- ```
486
-
487
- ## Advanced Usage
488
-
489
- ### Custom Function Registry
490
-
491
- ```typescript
492
- import { FunctionRegistry } from '@praxisui/specification';
493
-
494
- // Create custom function registry
495
- const customRegistry = new FunctionRegistry();
496
-
497
- // Register custom functions
498
- customRegistry.register('isEmail', {
499
- name: 'isEmail',
500
- arity: 1,
501
- implementation: (value: string) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
502
- description: 'Validates email format'
503
- });
504
-
505
- customRegistry.register('dateAdd', {
506
- name: 'dateAdd',
507
- arity: 3,
508
- implementation: (date: Date, amount: number, unit: string) => {
509
- // Implementation for date arithmetic
510
- },
511
- description: 'Adds time to a date'
512
- });
513
-
514
- // Use in expression editor
515
- <praxis-expression-editor
516
- [functionRegistry]="customRegistry"
517
- expression="isEmail(userEmail) && dateAdd(startDate, 30, 'days') > now()">
518
- </praxis-expression-editor>
519
- ```
520
-
521
- ### Custom Context Provider
522
-
523
- ```typescript
524
- import { ContextProvider } from "@praxisui/specification";
525
-
526
- class DatabaseContextProvider implements ContextProvider {
527
- private cache = new Map<string, any>();
528
-
529
- async getValue(key: string): Promise<any> {
530
- if (this.cache.has(key)) {
531
- return this.cache.get(key);
532
- }
533
-
534
- // Fetch from database or API
535
- const value = await this.fetchFromDatabase(key);
536
- this.cache.set(key, value);
537
- return value;
538
- }
539
-
540
- hasValue(key: string): boolean {
541
- return this.cache.has(key) || this.isValidKey(key);
542
- }
543
-
544
- setValue(key: string, value: any): void {
545
- this.cache.set(key, value);
546
- }
547
-
548
- // ... other methods
549
- }
550
-
551
- // Use custom provider
552
- const provider = new DatabaseContextProvider();
553
- this.bridgeService.updateContextProvider(provider);
554
- ```
555
-
556
- ### Integration with Form Builders
557
-
558
- ```typescript
559
- @Component({
560
- template: `
561
- <form [formGroup]="ruleForm">
562
- <!-- Visual Rule Builder -->
563
- <praxis-expression-editor formControlName="expression" [fieldSchemas]="formFieldSchemas" [contextVariables]="availableVariables"> </praxis-expression-editor>
564
-
565
- <!-- Context Variable Management -->
566
- <praxis-context-variable-manager [(contextVariables)]="availableVariables" [allowedScopes]="['user', 'session']"> </praxis-context-variable-manager>
567
-
568
- <!-- Generated Rule Preview -->
569
- <pre>{{ generatedRule | json }}</pre>
570
- </form>
571
- `,
572
- })
573
- export class RuleBuilderFormComponent {
574
- ruleForm = this.fb.group({
575
- expression: ["", Validators.required],
576
- metadata: this.fb.group({
577
- name: ["", Validators.required],
578
- description: [""],
579
- priority: [1],
580
- }),
581
- });
582
-
583
- get generatedRule() {
584
- const expression = this.ruleForm.get("expression")?.value;
585
- if (!expression) return null;
586
-
587
- const parseResult = this.bridgeService.parseDslExpression(expression, {
588
- knownFields: this.formFieldSchemas.map((f) => f.name),
589
- });
590
-
591
- return parseResult.success ? parseResult.specification?.toJSON() : null;
592
- }
593
- }
594
- ```
595
-
596
- ## Performance Guidelines
597
-
598
- ### Optimization Tips
599
-
600
- 1. **Field Schema Management**:
601
-
602
- ```typescript
603
- // ✅ Good: Reuse field schema objects
604
- const fieldSchemas = useMemo(() => fields.map((f) => ({ name: f.name, type: f.type, label: f.label })), [fields]);
605
-
606
- // ❌ Avoid: Creating new objects on every render
607
- const fieldSchemas = fields.map((f) => ({ name: f.name, type: f.type }));
608
- ```
609
-
610
- 2. **Expression Validation**:
611
-
612
- ```typescript
613
- // ✅ Good: Debounce validation
614
- const debouncedValidation = useMemo(
615
- () => debounce(validateExpression, 300),
616
- []
617
- );
618
-
619
- // ❌ Avoid: Validating on every keystroke
620
- onChange={(expr) => validateExpression(expr)}
621
- ```
622
-
623
- ## Building
624
-
625
- To build the library, run:
626
-
627
- ```bash
628
- ng build praxis-visual-builder
629
- ```
630
-
631
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
632
-
633
- ### Publishing the Library
634
-
635
- Once the project is built, you can publish your library by following these steps:
636
-
637
- 1. Navigate to the `dist` directory:
638
-
639
- ```bash
640
- cd dist/praxis-visual-builder
641
- ```
642
-
643
- 2. Run the `npm publish` command to publish your library to the npm registry:
644
- ```bash
645
- npm publish
646
- ```
647
-
648
- ## Running unit tests
649
-
650
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
651
-
652
- ```bash
653
- ng test praxis-visual-builder
654
- ```
85
+ ## Public Surface
655
86
 
656
- ## Contributing
87
+ Main exports available from `@praxisui/visual-builder`:
657
88
 
658
- Please read our [Contributing Guide](CONTRIBUTING.md) for details on our code of conduct and the process for submitting pull requests.
89
+ - Components: `RuleEditorComponent`, `RuleCanvasComponent`, `RuleNodeComponent`, `FieldConditionEditorComponent`, `ConditionalValidatorEditorComponent`, `CollectionValidatorEditorComponent`, `MetadataEditorComponent`, `JsonViewerComponent`, `ExportDialogComponent`, `VisualRuleBuilderComponent`, `TemplateGalleryComponent`, `TemplateEditorDialogComponent`, `TemplatePreviewDialogComponent`, `RuleListComponent`, `ContextVariableManagerComponent`
90
+ - Services: `RuleBuilderService`, `ExportIntegrationService`, `WebhookIntegrationService`, `RuleTemplateService`, `RuleValidationService`, `RuleNodeRegistryService`, `ContextManagementService`, `FieldSchemaService`
91
+ - Models/Types: `FieldSchema`, `RuleBuilderConfig`, `ArrayFieldSchema`, `ContextScope`, `ContextEntry`, `ContextValue`
659
92
 
660
- ## License
93
+ ## Legacy Note
661
94
 
662
- Apache-2.0 see the `LICENSE` packaged with this library or the repository root.
95
+ The old textual `ExpressionEditorComponent` and DSL validation path were removed from the canonical library surface. Legacy DSL migration material should not be used as a baseline for new integrations.