@syncfusion/ej2-react-form-builder 0.1.0-beta

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.
Files changed (145) hide show
  1. package/Common/CheckboxGroup.d.ts +13 -0
  2. package/Common/CheckboxGroup.js +50 -0
  3. package/Common/ConditionalRuleEngine.d.ts +296 -0
  4. package/Common/ConditionalRuleEngine.js +566 -0
  5. package/Common/CustomTabComponent.d.ts +17 -0
  6. package/Common/CustomTabComponent.js +30 -0
  7. package/Common/CustomValidationEngine.d.ts +58 -0
  8. package/Common/CustomValidationEngine.js +146 -0
  9. package/Common/DataGridComponent.d.ts +20 -0
  10. package/Common/DataGridComponent.js +273 -0
  11. package/Common/FormValidator.d.ts +485 -0
  12. package/Common/FormValidator.js +575 -0
  13. package/Common/MinMaxRuleEngine.d.ts +105 -0
  14. package/Common/MinMaxRuleEngine.js +365 -0
  15. package/Common/StaticHtmlRenderer.d.ts +14 -0
  16. package/Common/StaticHtmlRenderer.js +24 -0
  17. package/Common/expressions/DependencyTracker.d.ts +30 -0
  18. package/Common/expressions/DependencyTracker.js +128 -0
  19. package/Common/expressions/ExpressionEngine.d.ts +95 -0
  20. package/Common/expressions/ExpressionEngine.js +506 -0
  21. package/Common/expressions/ExpressionParser.d.ts +41 -0
  22. package/Common/expressions/ExpressionParser.js +269 -0
  23. package/Common/expressions/useGridExpressions.d.ts +12 -0
  24. package/Common/expressions/useGridExpressions.js +71 -0
  25. package/Common/globalFormId.d.ts +26 -0
  26. package/Common/globalFormId.js +45 -0
  27. package/Common/json-converter.d.ts +444 -0
  28. package/Common/json-converter.js +1184 -0
  29. package/Common/parser.d.ts +98 -0
  30. package/Common/parser.js +753 -0
  31. package/Common/utils.d.ts +130 -0
  32. package/Common/utils.js +574 -0
  33. package/README.md +53 -0
  34. package/index.d.ts +6 -0
  35. package/index.js +6 -0
  36. package/license +13 -0
  37. package/package.json +54 -0
  38. package/src/FormBuilder.d.ts +211 -0
  39. package/src/FormBuilder.js +1299 -0
  40. package/src/components/Database/ConnectionForm.d.ts +8 -0
  41. package/src/components/Database/ConnectionForm.js +28 -0
  42. package/src/components/TopBar.d.ts +10 -0
  43. package/src/components/TopBar.js +7 -0
  44. package/src/components/icons/ComponentIcons.d.ts +44 -0
  45. package/src/components/icons/ComponentIcons.js +40 -0
  46. package/src/components/layout/FormFieldWrapper.d.ts +41 -0
  47. package/src/components/layout/FormFieldWrapper.js +112 -0
  48. package/src/components/layout/LeftPane.d.ts +23 -0
  49. package/src/components/layout/LeftPane.js +310 -0
  50. package/src/components/layout/ListBoxPane.d.ts +24 -0
  51. package/src/components/layout/ListBoxPane.js +94 -0
  52. package/src/components/layout/MainSection.d.ts +39 -0
  53. package/src/components/layout/MainSection.js +1766 -0
  54. package/src/components/layout/Preview.d.ts +12 -0
  55. package/src/components/layout/Preview.js +337 -0
  56. package/src/components/layout/RightPane.d.ts +22 -0
  57. package/src/components/layout/RightPane.js +370 -0
  58. package/src/components/theme/ThemeSwitcher.d.ts +14 -0
  59. package/src/components/theme/ThemeSwitcher.js +24 -0
  60. package/src/components/theme/theme-constant.d.ts +37 -0
  61. package/src/components/theme/theme-constant.js +23 -0
  62. package/src/config/component-properties.json +4929 -0
  63. package/src/config/form-setting.json +56 -0
  64. package/src/config/locale.d.ts +449 -0
  65. package/src/config/locale.js +449 -0
  66. package/src/context/FormSchemaContext.d.ts +32 -0
  67. package/src/context/FormSchemaContext.js +155 -0
  68. package/src/context/LocalizationContext.d.ts +27 -0
  69. package/src/context/LocalizationContext.js +43 -0
  70. package/src/context/ThemeContext.d.ts +23 -0
  71. package/src/context/ThemeContext.js +161 -0
  72. package/src/context/contexts.d.ts +6 -0
  73. package/src/context/contexts.js +6 -0
  74. package/src/context/defaultFormSchema.d.ts +6 -0
  75. package/src/context/defaultFormSchema.js +20 -0
  76. package/src/defaultPalette.d.ts +9 -0
  77. package/src/defaultPalette.js +45 -0
  78. package/src/defaultTemplate.d.ts +3 -0
  79. package/src/defaultTemplate.js +185 -0
  80. package/src/import/schemaImportUtils.d.ts +24 -0
  81. package/src/import/schemaImportUtils.js +121 -0
  82. package/src/index.d.ts +16 -0
  83. package/src/index.js +6 -0
  84. package/src/types/ActionButtons.d.ts +44 -0
  85. package/src/types/ActionButtons.js +27 -0
  86. package/src/types/ComponentProperties.d.ts +33 -0
  87. package/src/types/ComponentProperties.js +2 -0
  88. package/src/types/FormSchema.d.ts +585 -0
  89. package/src/types/FormSchema.js +147 -0
  90. package/src/types/connection.d.ts +19 -0
  91. package/src/types/connection.js +9 -0
  92. package/src/utils/AttributesEditor.d.ts +13 -0
  93. package/src/utils/AttributesEditor.js +117 -0
  94. package/src/utils/ButtonGroupEditor.d.ts +15 -0
  95. package/src/utils/ButtonGroupEditor.js +141 -0
  96. package/src/utils/ChoiceBasedFieldEditor.d.ts +16 -0
  97. package/src/utils/ChoiceBasedFieldEditor.js +87 -0
  98. package/src/utils/ClipboardHelper.d.ts +26 -0
  99. package/src/utils/ClipboardHelper.js +83 -0
  100. package/src/utils/ColumnBuilder.d.ts +11 -0
  101. package/src/utils/ColumnBuilder.js +153 -0
  102. package/src/utils/ColumnSpanEditor.d.ts +8 -0
  103. package/src/utils/ColumnSpanEditor.js +98 -0
  104. package/src/utils/ConditionDataEditor.d.ts +29 -0
  105. package/src/utils/ConditionDataEditor.js +177 -0
  106. package/src/utils/ConditionalEditorUtils.d.ts +27 -0
  107. package/src/utils/ConditionalEditorUtils.js +310 -0
  108. package/src/utils/ConditionsEditor.d.ts +20 -0
  109. package/src/utils/ConditionsEditor.js +571 -0
  110. package/src/utils/CustomAccordion.d.ts +13 -0
  111. package/src/utils/CustomAccordion.js +38 -0
  112. package/src/utils/CustomValidationEditor.d.ts +11 -0
  113. package/src/utils/CustomValidationEditor.js +208 -0
  114. package/src/utils/DataSourceCollector.d.ts +3 -0
  115. package/src/utils/DataSourceCollector.js +252 -0
  116. package/src/utils/ExpressionEditor.d.ts +13 -0
  117. package/src/utils/ExpressionEditor.js +99 -0
  118. package/src/utils/FormSettingsPanel.d.ts +9 -0
  119. package/src/utils/FormSettingsPanel.js +60 -0
  120. package/src/utils/LookupConfig.d.ts +11 -0
  121. package/src/utils/LookupConfig.js +159 -0
  122. package/src/utils/MinMaxValueEditor.d.ts +25 -0
  123. package/src/utils/MinMaxValueEditor.js +188 -0
  124. package/src/utils/OptionsEditor.d.ts +8 -0
  125. package/src/utils/OptionsEditor.js +79 -0
  126. package/src/utils/PropertyRenderer.d.ts +22 -0
  127. package/src/utils/PropertyRenderer.js +210 -0
  128. package/src/utils/ValueRenderer.d.ts +2 -0
  129. package/src/utils/ValueRenderer.js +91 -0
  130. package/src/utils/ej2DbAdaptor.d.ts +6 -0
  131. package/src/utils/ej2DbAdaptor.js +38 -0
  132. package/src/utils/mappingHeuristics.d.ts +10 -0
  133. package/src/utils/mappingHeuristics.js +33 -0
  134. package/src/utils/renderComponent.d.ts +18 -0
  135. package/src/utils/renderComponent.js +300 -0
  136. package/styles/tailwind3.css +67 -0
  137. package/styles/themes/action-buttons.css +110 -0
  138. package/styles/themes/datagrid.css +380 -0
  139. package/styles/themes/drag-reorder.css +248 -0
  140. package/styles/themes/import-dialog.css +292 -0
  141. package/styles/themes/index.css +658 -0
  142. package/styles/themes/left-pane.css +253 -0
  143. package/styles/themes/options-editor.css +63 -0
  144. package/styles/themes/preview.css +702 -0
  145. package/styles/themes/property-dialog.css +371 -0
@@ -0,0 +1,365 @@
1
+ /**
2
+ * Extract min/max constraints from minMaxRange array
3
+ * @private
4
+ */
5
+ export class MinMaxRuleEngine {
6
+ /**
7
+ * Extract min/max constraints from minMaxRange array
8
+ * Handles three cases:
9
+ * 1. Direct values (enabled: false)
10
+ * 2. Single field reference (enabled: true, 1 range)
11
+ * 3. Multiple ranges (enabled: true, 2+ ranges) - takes UTMOST
12
+ * @private
13
+ * @param minMaxRange - Array of range configurations
14
+ * @param formValues - Current form state values (id → value)
15
+ * @param componentType - 'number' | 'date' | 'dateRange'
16
+ * @returns Constraints object with appropriate keys
17
+ */
18
+ static getConstraints(minMaxRange, formValues, componentType = 'number') {
19
+ // No ranges provided
20
+ if (!minMaxRange || minMaxRange.length === 0) {
21
+ return this.getEmptyConstraints(componentType);
22
+ }
23
+ // First, check for CASE 1: Direct values (enabled: false)
24
+ const directValueRange = minMaxRange.find(range => range.enabled === false);
25
+ if (directValueRange) {
26
+ // Use direct values without field lookup
27
+ const minValue = this.resolveValue(directValueRange.minValue, {});
28
+ const maxValue = this.resolveValue(directValueRange.maxValue, {});
29
+ if (componentType === 'date' || componentType === 'dateRange') {
30
+ return {
31
+ minValue: undefined,
32
+ maxValue: undefined,
33
+ minDate: minValue,
34
+ maxDate: maxValue,
35
+ minTime: undefined,
36
+ maxTime: undefined
37
+ };
38
+ }
39
+ if (componentType === 'time') {
40
+ return {
41
+ minValue: undefined,
42
+ maxValue: undefined,
43
+ minDate: undefined,
44
+ maxDate: undefined,
45
+ minTime: minValue,
46
+ maxTime: maxValue
47
+ };
48
+ }
49
+ return {
50
+ minValue,
51
+ maxValue,
52
+ minDate: undefined,
53
+ maxDate: undefined,
54
+ minTime: undefined,
55
+ maxTime: undefined
56
+ };
57
+ }
58
+ // CASE 2 & 3: Field references (enabled: true)
59
+ const enabledRanges = minMaxRange.filter(range => range.enabled === true);
60
+ // No enabled ranges - return empty
61
+ if (enabledRanges.length === 0) {
62
+ return this.getEmptyConstraints(componentType);
63
+ }
64
+ // Extract resolved values from each enabled range
65
+ const resolvedRanges = enabledRanges
66
+ .map(range => ({
67
+ min: this.resolveValue(range.minValue, formValues),
68
+ max: this.resolveValue(range.maxValue, formValues)
69
+ }))
70
+ .filter(range => range.min !== undefined || range.max !== undefined);
71
+ // No valid ranges
72
+ if (resolvedRanges.length === 0) {
73
+ return this.getEmptyConstraints(componentType);
74
+ }
75
+ // Combine multiple ranges using UTMOST (most permissive)
76
+ const finalMin = this.getPermissiveMin(resolvedRanges, componentType);
77
+ const finalMax = this.getPermissiveMax(resolvedRanges, componentType);
78
+ // Return appropriate keys based on component type
79
+ if (componentType === 'date' || componentType === 'dateRange') {
80
+ return {
81
+ minValue: undefined,
82
+ maxValue: undefined,
83
+ minDate: finalMin,
84
+ maxDate: finalMax,
85
+ minTime: undefined,
86
+ maxTime: undefined
87
+ };
88
+ }
89
+ if (componentType === 'time') {
90
+ return {
91
+ minValue: undefined,
92
+ maxValue: undefined,
93
+ minDate: undefined,
94
+ maxDate: undefined,
95
+ minTime: finalMin,
96
+ maxTime: finalMax
97
+ };
98
+ }
99
+ return {
100
+ minValue: finalMin,
101
+ maxValue: finalMax,
102
+ minDate: undefined,
103
+ maxDate: undefined,
104
+ minTime: undefined,
105
+ maxTime: undefined
106
+ };
107
+ }
108
+ /**
109
+ * Resolve a single value - either direct or field reference
110
+ *
111
+ * @param value - Direct value or field reference object
112
+ * @param formValues - Form state values for field lookup
113
+ * @returns Resolved value or undefined
114
+ */
115
+ static resolveValue(value, formValues) {
116
+ // Direct value (number or string)
117
+ if (typeof value === 'number') {
118
+ return value;
119
+ }
120
+ if (typeof value === 'string') {
121
+ return value;
122
+ }
123
+ // Field reference object
124
+ if (value && typeof value === 'object' && value.field) {
125
+ const fieldId = value.field;
126
+ const fieldValue = formValues[fieldId];
127
+ return fieldValue !== undefined ? fieldValue : undefined;
128
+ }
129
+ return undefined;
130
+ }
131
+ /**
132
+ * Get the most permissive (widest) minimum value
133
+ * For numbers: MIN of all mins
134
+ * For dates: earliest date (MIN string comparison on ISO format)
135
+ * For times: earliest time (MIN string comparison on 24-hour format)
136
+ *
137
+ * @param ranges - Array of {min, max} values
138
+ * @param componentType - Type of component
139
+ * @returns Most permissive minimum value
140
+ */
141
+ static getPermissiveMin(ranges, componentType) {
142
+ if (ranges.length === 0)
143
+ return undefined;
144
+ if (componentType === 'date' || componentType === 'dateRange') {
145
+ // For dates, find earliest (minimum) date
146
+ return ranges.reduce((earliest, current) => {
147
+ if (!current.min)
148
+ return earliest;
149
+ if (!earliest)
150
+ return current.min;
151
+ const earliestIso = this.convertToISO(earliest);
152
+ const currentIso = this.convertToISO(current.min);
153
+ return currentIso < earliestIso ? current.min : earliest;
154
+ }, undefined);
155
+ }
156
+ if (componentType === 'time') {
157
+ // For times, find earliest (minimum) time
158
+ return ranges.reduce((earliest, current) => {
159
+ if (!current.min)
160
+ return earliest;
161
+ if (!earliest)
162
+ return current.min;
163
+ const earliest24 = this.convertTimeTo24Hour(earliest);
164
+ const current24 = this.convertTimeTo24Hour(current.min);
165
+ return current24 < earliest24 ? current.min : earliest;
166
+ }, undefined);
167
+ }
168
+ // For numbers, find minimum (skip undefined values)
169
+ const minValues = ranges.map(r => r.min).filter(v => v !== undefined);
170
+ return minValues.length > 0 ? Math.min(...minValues) : undefined;
171
+ }
172
+ /**
173
+ * Get the most permissive (widest) maximum value
174
+ * For numbers: MAX of all maxs
175
+ * For dates: latest date (MAX string comparison on ISO format)
176
+ * For times: latest time (MAX string comparison on 24-hour format)
177
+ *
178
+ * @param ranges - Array of {min, max} values
179
+ * @param componentType - Type of component
180
+ * @returns Most permissive maximum value
181
+ */
182
+ static getPermissiveMax(ranges, componentType) {
183
+ if (ranges.length === 0)
184
+ return undefined;
185
+ if (componentType === 'date' || componentType === 'dateRange') {
186
+ // For dates, find latest (maximum) date
187
+ return ranges.reduce((latest, current) => {
188
+ if (!current.max)
189
+ return latest;
190
+ if (!latest)
191
+ return current.max;
192
+ const latestIso = this.convertToISO(latest);
193
+ const currentIso = this.convertToISO(current.max);
194
+ return currentIso > latestIso ? current.max : latest;
195
+ }, undefined);
196
+ }
197
+ if (componentType === 'time') {
198
+ // For times, find latest (maximum) time
199
+ return ranges.reduce((latest, current) => {
200
+ if (!current.max)
201
+ return latest;
202
+ if (!latest)
203
+ return current.max;
204
+ const latest24 = this.convertTimeTo24Hour(latest);
205
+ const current24 = this.convertTimeTo24Hour(current.max);
206
+ return current24 > latest24 ? current.max : latest;
207
+ }, undefined);
208
+ }
209
+ // For numbers, find maximum (skip undefined values)
210
+ const maxValues = ranges.map(r => r.max).filter(v => v !== undefined);
211
+ return maxValues.length > 0 ? Math.max(...maxValues) : undefined;
212
+ }
213
+ /**
214
+ * Convert MM/DD/YYYY date format to ISO (YYYY-MM-DD) for comparison
215
+ * Handles both formats gracefully
216
+ *
217
+ * @param dateString - Date in MM/DD/YYYY or ISO format
218
+ * @returns ISO date string (YYYY-MM-DD)
219
+ */
220
+ static convertToISO(dateString) {
221
+ if (!dateString)
222
+ return '';
223
+ // Already ISO format (YYYY-MM-DD)
224
+ if (dateString.includes('-') && dateString.length === 10) {
225
+ return dateString;
226
+ }
227
+ // MM/DD/YYYY format
228
+ if (dateString.includes('/')) {
229
+ const [month, day, year] = dateString.split('/');
230
+ if (!month || !day || !year)
231
+ return dateString;
232
+ return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')}`;
233
+ }
234
+ return dateString;
235
+ }
236
+ /**
237
+ * Convert 12-hour time format to 24-hour format for comparison
238
+ * Handles both 12-hour (hh:mm a) and 24-hour (HH:mm) formats
239
+ *
240
+ * @param timeString - Time in 12-hour (hh:mm a/p) or 24-hour (HH:mm) format
241
+ * @returns 24-hour format time string (HH:mm)
242
+ */
243
+ static convertTimeTo24Hour(timeString) {
244
+ if (!timeString)
245
+ return '';
246
+ // Check if it contains AM/PM (12-hour format)
247
+ const isPM = /PM/i.test(timeString);
248
+ const isAM = /AM/i.test(timeString);
249
+ if (isAM || isPM) {
250
+ // Extract time part (before AM/PM)
251
+ const timePart = timeString.replace(/\s*(AM|PM)/i, '').trim();
252
+ const [hours, minutes] = timePart.split(':');
253
+ if (!hours || !minutes)
254
+ return timeString;
255
+ let hour = parseInt(hours, 10);
256
+ if (isPM && hour !== 12) {
257
+ hour += 12; // Convert PM hours (except 12) to 24-hour
258
+ }
259
+ else if (isAM && hour === 12) {
260
+ hour = 0; // Convert 12 AM to 00:00
261
+ }
262
+ return `${String(hour).padStart(2, '0')}:${minutes}`;
263
+ }
264
+ // Already in 24-hour format or unknown format
265
+ return timeString;
266
+ }
267
+ /**
268
+ * Get empty constraints object based on component type
269
+ */
270
+ static getEmptyConstraints(componentType) {
271
+ if (componentType === 'date' || componentType === 'dateRange') {
272
+ return {
273
+ minValue: undefined,
274
+ maxValue: undefined,
275
+ minDate: undefined,
276
+ maxDate: undefined,
277
+ minTime: undefined,
278
+ maxTime: undefined
279
+ };
280
+ }
281
+ if (componentType === 'time') {
282
+ return {
283
+ minValue: undefined,
284
+ maxValue: undefined,
285
+ minDate: undefined,
286
+ maxDate: undefined,
287
+ minTime: undefined,
288
+ maxTime: undefined
289
+ };
290
+ }
291
+ return {
292
+ minValue: undefined,
293
+ maxValue: undefined,
294
+ minDate: undefined,
295
+ maxDate: undefined,
296
+ minTime: undefined,
297
+ maxTime: undefined
298
+ };
299
+ }
300
+ /**
301
+ * Validate if a value passes all min/max constraints (optional)
302
+ * Can be used for client-side validation before submission
303
+ *
304
+ * @param value - Value to validate
305
+ * @param constraints - Constraints to check against
306
+ * @param componentType - Type of component
307
+ * @returns Validation result with error message if invalid
308
+ */
309
+ static validateValue(value, constraints, componentType) {
310
+ if (value === undefined || value === null || value === '') {
311
+ return { isValid: true }; // Empty values pass (required handled separately)
312
+ }
313
+ if (componentType === 'date' || componentType === 'dateRange') {
314
+ if (constraints.minDate) {
315
+ const minIso = this.convertToISO(constraints.minDate);
316
+ const valueIso = this.convertToISO(value);
317
+ if (valueIso < minIso) {
318
+ return { isValid: false, error: `Date must be on or after ${constraints.minDate}` };
319
+ }
320
+ }
321
+ if (constraints.maxDate) {
322
+ const maxIso = this.convertToISO(constraints.maxDate);
323
+ const valueIso = this.convertToISO(value);
324
+ if (valueIso > maxIso) {
325
+ return { isValid: false, error: `Date must be on or before ${constraints.maxDate}` };
326
+ }
327
+ }
328
+ }
329
+ else if (componentType === 'time') {
330
+ if (constraints.minTime) {
331
+ if (value < constraints.minTime) {
332
+ return { isValid: false, error: `Time must be on or after ${constraints.minTime}` };
333
+ }
334
+ }
335
+ if (constraints.maxTime) {
336
+ if (value > constraints.maxTime) {
337
+ return { isValid: false, error: `Time must be on or before ${constraints.maxTime}` };
338
+ }
339
+ }
340
+ }
341
+ else {
342
+ // Number type
343
+ const numValue = Number(value);
344
+ if (constraints.minValue !== undefined) {
345
+ const minNum = Number(constraints.minValue);
346
+ if (numValue < minNum) {
347
+ return {
348
+ isValid: false,
349
+ error: `Value must be at least ${constraints.minValue}`
350
+ };
351
+ }
352
+ }
353
+ if (constraints.maxValue !== undefined) {
354
+ const maxNum = Number(constraints.maxValue);
355
+ if (numValue > maxNum) {
356
+ return {
357
+ isValid: false,
358
+ error: `Value must be at most ${constraints.maxValue}`
359
+ };
360
+ }
361
+ }
362
+ }
363
+ return { isValid: true };
364
+ }
365
+ }
@@ -0,0 +1,14 @@
1
+ import React from 'react';
2
+ /**
3
+ * @private
4
+ */
5
+ export interface StaticHtmlRendererProps {
6
+ content: string;
7
+ id?: string;
8
+ enableHtmlSanitizer?: boolean;
9
+ }
10
+ /**
11
+ * @private
12
+ */
13
+ export declare const StaticHtmlRenderer: React.FC<StaticHtmlRendererProps>;
14
+ export default StaticHtmlRenderer;
@@ -0,0 +1,24 @@
1
+ import React from 'react';
2
+ import { SanitizeHtmlHelper } from '@syncfusion/ej2-base';
3
+ /**
4
+ * @private
5
+ */
6
+ export const StaticHtmlRenderer = ({ content, id, enableHtmlSanitizer }) => {
7
+ const sanitizedHtml = React.useMemo(() => {
8
+ try {
9
+ if (enableHtmlSanitizer) {
10
+ return SanitizeHtmlHelper.sanitize(content);
11
+ }
12
+ return content;
13
+ }
14
+ catch (error) {
15
+ console.error('HTML sanitization error:', error);
16
+ return '';
17
+ }
18
+ }, [content]);
19
+ return React.createElement('div', {
20
+ id,
21
+ dangerouslySetInnerHTML: { __html: sanitizedHtml }
22
+ });
23
+ };
24
+ export default StaticHtmlRenderer;
@@ -0,0 +1,30 @@
1
+ /**
2
+ * DependencyTracker - Extract and track field dependencies
3
+ *
4
+ * Analyzes expressions to find field references and build dependency graphs
5
+ * Detects circular dependencies and calculates evaluation order
6
+ * @private
7
+ * @spec Expression_Engine.md
8
+ */
9
+ export declare class DependencyTracker {
10
+ private escapedBracePattern;
11
+ /**
12
+ * Extract field names referenced in expression
13
+ */
14
+ extractFieldReferences(expression: string): string[];
15
+ /**
16
+ * Build dependency graph from multiple expressions
17
+ */
18
+ buildGraph(expressions: Record<string, string>): Record<string, string[]>;
19
+ /**
20
+ * Detect circular dependencies using DFS
21
+ */
22
+ detectCircularDependencies(graph: Record<string, string[]>): string[][];
23
+ /**
24
+ * Get topological sort order for evaluation
25
+ */
26
+ getTopologicalOrder(graph: Record<string, string[]>): string[];
27
+ private isFunctionCall;
28
+ private isOperator;
29
+ private isKeyword;
30
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * DependencyTracker - Extract and track field dependencies
3
+ *
4
+ * Analyzes expressions to find field references and build dependency graphs
5
+ * Detects circular dependencies and calculates evaluation order
6
+ * @private
7
+ * @spec Expression_Engine.md
8
+ */
9
+ export class DependencyTracker {
10
+ escapedBracePattern = /\\{.*?\\}/g;
11
+ /**
12
+ * Extract field names referenced in expression
13
+ */
14
+ extractFieldReferences(expression) {
15
+ if (!expression)
16
+ return [];
17
+ const refs = new Set();
18
+ let match;
19
+ // Remove escaped braces first
20
+ const cleanExpression = expression.replace(this.escapedBracePattern, '');
21
+ // Extract field references from {field} syntax
22
+ const fieldRefPattern = /\{([^{}]+)\}/g;
23
+ while ((match = fieldRefPattern.exec(cleanExpression)) !== null) {
24
+ const ref = match[1].trim();
25
+ // Skip if it's a function call or operator
26
+ if (!this.isFunctionCall(ref) && !this.isOperator(ref)) {
27
+ // Extract field name from nested paths
28
+ const fieldName = ref.split(/[.\[\]]/)[0].trim();
29
+ if (fieldName && !this.isKeyword(fieldName)) {
30
+ refs.add(fieldName);
31
+ }
32
+ }
33
+ }
34
+ // Extract identifiers from ${...} math expressions
35
+ const mathExprPattern = /\$\{([^}]+)\}/g;
36
+ while ((match = mathExprPattern.exec(cleanExpression)) !== null) {
37
+ const mathExpr = match[1];
38
+ // Extract all identifiers from the math expression
39
+ // Match word boundaries followed by identifier characters
40
+ // But skip if followed by ( or . (those are function calls or property access)
41
+ const identPattern = /\b([a-zA-Z_][a-zA-Z0-9_]*)\b(?!\s*[(\.])/g;
42
+ let idMatch;
43
+ while ((idMatch = identPattern.exec(mathExpr)) !== null) {
44
+ const identifier = idMatch[1];
45
+ // Skip keywords and constants
46
+ if (!this.isKeyword(identifier) && identifier !== 'Math') {
47
+ refs.add(identifier);
48
+ }
49
+ }
50
+ }
51
+ return Array.from(refs);
52
+ }
53
+ /**
54
+ * Build dependency graph from multiple expressions
55
+ */
56
+ buildGraph(expressions) {
57
+ const graph = {};
58
+ for (const [fieldName, expression] of Object.entries(expressions)) {
59
+ const deps = this.extractFieldReferences(expression);
60
+ graph[fieldName] = deps;
61
+ }
62
+ return graph;
63
+ }
64
+ /**
65
+ * Detect circular dependencies using DFS
66
+ */
67
+ detectCircularDependencies(graph) {
68
+ const visited = new Set();
69
+ const recursionStack = new Set();
70
+ const cycles = [];
71
+ const visit = (node, path) => {
72
+ visited.add(node);
73
+ recursionStack.add(node);
74
+ path.push(node);
75
+ const deps = graph[node] || [];
76
+ for (const dep of deps) {
77
+ if (!visited.has(dep)) {
78
+ visit(dep, [...path]);
79
+ }
80
+ else if (recursionStack.has(dep)) {
81
+ const cycleStart = path.indexOf(dep);
82
+ cycles.push([...path.slice(cycleStart), dep]);
83
+ }
84
+ }
85
+ recursionStack.delete(node);
86
+ };
87
+ for (const node of Object.keys(graph)) {
88
+ if (!visited.has(node)) {
89
+ visit(node, []);
90
+ }
91
+ }
92
+ return cycles;
93
+ }
94
+ /**
95
+ * Get topological sort order for evaluation
96
+ */
97
+ getTopologicalOrder(graph) {
98
+ const visited = new Set();
99
+ const order = [];
100
+ const visit = (node) => {
101
+ if (visited.has(node))
102
+ return;
103
+ visited.add(node);
104
+ const deps = graph[node] || [];
105
+ for (const dep of deps) {
106
+ if (graph[dep]) {
107
+ visit(dep);
108
+ }
109
+ }
110
+ order.push(node);
111
+ };
112
+ for (const node of Object.keys(graph)) {
113
+ visit(node);
114
+ }
115
+ return order;
116
+ }
117
+ // Private helpers
118
+ isFunctionCall(ref) {
119
+ return /^\w+\s*\(/.test(ref);
120
+ }
121
+ isOperator(ref) {
122
+ return /^[+\-*/()=!<>&|]/.test(ref);
123
+ }
124
+ isKeyword(name) {
125
+ const keywords = ['PI', 'TRUE', 'FALSE', 'NULL', 'UNDEFINED', 'and', 'or', 'in'];
126
+ return keywords.includes(name.toUpperCase());
127
+ }
128
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * @private
3
+ */
4
+ export type FormValueType = string | number | boolean | Date | null | undefined | any[];
5
+ /**
6
+ * @private
7
+ */
8
+ export interface EvaluationOptions {
9
+ timeout?: number;
10
+ maxDepth?: number;
11
+ locale?: string;
12
+ currencyCode?: string;
13
+ throwOnError?: boolean;
14
+ }
15
+ /**
16
+ * @private
17
+ */
18
+ export type DependencyGraph = Record<string, string[]>;
19
+ /**
20
+ * ExpressionEngine - Core evaluation engine
21
+ *
22
+ * Safely evaluates user-defined expressions against form state
23
+ * Supports field references, math operations, functions, and conditionals
24
+ * @private
25
+ */
26
+ export declare class ExpressionEngine {
27
+ private parser;
28
+ private dependencyTracker;
29
+ private evaluationCache;
30
+ private compiledFunctions;
31
+ constructor();
32
+ /**
33
+ * Evaluate expression against form values
34
+ */
35
+ evaluate(expression: string, formValues: Record<string, FormValueType>, options?: EvaluationOptions, isCustomValidation?: boolean): any;
36
+ /**
37
+ * Extract field names referenced in expression
38
+ */
39
+ extractDependencies(expression: string): string[];
40
+ /**
41
+ * Build dependency graph for all expressions
42
+ */
43
+ buildDependencyGraph(components: any[]): DependencyGraph;
44
+ /**
45
+ * Detect circular dependencies
46
+ */
47
+ detectCircularDependencies(graph: DependencyGraph): string[][];
48
+ /**
49
+ * Get topological order for evaluation
50
+ */
51
+ getTopologicalOrder(graph: DependencyGraph): string[];
52
+ /**
53
+ * Clear internal caches
54
+ */
55
+ clearCache(): void;
56
+ private getCacheKey;
57
+ private buildContext;
58
+ }
59
+ /**
60
+ * ExpressionValidator - Validates expressions before runtime
61
+ *
62
+ * Checks syntax, validates field references, and detects circular dependencies
63
+ * @private
64
+ * @spec Expression_Engine.md
65
+ */
66
+ export declare class ExpressionValidator {
67
+ private supportedFunctions;
68
+ private keywords;
69
+ /**
70
+ * Validate expression against available fields
71
+ */
72
+ validate(expression: string, availableFields: string[] | any[]): ValidationError[];
73
+ /**
74
+ * Validate field operations - check for invalid operators on string/date types
75
+ */
76
+ private validateFieldOperations;
77
+ private validateSyntax;
78
+ /**
79
+ * @private
80
+ */
81
+ validateFieldReferences(expression: string, availableFields: string[] | any[]): ValidationError[];
82
+ private validateFunctionCalls;
83
+ private isFunctionCall;
84
+ private isMathExpression;
85
+ }
86
+ /**
87
+ * @private
88
+ */
89
+ export interface ValidationError {
90
+ type: 'SyntaxError' | 'UndefinedField' | 'InvalidFunction' | 'CircularDependency' | 'InvalidOperation';
91
+ message: string;
92
+ field?: string;
93
+ line?: number;
94
+ column?: number;
95
+ }