@memberjunction/react-test-harness 2.124.0 → 2.125.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.
Files changed (40) hide show
  1. package/dist/lib/component-linter.d.ts.map +1 -1
  2. package/dist/lib/component-linter.js +433 -2
  3. package/dist/lib/component-linter.js.map +1 -1
  4. package/dist/lib/constraint-validators/base-constraint-validator.d.ts +259 -0
  5. package/dist/lib/constraint-validators/base-constraint-validator.d.ts.map +1 -0
  6. package/dist/lib/constraint-validators/base-constraint-validator.js +304 -0
  7. package/dist/lib/constraint-validators/base-constraint-validator.js.map +1 -0
  8. package/dist/lib/constraint-validators/index.d.ts +21 -0
  9. package/dist/lib/constraint-validators/index.d.ts.map +1 -0
  10. package/dist/lib/constraint-validators/index.js +37 -0
  11. package/dist/lib/constraint-validators/index.js.map +1 -0
  12. package/dist/lib/constraint-validators/required-when-validator.d.ts +43 -0
  13. package/dist/lib/constraint-validators/required-when-validator.d.ts.map +1 -0
  14. package/dist/lib/constraint-validators/required-when-validator.js +97 -0
  15. package/dist/lib/constraint-validators/required-when-validator.js.map +1 -0
  16. package/dist/lib/constraint-validators/sql-where-clause-validator.d.ts +116 -0
  17. package/dist/lib/constraint-validators/sql-where-clause-validator.d.ts.map +1 -0
  18. package/dist/lib/constraint-validators/sql-where-clause-validator.js +381 -0
  19. package/dist/lib/constraint-validators/sql-where-clause-validator.js.map +1 -0
  20. package/dist/lib/constraint-validators/subset-of-entity-fields-validator.d.ts +60 -0
  21. package/dist/lib/constraint-validators/subset-of-entity-fields-validator.d.ts.map +1 -0
  22. package/dist/lib/constraint-validators/subset-of-entity-fields-validator.js +198 -0
  23. package/dist/lib/constraint-validators/subset-of-entity-fields-validator.js.map +1 -0
  24. package/dist/lib/constraint-validators/validation-context.d.ts +326 -0
  25. package/dist/lib/constraint-validators/validation-context.d.ts.map +1 -0
  26. package/dist/lib/constraint-validators/validation-context.js +14 -0
  27. package/dist/lib/constraint-validators/validation-context.js.map +1 -0
  28. package/dist/lib/prop-value-extractor.d.ts +147 -0
  29. package/dist/lib/prop-value-extractor.d.ts.map +1 -0
  30. package/dist/lib/prop-value-extractor.js +499 -0
  31. package/dist/lib/prop-value-extractor.js.map +1 -0
  32. package/dist/lib/type-context.d.ts +2 -0
  33. package/dist/lib/type-context.d.ts.map +1 -1
  34. package/dist/lib/type-context.js +22 -9
  35. package/dist/lib/type-context.js.map +1 -1
  36. package/dist/lib/type-inference-engine.d.ts +20 -0
  37. package/dist/lib/type-inference-engine.d.ts.map +1 -1
  38. package/dist/lib/type-inference-engine.js +253 -20
  39. package/dist/lib/type-inference-engine.js.map +1 -1
  40. package/package.json +13 -9
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Base Constraint Validator
3
+ *
4
+ * Abstract base class for all constraint validators.
5
+ * Validators implement business logic for validating prop values at lint-time.
6
+ *
7
+ * Key Concepts:
8
+ * - **Validators = Code**: Implemented as TypeScript classes
9
+ * - **Constraints = Metadata**: Declared in component spec JSON
10
+ * - **Separation**: Validator logic is separate from constraint definitions
11
+ *
12
+ * Example:
13
+ * ```typescript
14
+ * // Validator implementation (code)
15
+ * class SubsetOfEntityFieldsValidator extends BaseConstraintValidator {
16
+ * async validate(context, constraint) {
17
+ * // Implementation...
18
+ * }
19
+ * }
20
+ *
21
+ * // Constraint definition (metadata in spec JSON)
22
+ * {
23
+ * "type": "subset-of-entity-fields",
24
+ * "dependsOn": "entityName"
25
+ * }
26
+ * ```
27
+ */
28
+ import { PropertyConstraint, ConstraintViolation } from '@memberjunction/interactive-component-types';
29
+ import { ValidationContext } from './validation-context';
30
+ import { DynamicValue } from '../prop-value-extractor';
31
+ /**
32
+ * Abstract base class for constraint validators
33
+ *
34
+ * All constraint validators must extend this class and implement the validate() method.
35
+ */
36
+ export declare abstract class BaseConstraintValidator {
37
+ /**
38
+ * Validate a prop value against a constraint
39
+ *
40
+ * This is the main method that subclasses must implement.
41
+ * It receives the validation context and constraint definition,
42
+ * and returns an array of violations (empty if valid).
43
+ *
44
+ * Note: Changed from async to sync since validators perform no actual I/O.
45
+ * This allows seamless integration with the synchronous linter architecture.
46
+ *
47
+ * @param context - Validation context with prop value, component spec, entities, etc.
48
+ * @param constraint - The constraint definition from the component spec
49
+ * @returns Array of violations (empty if valid)
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * class MyValidator extends BaseConstraintValidator {
54
+ * validate(context: ValidationContext, constraint: PropertyConstraint): ConstraintViolation[] {
55
+ * const violations: ConstraintViolation[] = [];
56
+ *
57
+ * // Check if value can be validated statically
58
+ * if (PropValueExtractor.isDynamicValue(context.propertyValue)) {
59
+ * return violations; // Skip - can't validate dynamic values
60
+ * }
61
+ *
62
+ * // Perform validation logic
63
+ * // ...
64
+ *
65
+ * return violations;
66
+ * }
67
+ * }
68
+ * ```
69
+ */
70
+ abstract validate(context: ValidationContext, constraint: PropertyConstraint): ConstraintViolation[];
71
+ /**
72
+ * Get the name of this validator
73
+ *
74
+ * Used for debugging and error messages.
75
+ * Defaults to the class name, but can be overridden.
76
+ *
77
+ * @returns Validator name
78
+ */
79
+ getName(): string;
80
+ /**
81
+ * Get a description of what this validator checks
82
+ *
83
+ * Used for documentation and error messages.
84
+ * Should be a brief, user-friendly description.
85
+ *
86
+ * @returns Validator description
87
+ *
88
+ * @example
89
+ * ```typescript
90
+ * getDescription() {
91
+ * return "Validates that array elements are valid field names for the specified entity";
92
+ * }
93
+ * ```
94
+ */
95
+ abstract getDescription(): string;
96
+ /**
97
+ * Check if a value is dynamic (can't be validated statically)
98
+ *
99
+ * Dynamic values include identifiers (variables) and expressions (function calls, etc.)
100
+ * that can't be resolved at lint-time.
101
+ *
102
+ * @param value - The extracted value to check
103
+ * @returns True if the value is dynamic
104
+ *
105
+ * @example
106
+ * ```typescript
107
+ * if (this.isDynamicValue(context.propertyValue)) {
108
+ * return []; // Skip validation
109
+ * }
110
+ * ```
111
+ */
112
+ protected isDynamicValue(value: any): value is DynamicValue;
113
+ /**
114
+ * Create a violation with consistent formatting
115
+ *
116
+ * Helper method to create ConstraintViolation objects with common fields populated.
117
+ *
118
+ * @param type - Violation type (e.g., 'invalid-field', 'type-mismatch')
119
+ * @param message - Error message
120
+ * @param severity - Severity level (default: 'high')
121
+ * @param suggestion - Optional fix suggestion
122
+ * @param metadata - Optional additional metadata
123
+ * @returns ConstraintViolation object
124
+ *
125
+ * @example
126
+ * ```typescript
127
+ * return [
128
+ * this.createViolation(
129
+ * 'invalid-field',
130
+ * `Field '${field}' does not exist on entity '${entityName}'`,
131
+ * 'critical',
132
+ * `Use one of: ${validFields.join(', ')}`,
133
+ * { field, entityName, validFields }
134
+ * )
135
+ * ];
136
+ * ```
137
+ */
138
+ protected createViolation(type: string, message: string, severity?: 'critical' | 'high' | 'medium' | 'low', suggestion?: string, metadata?: Record<string, any>): ConstraintViolation;
139
+ /**
140
+ * Get the dependent prop value
141
+ *
142
+ * If the constraint has a `dependsOn` field, this method retrieves
143
+ * the value of that dependent prop from sibling props.
144
+ *
145
+ * Returns null if:
146
+ * - No dependency is specified
147
+ * - The dependent prop is not found
148
+ * - The dependent prop has a dynamic value
149
+ *
150
+ * @param context - Validation context
151
+ * @param constraint - Constraint definition
152
+ * @returns The dependent prop value, or null if not available
153
+ *
154
+ * @example
155
+ * ```typescript
156
+ * // Constraint: { type: 'subset-of-entity-fields', dependsOn: 'entityName' }
157
+ * const entityName = this.getDependentPropValue(context, constraint);
158
+ * if (!entityName) {
159
+ * // Can't validate - entityName not provided or is dynamic
160
+ * return [];
161
+ * }
162
+ * ```
163
+ */
164
+ protected getDependentPropValue(context: ValidationContext, constraint: PropertyConstraint): any;
165
+ /**
166
+ * Format a list of values for error messages
167
+ *
168
+ * Truncates long lists with "..." and formats values nicely.
169
+ *
170
+ * @param values - Array of values
171
+ * @param maxItems - Maximum items to show (default: 10)
172
+ * @returns Formatted string
173
+ *
174
+ * @example
175
+ * ```typescript
176
+ * const fields = ['FirstName', 'LastName', 'Email', ...]; // 50 fields
177
+ * const formatted = this.formatValueList(fields, 5);
178
+ * // => "FirstName, LastName, Email, Status, JoinDate... (45 more)"
179
+ * ```
180
+ */
181
+ protected formatValueList(values: any[], maxItems?: number): string;
182
+ /**
183
+ * Apply custom error template
184
+ *
185
+ * If the constraint has an `errorTemplate`, applies it by replacing
186
+ * template variables with actual values.
187
+ *
188
+ * Template variables:
189
+ * - {property} - Property name from context
190
+ * - {value} - Property value from context
191
+ * - {constraint} - Constraint type
192
+ * - Custom variables from templateVars parameter
193
+ *
194
+ * @param constraint - Constraint definition
195
+ * @param context - Validation context
196
+ * @param defaultMessage - Default message to use if no template
197
+ * @param templateVars - Additional template variables
198
+ * @returns Formatted message
199
+ *
200
+ * @example
201
+ * ```typescript
202
+ * // Constraint: { errorTemplate: "Field '{field}' not found on {entityName}" }
203
+ * const message = this.applyErrorTemplate(
204
+ * constraint,
205
+ * context,
206
+ * `Field '${field}' does not exist`,
207
+ * { field: 'FullName', entityName: 'Members' }
208
+ * );
209
+ * // => "Field 'FullName' not found on Members"
210
+ * ```
211
+ */
212
+ protected applyErrorTemplate(constraint: PropertyConstraint, context: ValidationContext, defaultMessage: string, templateVars?: Record<string, any>): string;
213
+ /**
214
+ * Calculate Levenshtein distance between two strings
215
+ *
216
+ * Used for finding similar field names for "Did you mean?" suggestions.
217
+ *
218
+ * @param a - First string
219
+ * @param b - Second string
220
+ * @returns Edit distance
221
+ */
222
+ protected levenshteinDistance(a: string, b: string): number;
223
+ /**
224
+ * Find similar strings using Levenshtein distance
225
+ *
226
+ * Returns strings sorted by similarity (most similar first).
227
+ *
228
+ * @param target - The target string to find matches for
229
+ * @param candidates - Array of candidate strings
230
+ * @param maxResults - Maximum number of results (default: 3)
231
+ * @param maxDistance - Maximum edit distance to consider (default: 3)
232
+ * @returns Array of similar strings, sorted by distance
233
+ *
234
+ * @example
235
+ * ```typescript
236
+ * const fields = ['FirstName', 'LastName', 'Email', 'Status'];
237
+ * const similar = this.findSimilar('FristName', fields, 2);
238
+ * // => ['FirstName'] (edit distance 1)
239
+ * ```
240
+ */
241
+ protected findSimilar(target: string, candidates: string[], maxResults?: number, maxDistance?: number): string[];
242
+ /**
243
+ * Case-insensitive string comparison
244
+ *
245
+ * @param a - First string
246
+ * @param b - Second string
247
+ * @returns True if strings are equal (case-insensitive)
248
+ */
249
+ protected equalsCaseInsensitive(a: string, b: string): boolean;
250
+ /**
251
+ * Check if array contains value (case-insensitive for strings)
252
+ *
253
+ * @param arr - Array to search
254
+ * @param value - Value to find
255
+ * @returns True if array contains the value
256
+ */
257
+ protected includesCaseInsensitive(arr: string[], value: string): boolean;
258
+ }
259
+ //# sourceMappingURL=base-constraint-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-constraint-validator.d.ts","sourceRoot":"","sources":["../../../src/lib/constraint-validators/base-constraint-validator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,EACL,kBAAkB,EAClB,mBAAmB,EACpB,MAAM,6CAA6C,CAAC;AACrD,OAAO,EAAE,iBAAiB,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,EAAsB,YAAY,EAAE,MAAM,yBAAyB,CAAC;AAE3E;;;;GAIG;AACH,8BAAsB,uBAAuB;IAC3C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAgCG;IACH,QAAQ,CAAC,QAAQ,CACf,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,kBAAkB,GAC7B,mBAAmB,EAAE;IAExB;;;;;;;OAOG;IACH,OAAO,IAAI,MAAM;IAIjB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,cAAc,IAAI,MAAM;IAMjC;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,cAAc,CAAC,KAAK,EAAE,GAAG,GAAG,KAAK,IAAI,YAAY;IAI3D;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,SAAS,CAAC,eAAe,CACvB,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,QAAQ,GAAE,UAAU,GAAG,MAAM,GAAG,QAAQ,GAAG,KAAc,EACzD,UAAU,CAAC,EAAE,MAAM,EACnB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAC7B,mBAAmB;IAUtB;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACH,SAAS,CAAC,qBAAqB,CAC7B,OAAO,EAAE,iBAAiB,EAC1B,UAAU,EAAE,kBAAkB,GAC7B,GAAG;IAkBN;;;;;;;;;;;;;;;OAeG;IACH,SAAS,CAAC,eAAe,CAAC,MAAM,EAAE,GAAG,EAAE,EAAE,QAAQ,GAAE,MAAW,GAAG,MAAM;IAcvE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACH,SAAS,CAAC,kBAAkB,CAC1B,UAAU,EAAE,kBAAkB,EAC9B,OAAO,EAAE,iBAAiB,EAC1B,cAAc,EAAE,MAAM,EACtB,YAAY,GAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAM,GACrC,MAAM;IAwBT;;;;;;;;OAQG;IACH,SAAS,CAAC,mBAAmB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM;IA+B3D;;;;;;;;;;;;;;;;;OAiBG;IACH,SAAS,CAAC,WAAW,CACnB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAAE,EACpB,UAAU,GAAE,MAAU,EACtB,WAAW,GAAE,MAAU,GACtB,MAAM,EAAE;IAYX;;;;;;OAMG;IACH,SAAS,CAAC,qBAAqB,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO;IAI9D;;;;;;OAMG;IACH,SAAS,CAAC,uBAAuB,CAAC,GAAG,EAAE,MAAM,EAAE,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO;CAGzE"}
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ /**
3
+ * Base Constraint Validator
4
+ *
5
+ * Abstract base class for all constraint validators.
6
+ * Validators implement business logic for validating prop values at lint-time.
7
+ *
8
+ * Key Concepts:
9
+ * - **Validators = Code**: Implemented as TypeScript classes
10
+ * - **Constraints = Metadata**: Declared in component spec JSON
11
+ * - **Separation**: Validator logic is separate from constraint definitions
12
+ *
13
+ * Example:
14
+ * ```typescript
15
+ * // Validator implementation (code)
16
+ * class SubsetOfEntityFieldsValidator extends BaseConstraintValidator {
17
+ * async validate(context, constraint) {
18
+ * // Implementation...
19
+ * }
20
+ * }
21
+ *
22
+ * // Constraint definition (metadata in spec JSON)
23
+ * {
24
+ * "type": "subset-of-entity-fields",
25
+ * "dependsOn": "entityName"
26
+ * }
27
+ * ```
28
+ */
29
+ Object.defineProperty(exports, "__esModule", { value: true });
30
+ exports.BaseConstraintValidator = void 0;
31
+ const prop_value_extractor_1 = require("../prop-value-extractor");
32
+ /**
33
+ * Abstract base class for constraint validators
34
+ *
35
+ * All constraint validators must extend this class and implement the validate() method.
36
+ */
37
+ class BaseConstraintValidator {
38
+ /**
39
+ * Get the name of this validator
40
+ *
41
+ * Used for debugging and error messages.
42
+ * Defaults to the class name, but can be overridden.
43
+ *
44
+ * @returns Validator name
45
+ */
46
+ getName() {
47
+ return this.constructor.name;
48
+ }
49
+ // ============================================================================
50
+ // Protected Helper Methods
51
+ // ============================================================================
52
+ /**
53
+ * Check if a value is dynamic (can't be validated statically)
54
+ *
55
+ * Dynamic values include identifiers (variables) and expressions (function calls, etc.)
56
+ * that can't be resolved at lint-time.
57
+ *
58
+ * @param value - The extracted value to check
59
+ * @returns True if the value is dynamic
60
+ *
61
+ * @example
62
+ * ```typescript
63
+ * if (this.isDynamicValue(context.propertyValue)) {
64
+ * return []; // Skip validation
65
+ * }
66
+ * ```
67
+ */
68
+ isDynamicValue(value) {
69
+ return prop_value_extractor_1.PropValueExtractor.isDynamicValue(value);
70
+ }
71
+ /**
72
+ * Create a violation with consistent formatting
73
+ *
74
+ * Helper method to create ConstraintViolation objects with common fields populated.
75
+ *
76
+ * @param type - Violation type (e.g., 'invalid-field', 'type-mismatch')
77
+ * @param message - Error message
78
+ * @param severity - Severity level (default: 'high')
79
+ * @param suggestion - Optional fix suggestion
80
+ * @param metadata - Optional additional metadata
81
+ * @returns ConstraintViolation object
82
+ *
83
+ * @example
84
+ * ```typescript
85
+ * return [
86
+ * this.createViolation(
87
+ * 'invalid-field',
88
+ * `Field '${field}' does not exist on entity '${entityName}'`,
89
+ * 'critical',
90
+ * `Use one of: ${validFields.join(', ')}`,
91
+ * { field, entityName, validFields }
92
+ * )
93
+ * ];
94
+ * ```
95
+ */
96
+ createViolation(type, message, severity = 'high', suggestion, metadata) {
97
+ return {
98
+ type,
99
+ message,
100
+ severity,
101
+ suggestion,
102
+ metadata,
103
+ };
104
+ }
105
+ /**
106
+ * Get the dependent prop value
107
+ *
108
+ * If the constraint has a `dependsOn` field, this method retrieves
109
+ * the value of that dependent prop from sibling props.
110
+ *
111
+ * Returns null if:
112
+ * - No dependency is specified
113
+ * - The dependent prop is not found
114
+ * - The dependent prop has a dynamic value
115
+ *
116
+ * @param context - Validation context
117
+ * @param constraint - Constraint definition
118
+ * @returns The dependent prop value, or null if not available
119
+ *
120
+ * @example
121
+ * ```typescript
122
+ * // Constraint: { type: 'subset-of-entity-fields', dependsOn: 'entityName' }
123
+ * const entityName = this.getDependentPropValue(context, constraint);
124
+ * if (!entityName) {
125
+ * // Can't validate - entityName not provided or is dynamic
126
+ * return [];
127
+ * }
128
+ * ```
129
+ */
130
+ getDependentPropValue(context, constraint) {
131
+ if (!constraint.dependsOn) {
132
+ return null;
133
+ }
134
+ const dependentValue = context.siblingProps.get(constraint.dependsOn);
135
+ if (!dependentValue) {
136
+ return null;
137
+ }
138
+ if (this.isDynamicValue(dependentValue)) {
139
+ return null;
140
+ }
141
+ return dependentValue;
142
+ }
143
+ /**
144
+ * Format a list of values for error messages
145
+ *
146
+ * Truncates long lists with "..." and formats values nicely.
147
+ *
148
+ * @param values - Array of values
149
+ * @param maxItems - Maximum items to show (default: 10)
150
+ * @returns Formatted string
151
+ *
152
+ * @example
153
+ * ```typescript
154
+ * const fields = ['FirstName', 'LastName', 'Email', ...]; // 50 fields
155
+ * const formatted = this.formatValueList(fields, 5);
156
+ * // => "FirstName, LastName, Email, Status, JoinDate... (45 more)"
157
+ * ```
158
+ */
159
+ formatValueList(values, maxItems = 10) {
160
+ if (values.length === 0) {
161
+ return '(none)';
162
+ }
163
+ if (values.length <= maxItems) {
164
+ return values.map((v) => `'${v}'`).join(', ');
165
+ }
166
+ const shown = values.slice(0, maxItems).map((v) => `'${v}'`);
167
+ const remaining = values.length - maxItems;
168
+ return `${shown.join(', ')}... (${remaining} more)`;
169
+ }
170
+ /**
171
+ * Apply custom error template
172
+ *
173
+ * If the constraint has an `errorTemplate`, applies it by replacing
174
+ * template variables with actual values.
175
+ *
176
+ * Template variables:
177
+ * - {property} - Property name from context
178
+ * - {value} - Property value from context
179
+ * - {constraint} - Constraint type
180
+ * - Custom variables from templateVars parameter
181
+ *
182
+ * @param constraint - Constraint definition
183
+ * @param context - Validation context
184
+ * @param defaultMessage - Default message to use if no template
185
+ * @param templateVars - Additional template variables
186
+ * @returns Formatted message
187
+ *
188
+ * @example
189
+ * ```typescript
190
+ * // Constraint: { errorTemplate: "Field '{field}' not found on {entityName}" }
191
+ * const message = this.applyErrorTemplate(
192
+ * constraint,
193
+ * context,
194
+ * `Field '${field}' does not exist`,
195
+ * { field: 'FullName', entityName: 'Members' }
196
+ * );
197
+ * // => "Field 'FullName' not found on Members"
198
+ * ```
199
+ */
200
+ applyErrorTemplate(constraint, context, defaultMessage, templateVars = {}) {
201
+ if (!constraint.errorTemplate) {
202
+ return defaultMessage;
203
+ }
204
+ let message = constraint.errorTemplate;
205
+ // Standard variables
206
+ const vars = {
207
+ property: context.propertyName,
208
+ value: prop_value_extractor_1.PropValueExtractor.describe(context.propertyValue),
209
+ constraint: constraint.type,
210
+ ...templateVars,
211
+ };
212
+ // Replace template variables
213
+ for (const [key, value] of Object.entries(vars)) {
214
+ const regex = new RegExp(`\\{${key}\\}`, 'g');
215
+ message = message.replace(regex, String(value));
216
+ }
217
+ return message;
218
+ }
219
+ /**
220
+ * Calculate Levenshtein distance between two strings
221
+ *
222
+ * Used for finding similar field names for "Did you mean?" suggestions.
223
+ *
224
+ * @param a - First string
225
+ * @param b - Second string
226
+ * @returns Edit distance
227
+ */
228
+ levenshteinDistance(a, b) {
229
+ const matrix = [];
230
+ // Initialize first column
231
+ for (let i = 0; i <= b.length; i++) {
232
+ matrix[i] = [i];
233
+ }
234
+ // Initialize first row
235
+ for (let j = 0; j <= a.length; j++) {
236
+ matrix[0][j] = j;
237
+ }
238
+ // Fill matrix
239
+ for (let i = 1; i <= b.length; i++) {
240
+ for (let j = 1; j <= a.length; j++) {
241
+ if (b.charAt(i - 1) === a.charAt(j - 1)) {
242
+ matrix[i][j] = matrix[i - 1][j - 1];
243
+ }
244
+ else {
245
+ matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution
246
+ matrix[i][j - 1] + 1, // insertion
247
+ matrix[i - 1][j] + 1 // deletion
248
+ );
249
+ }
250
+ }
251
+ }
252
+ return matrix[b.length][a.length];
253
+ }
254
+ /**
255
+ * Find similar strings using Levenshtein distance
256
+ *
257
+ * Returns strings sorted by similarity (most similar first).
258
+ *
259
+ * @param target - The target string to find matches for
260
+ * @param candidates - Array of candidate strings
261
+ * @param maxResults - Maximum number of results (default: 3)
262
+ * @param maxDistance - Maximum edit distance to consider (default: 3)
263
+ * @returns Array of similar strings, sorted by distance
264
+ *
265
+ * @example
266
+ * ```typescript
267
+ * const fields = ['FirstName', 'LastName', 'Email', 'Status'];
268
+ * const similar = this.findSimilar('FristName', fields, 2);
269
+ * // => ['FirstName'] (edit distance 1)
270
+ * ```
271
+ */
272
+ findSimilar(target, candidates, maxResults = 3, maxDistance = 3) {
273
+ const withDistances = candidates
274
+ .map((candidate) => ({
275
+ value: candidate,
276
+ distance: this.levenshteinDistance(target.toLowerCase(), candidate.toLowerCase()),
277
+ }))
278
+ .filter((item) => item.distance <= maxDistance)
279
+ .sort((a, b) => a.distance - b.distance);
280
+ return withDistances.slice(0, maxResults).map((item) => item.value);
281
+ }
282
+ /**
283
+ * Case-insensitive string comparison
284
+ *
285
+ * @param a - First string
286
+ * @param b - Second string
287
+ * @returns True if strings are equal (case-insensitive)
288
+ */
289
+ equalsCaseInsensitive(a, b) {
290
+ return a.toLowerCase() === b.toLowerCase();
291
+ }
292
+ /**
293
+ * Check if array contains value (case-insensitive for strings)
294
+ *
295
+ * @param arr - Array to search
296
+ * @param value - Value to find
297
+ * @returns True if array contains the value
298
+ */
299
+ includesCaseInsensitive(arr, value) {
300
+ return arr.some((item) => this.equalsCaseInsensitive(item, value));
301
+ }
302
+ }
303
+ exports.BaseConstraintValidator = BaseConstraintValidator;
304
+ //# sourceMappingURL=base-constraint-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"base-constraint-validator.js","sourceRoot":"","sources":["../../../src/lib/constraint-validators/base-constraint-validator.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;;;AAOH,kEAA2E;AAE3E;;;;GAIG;AACH,MAAsB,uBAAuB;IAuC3C;;;;;;;OAOG;IACH,OAAO;QACL,OAAO,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC;IAC/B,CAAC;IAmBD,+EAA+E;IAC/E,2BAA2B;IAC3B,+EAA+E;IAE/E;;;;;;;;;;;;;;;OAeG;IACO,cAAc,CAAC,KAAU;QACjC,OAAO,yCAAkB,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAClD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACO,eAAe,CACvB,IAAY,EACZ,OAAe,EACf,WAAmD,MAAM,EACzD,UAAmB,EACnB,QAA8B;QAE9B,OAAO;YACL,IAAI;YACJ,OAAO;YACP,QAAQ;YACR,UAAU;YACV,QAAQ;SACT,CAAC;IACJ,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;OAwBG;IACO,qBAAqB,CAC7B,OAA0B,EAC1B,UAA8B;QAE9B,IAAI,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC;YAC1B,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,cAAc,GAAG,OAAO,CAAC,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC;QAEtE,IAAI,CAAC,cAAc,EAAE,CAAC;YACpB,OAAO,IAAI,CAAC;QACd,CAAC;QAED,IAAI,IAAI,CAAC,cAAc,CAAC,cAAc,CAAC,EAAE,CAAC;YACxC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,cAAc,CAAC;IACxB,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACO,eAAe,CAAC,MAAa,EAAE,WAAmB,EAAE;QAC5D,IAAI,MAAM,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,IAAI,MAAM,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC;YAC9B,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAChD,CAAC;QAED,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAC7D,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,GAAG,QAAQ,CAAC;QAC3C,OAAO,GAAG,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,SAAS,QAAQ,CAAC;IACtD,CAAC;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA6BG;IACO,kBAAkB,CAC1B,UAA8B,EAC9B,OAA0B,EAC1B,cAAsB,EACtB,eAAoC,EAAE;QAEtC,IAAI,CAAC,UAAU,CAAC,aAAa,EAAE,CAAC;YAC9B,OAAO,cAAc,CAAC;QACxB,CAAC;QAED,IAAI,OAAO,GAAG,UAAU,CAAC,aAAa,CAAC;QAEvC,qBAAqB;QACrB,MAAM,IAAI,GAAwB;YAChC,QAAQ,EAAE,OAAO,CAAC,YAAY;YAC9B,KAAK,EAAE,yCAAkB,CAAC,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC;YACzD,UAAU,EAAE,UAAU,CAAC,IAAI;YAC3B,GAAG,YAAY;SAChB,CAAC;QAEF,6BAA6B;QAC7B,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,CAAC;YAChD,MAAM,KAAK,GAAG,IAAI,MAAM,CAAC,MAAM,GAAG,KAAK,EAAE,GAAG,CAAC,CAAC;YAC9C,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAClD,CAAC;QAED,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;;;;;;;OAQG;IACO,mBAAmB,CAAC,CAAS,EAAE,CAAS;QAChD,MAAM,MAAM,GAAe,EAAE,CAAC;QAE9B,0BAA0B;QAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QAClB,CAAC;QAED,uBAAuB;QACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QACnB,CAAC;QAED,cAAc;QACd,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACnC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;gBACnC,IAAI,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC;oBACxC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;gBACtC,CAAC;qBAAM,CAAC;oBACN,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,GAAG,CACrB,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,eAAe;oBACzC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,EAAE,YAAY;oBAClC,MAAM,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,WAAW;qBACjC,CAAC;gBACJ,CAAC;YACH,CAAC;QACH,CAAC;QAED,OAAO,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;IACpC,CAAC;IAED;;;;;;;;;;;;;;;;;OAiBG;IACO,WAAW,CACnB,MAAc,EACd,UAAoB,EACpB,aAAqB,CAAC,EACtB,cAAsB,CAAC;QAEvB,MAAM,aAAa,GAAG,UAAU;aAC7B,GAAG,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;YACnB,KAAK,EAAE,SAAS;YAChB,QAAQ,EAAE,IAAI,CAAC,mBAAmB,CAAC,MAAM,CAAC,WAAW,EAAE,EAAE,SAAS,CAAC,WAAW,EAAE,CAAC;SAClF,CAAC,CAAC;aACF,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,IAAI,WAAW,CAAC;aAC9C,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,QAAQ,CAAC,CAAC;QAE3C,OAAO,aAAa,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACtE,CAAC;IAED;;;;;;OAMG;IACO,qBAAqB,CAAC,CAAS,EAAE,CAAS;QAClD,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;IAC7C,CAAC;IAED;;;;;;OAMG;IACO,uBAAuB,CAAC,GAAa,EAAE,KAAa;QAC5D,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,qBAAqB,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC;IACrE,CAAC;CACF;AA5WD,0DA4WC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Constraint Validators Module
3
+ *
4
+ * This module provides the infrastructure for validating component props
5
+ * against business rules defined in component specifications.
6
+ *
7
+ * Validators use MemberJunction's @RegisterClass decorator for automatic discovery.
8
+ * Each validator is registered with BaseConstraintValidator and its constraint type as the key.
9
+ *
10
+ * Key exports:
11
+ * - BaseConstraintValidator: Abstract base class for all validators
12
+ * - ValidationContext: Context passed to validators
13
+ * - Entity/Query metadata types
14
+ * - Concrete validator implementations (auto-registered via @RegisterClass)
15
+ */
16
+ export * from './base-constraint-validator';
17
+ export * from './validation-context';
18
+ export * from './subset-of-entity-fields-validator';
19
+ export * from './sql-where-clause-validator';
20
+ export * from './required-when-validator';
21
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/lib/constraint-validators/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,cAAc,6BAA6B,CAAC;AAC5C,cAAc,sBAAsB,CAAC;AACrC,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ /**
3
+ * Constraint Validators Module
4
+ *
5
+ * This module provides the infrastructure for validating component props
6
+ * against business rules defined in component specifications.
7
+ *
8
+ * Validators use MemberJunction's @RegisterClass decorator for automatic discovery.
9
+ * Each validator is registered with BaseConstraintValidator and its constraint type as the key.
10
+ *
11
+ * Key exports:
12
+ * - BaseConstraintValidator: Abstract base class for all validators
13
+ * - ValidationContext: Context passed to validators
14
+ * - Entity/Query metadata types
15
+ * - Concrete validator implementations (auto-registered via @RegisterClass)
16
+ */
17
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ var desc = Object.getOwnPropertyDescriptor(m, k);
20
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
21
+ desc = { enumerable: true, get: function() { return m[k]; } };
22
+ }
23
+ Object.defineProperty(o, k2, desc);
24
+ }) : (function(o, m, k, k2) {
25
+ if (k2 === undefined) k2 = k;
26
+ o[k2] = m[k];
27
+ }));
28
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
29
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
30
+ };
31
+ Object.defineProperty(exports, "__esModule", { value: true });
32
+ __exportStar(require("./base-constraint-validator"), exports);
33
+ __exportStar(require("./validation-context"), exports);
34
+ __exportStar(require("./subset-of-entity-fields-validator"), exports);
35
+ __exportStar(require("./sql-where-clause-validator"), exports);
36
+ __exportStar(require("./required-when-validator"), exports);
37
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/lib/constraint-validators/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;;;;;AAEH,8DAA4C;AAC5C,uDAAqC;AACrC,sEAAoD;AACpD,+DAA6C;AAC7C,4DAA0C"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Required-When Constraint Validator
3
+ *
4
+ * Validates that a property is present when a specified condition is met.
5
+ * Used for conditional requirements like "valueField is required when aggregateMethod is 'sum'".
6
+ *
7
+ * Config:
8
+ * - condition: JavaScript expression evaluated with siblingProp as the dependent property value
9
+ * Example: "siblingProp === 'sum' || siblingProp === 'average'"
10
+ *
11
+ * Example usage in spec JSON:
12
+ * ```json
13
+ * {
14
+ * "type": "required-when",
15
+ * "dependsOn": "aggregateMethod",
16
+ * "config": {
17
+ * "condition": "siblingProp === 'sum' || siblingProp === 'average' || siblingProp === 'min' || siblingProp === 'max'"
18
+ * },
19
+ * "errorTemplate": "valueField is required when aggregateMethod is '{aggregateMethod}'"
20
+ * }
21
+ * ```
22
+ */
23
+ import { PropertyConstraint, ConstraintViolation } from '@memberjunction/interactive-component-types';
24
+ import { BaseConstraintValidator } from './base-constraint-validator';
25
+ import { ValidationContext } from './validation-context';
26
+ export declare class RequiredWhenValidator extends BaseConstraintValidator {
27
+ /**
28
+ * Get description of this validator
29
+ */
30
+ getDescription(): string;
31
+ /**
32
+ * Validate that a property is present when a condition is met
33
+ *
34
+ * Evaluates a JavaScript condition expression with the dependent property value,
35
+ * and checks if the current property has a value when the condition is true.
36
+ *
37
+ * @param context - Validation context containing property info and sibling props
38
+ * @param constraint - The constraint definition from the spec
39
+ * @returns Array of violations (empty if valid)
40
+ */
41
+ validate(context: ValidationContext, constraint: PropertyConstraint): ConstraintViolation[];
42
+ }
43
+ //# sourceMappingURL=required-when-validator.d.ts.map