@praxisui/visual-builder 4.0.0-beta.0 → 6.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 +42 -609
- package/fesm2022/praxisui-visual-builder.mjs +15811 -21744
- package/index.d.ts +85 -840
- package/package.json +5 -8
package/README.md
CHANGED
|
@@ -1,34 +1,26 @@
|
|
|
1
1
|
# @praxisui/visual-builder
|
|
2
2
|
|
|
3
|
-
Visual rule
|
|
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
|
|
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
|
|
14
|
-
- Validate and
|
|
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
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
-
|
|
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
|
|
36
|
+
import { ContextVariableManagerComponent } from '@praxisui/visual-builder';
|
|
71
37
|
|
|
72
38
|
@Component({
|
|
73
|
-
template: `
|
|
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
|
|
77
|
-
allowedScopes = [
|
|
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
|
|
52
|
+
### Visual Rule Editor
|
|
86
53
|
|
|
87
54
|
```typescript
|
|
88
|
-
import { RuleEditorComponent } from
|
|
55
|
+
import { RuleEditorComponent } from '@praxisui/visual-builder';
|
|
89
56
|
|
|
90
57
|
@Component({
|
|
91
|
-
template: `
|
|
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:
|
|
99
|
-
console.log(
|
|
71
|
+
onRulesChanged(rules: unknown) {
|
|
72
|
+
console.log('Updated rules:', rules);
|
|
100
73
|
}
|
|
101
74
|
}
|
|
102
75
|
```
|
|
103
76
|
|
|
104
|
-
|
|
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
|
-
|
|
79
|
+
Use the canonical public services:
|
|
280
80
|
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
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
|
-
|
|
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
|
-
|
|
87
|
+
Main exports available from `@praxisui/visual-builder`:
|
|
657
88
|
|
|
658
|
-
|
|
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
|
-
##
|
|
93
|
+
## Legacy Note
|
|
661
94
|
|
|
662
|
-
|
|
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.
|