@ram_28/kf-ai-sdk 1.0.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/LICENSE +21 -0
- package/README.md +840 -0
- package/dist/api/client.d.ts +78 -0
- package/dist/api/client.d.ts.map +1 -0
- package/dist/api/datetime.d.ts +21 -0
- package/dist/api/datetime.d.ts.map +1 -0
- package/dist/api/index.d.ts +7 -0
- package/dist/api/index.d.ts.map +1 -0
- package/dist/api/metadata.d.ts +75 -0
- package/dist/api/metadata.d.ts.map +1 -0
- package/dist/components/hooks/index.d.ts +8 -0
- package/dist/components/hooks/index.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/index.d.ts +5 -0
- package/dist/components/hooks/useFilter/index.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts +33 -0
- package/dist/components/hooks/useFilter/payloadBuilder.utils.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/types.d.ts +137 -0
- package/dist/components/hooks/useFilter/types.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/useFilter.d.ts +3 -0
- package/dist/components/hooks/useFilter/useFilter.d.ts.map +1 -0
- package/dist/components/hooks/useFilter/validation.utils.d.ts +38 -0
- package/dist/components/hooks/useFilter/validation.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/apiClient.d.ts +71 -0
- package/dist/components/hooks/useForm/apiClient.d.ts.map +1 -0
- package/dist/components/hooks/useForm/expressionValidator.utils.d.ts +28 -0
- package/dist/components/hooks/useForm/expressionValidator.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/index.d.ts +6 -0
- package/dist/components/hooks/useForm/index.d.ts.map +1 -0
- package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts +88 -0
- package/dist/components/hooks/useForm/optimizedExpressionValidator.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts +28 -0
- package/dist/components/hooks/useForm/ruleClassifier.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/schemaParser.utils.d.ts +29 -0
- package/dist/components/hooks/useForm/schemaParser.utils.d.ts.map +1 -0
- package/dist/components/hooks/useForm/types.d.ts +412 -0
- package/dist/components/hooks/useForm/types.d.ts.map +1 -0
- package/dist/components/hooks/useForm/useForm.d.ts +3 -0
- package/dist/components/hooks/useForm/useForm.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/apiClient.d.ts +99 -0
- package/dist/components/hooks/useKanban/apiClient.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/context.d.ts +4 -0
- package/dist/components/hooks/useKanban/context.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/dragDropManager.d.ts +27 -0
- package/dist/components/hooks/useKanban/dragDropManager.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/index.d.ts +6 -0
- package/dist/components/hooks/useKanban/index.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/types.d.ts +438 -0
- package/dist/components/hooks/useKanban/types.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/useKanban.d.ts +3 -0
- package/dist/components/hooks/useKanban/useKanban.d.ts.map +1 -0
- package/dist/components/hooks/useKanban/useKanbanSimple.d.ts +62 -0
- package/dist/components/hooks/useKanban/useKanbanSimple.d.ts.map +1 -0
- package/dist/components/hooks/useTable/index.d.ts +3 -0
- package/dist/components/hooks/useTable/index.d.ts.map +1 -0
- package/dist/components/hooks/useTable/types.d.ts +107 -0
- package/dist/components/hooks/useTable/types.d.ts.map +1 -0
- package/dist/components/hooks/useTable/useTable.d.ts +8 -0
- package/dist/components/hooks/useTable/useTable.d.ts.map +1 -0
- package/dist/components/index.d.ts +3 -0
- package/dist/components/index.d.ts.map +1 -0
- package/dist/components/ui/index.d.ts +2 -0
- package/dist/components/ui/index.d.ts.map +1 -0
- package/dist/components/ui/kanban/Kanban.d.ts +12 -0
- package/dist/components/ui/kanban/Kanban.d.ts.map +1 -0
- package/dist/components/ui/kanban/index.d.ts +2 -0
- package/dist/components/ui/kanban/index.d.ts.map +1 -0
- package/dist/index.cjs +45 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +6522 -0
- package/dist/types/base-fields.d.ts +182 -0
- package/dist/types/base-fields.d.ts.map +1 -0
- package/dist/types/common.d.ts +238 -0
- package/dist/types/common.d.ts.map +1 -0
- package/dist/types/index.d.ts +3 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/utils/cn.d.ts +7 -0
- package/dist/utils/cn.d.ts.map +1 -0
- package/dist/utils/formatting.d.ts +52 -0
- package/dist/utils/formatting.d.ts.map +1 -0
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/package.json +98 -0
- package/sdk/api/client.ts +447 -0
- package/sdk/api/datetime.ts +33 -0
- package/sdk/api/index.ts +61 -0
- package/sdk/api/metadata.ts +148 -0
- package/sdk/components/hooks/index.ts +34 -0
- package/sdk/components/hooks/useFilter/index.ts +37 -0
- package/sdk/components/hooks/useFilter/payloadBuilder.utils.ts +298 -0
- package/sdk/components/hooks/useFilter/types.ts +158 -0
- package/sdk/components/hooks/useFilter/useFilter.llm.txt +497 -0
- package/sdk/components/hooks/useFilter/useFilter.ts +494 -0
- package/sdk/components/hooks/useFilter/validation.utils.ts +401 -0
- package/sdk/components/hooks/useForm/apiClient.ts +441 -0
- package/sdk/components/hooks/useForm/expressionValidator.utils.ts +444 -0
- package/sdk/components/hooks/useForm/index.ts +64 -0
- package/sdk/components/hooks/useForm/optimizedExpressionValidator.utils.ts +482 -0
- package/sdk/components/hooks/useForm/ruleClassifier.utils.ts +424 -0
- package/sdk/components/hooks/useForm/schemaParser.utils.ts +519 -0
- package/sdk/components/hooks/useForm/types.ts +630 -0
- package/sdk/components/hooks/useForm/useForm.llm.txt +340 -0
- package/sdk/components/hooks/useForm/useForm.ts +821 -0
- package/sdk/components/hooks/useKanban/apiClient.ts +494 -0
- package/sdk/components/hooks/useKanban/context.ts +14 -0
- package/sdk/components/hooks/useKanban/dragDropManager.ts +529 -0
- package/sdk/components/hooks/useKanban/index.ts +63 -0
- package/sdk/components/hooks/useKanban/types.ts +606 -0
- package/sdk/components/hooks/useKanban/useKanban.llm.txt +482 -0
- package/sdk/components/hooks/useKanban/useKanban.ts +725 -0
- package/sdk/components/hooks/useKanban/useKanbanSimple.ts +389 -0
- package/sdk/components/hooks/useTable/index.ts +5 -0
- package/sdk/components/hooks/useTable/types.ts +154 -0
- package/sdk/components/hooks/useTable/useTable.llm.txt +344 -0
- package/sdk/components/hooks/useTable/useTable.ts +413 -0
- package/sdk/components/index.ts +15 -0
- package/sdk/components/ui/index.ts +2 -0
- package/sdk/components/ui/kanban/Kanban.tsx +134 -0
- package/sdk/components/ui/kanban/index.ts +11 -0
- package/sdk/index.ts +13 -0
- package/sdk/types/base-fields.ts +221 -0
- package/sdk/types/common.ts +306 -0
- package/sdk/types/index.ts +5 -0
- package/sdk/utils/cn.ts +10 -0
- package/sdk/utils/formatting.ts +212 -0
- package/sdk/utils/index.ts +5 -0
|
@@ -0,0 +1,519 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// SCHEMA PARSER
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Converts backend field schemas to react-hook-form validation rules
|
|
5
|
+
|
|
6
|
+
import type {
|
|
7
|
+
BackendSchema,
|
|
8
|
+
BDOSchema,
|
|
9
|
+
BackendFieldDefinition,
|
|
10
|
+
ProcessedField,
|
|
11
|
+
ProcessedSchema,
|
|
12
|
+
ValidationRule,
|
|
13
|
+
FieldPermission,
|
|
14
|
+
} from "./types";
|
|
15
|
+
import {
|
|
16
|
+
calculateDefaultValue,
|
|
17
|
+
calculateComputedValue,
|
|
18
|
+
} from "./expressionValidator.utils";
|
|
19
|
+
import {
|
|
20
|
+
classifyRules,
|
|
21
|
+
createFieldRuleMapping,
|
|
22
|
+
calculateFieldPermissions,
|
|
23
|
+
convertLegacySchema,
|
|
24
|
+
normalizeBDOSchema,
|
|
25
|
+
} from "./ruleClassifier.utils";
|
|
26
|
+
|
|
27
|
+
// ============================================================
|
|
28
|
+
// FIELD TYPE MAPPING
|
|
29
|
+
// ============================================================
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Map backend field types to HTML input types
|
|
33
|
+
*/
|
|
34
|
+
function mapFieldType(backendType: string, fieldName: string): string {
|
|
35
|
+
switch (backendType) {
|
|
36
|
+
case "String":
|
|
37
|
+
// Infer specific string types from field name
|
|
38
|
+
if (fieldName.toLowerCase().includes("email")) {
|
|
39
|
+
return "email";
|
|
40
|
+
}
|
|
41
|
+
if (fieldName.toLowerCase().includes("password")) {
|
|
42
|
+
return "password";
|
|
43
|
+
}
|
|
44
|
+
return "text";
|
|
45
|
+
|
|
46
|
+
case "Number":
|
|
47
|
+
return "number";
|
|
48
|
+
|
|
49
|
+
case "Boolean":
|
|
50
|
+
return "checkbox";
|
|
51
|
+
|
|
52
|
+
case "Date":
|
|
53
|
+
return "date";
|
|
54
|
+
|
|
55
|
+
case "DateTime":
|
|
56
|
+
return "datetime-local";
|
|
57
|
+
|
|
58
|
+
case "Reference":
|
|
59
|
+
return "reference";
|
|
60
|
+
|
|
61
|
+
case "Array":
|
|
62
|
+
case "Object":
|
|
63
|
+
return "textarea"; // JSON string representation
|
|
64
|
+
|
|
65
|
+
default:
|
|
66
|
+
return "text";
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Generate field label from field name
|
|
72
|
+
*/
|
|
73
|
+
function generateLabel(fieldName: string): string {
|
|
74
|
+
return fieldName
|
|
75
|
+
.replace(/([A-Z])/g, " $1") // Add space before capital letters
|
|
76
|
+
.replace(/^./, (str) => str.toUpperCase()) // Capitalize first letter
|
|
77
|
+
.replace(/_/g, " ") // Replace underscores with spaces
|
|
78
|
+
.trim();
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// ============================================================
|
|
82
|
+
// VALIDATION RULE CONVERSION
|
|
83
|
+
// ============================================================
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Convert backend validation rules to react-hook-form validation
|
|
87
|
+
*/
|
|
88
|
+
function convertValidationRules(
|
|
89
|
+
fieldName: string,
|
|
90
|
+
fieldDef: BackendFieldDefinition,
|
|
91
|
+
_allFields: Record<string, BackendFieldDefinition>
|
|
92
|
+
): any {
|
|
93
|
+
const validation: any = {};
|
|
94
|
+
|
|
95
|
+
// Required validation
|
|
96
|
+
if (fieldDef.Required) {
|
|
97
|
+
validation.required = {
|
|
98
|
+
value: true,
|
|
99
|
+
message: `${generateLabel(fieldName)} is required`,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// Type-specific validation
|
|
104
|
+
switch (fieldDef.Type) {
|
|
105
|
+
case "Number":
|
|
106
|
+
validation.valueAsNumber = true;
|
|
107
|
+
break;
|
|
108
|
+
|
|
109
|
+
case "Date":
|
|
110
|
+
case "DateTime":
|
|
111
|
+
validation.valueAsDate = true;
|
|
112
|
+
break;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Custom validation using expression trees
|
|
116
|
+
// Note: fieldDef.Validation contains rule IDs (string[]), not ValidationRule[]
|
|
117
|
+
// Validation is handled by the form hook which has access to the full schema rules
|
|
118
|
+
if (fieldDef.Validation && fieldDef.Validation.length > 0) {
|
|
119
|
+
// Validation rules will be applied by the form hook
|
|
120
|
+
// This is just a placeholder to mark that validation exists
|
|
121
|
+
validation.validate = () => true;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return validation;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// ============================================================
|
|
128
|
+
// SELECT OPTIONS PROCESSING
|
|
129
|
+
// ============================================================
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Process field options for select/reference fields
|
|
133
|
+
*/
|
|
134
|
+
function processFieldOptions(
|
|
135
|
+
fieldDef: BackendFieldDefinition
|
|
136
|
+
): Array<{ value: any; label: string }> {
|
|
137
|
+
if (!fieldDef.Values) {
|
|
138
|
+
return [];
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Static options
|
|
142
|
+
if (fieldDef.Values.Mode === "Static" && fieldDef.Values.Items) {
|
|
143
|
+
return fieldDef.Values.Items.map((item: any) => ({
|
|
144
|
+
value: item.Value,
|
|
145
|
+
label: item.Label,
|
|
146
|
+
}));
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Dynamic options (reference field)
|
|
150
|
+
if (fieldDef.Values.Mode === "Dynamic" && fieldDef.Values.Reference) {
|
|
151
|
+
// For now, return empty array - will be populated by form hook
|
|
152
|
+
return [];
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
return [];
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// ============================================================
|
|
159
|
+
// DEFAULT VALUE PROCESSING
|
|
160
|
+
// ============================================================
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Calculate default value for a field
|
|
164
|
+
*/
|
|
165
|
+
function processDefaultValue(
|
|
166
|
+
fieldDef: BackendFieldDefinition,
|
|
167
|
+
formValues: Record<string, any> = {}
|
|
168
|
+
): any {
|
|
169
|
+
if (!fieldDef.DefaultValue) {
|
|
170
|
+
// Type-specific defaults
|
|
171
|
+
switch (fieldDef.Type) {
|
|
172
|
+
case "Boolean":
|
|
173
|
+
return false;
|
|
174
|
+
case "Number":
|
|
175
|
+
return 0;
|
|
176
|
+
case "String":
|
|
177
|
+
return "";
|
|
178
|
+
case "Array":
|
|
179
|
+
return [];
|
|
180
|
+
case "Object":
|
|
181
|
+
return {};
|
|
182
|
+
default:
|
|
183
|
+
return undefined;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return calculateDefaultValue(
|
|
188
|
+
fieldDef.DefaultValue.ExpressionTree,
|
|
189
|
+
formValues
|
|
190
|
+
);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// ============================================================
|
|
194
|
+
// FIELD PROCESSING
|
|
195
|
+
// ============================================================
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Process a single field definition
|
|
199
|
+
*/
|
|
200
|
+
function processField(
|
|
201
|
+
fieldName: string,
|
|
202
|
+
fieldDef: BackendFieldDefinition,
|
|
203
|
+
allFields: Record<string, BackendFieldDefinition>,
|
|
204
|
+
formValues: Record<string, any> = {},
|
|
205
|
+
permission?: FieldPermission,
|
|
206
|
+
rules?: {
|
|
207
|
+
validation: string[];
|
|
208
|
+
computation: string[];
|
|
209
|
+
businessLogic: string[];
|
|
210
|
+
}
|
|
211
|
+
): ProcessedField {
|
|
212
|
+
const defaultPermission: FieldPermission = {
|
|
213
|
+
editable: true,
|
|
214
|
+
readable: true,
|
|
215
|
+
hidden: false,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
const defaultRules = {
|
|
219
|
+
validation: [],
|
|
220
|
+
computation: [],
|
|
221
|
+
businessLogic: [],
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const fieldRules = rules || defaultRules;
|
|
225
|
+
const isComputed =
|
|
226
|
+
fieldDef.Computed ||
|
|
227
|
+
!!fieldDef.Formula ||
|
|
228
|
+
fieldRules.computation.length > 0;
|
|
229
|
+
|
|
230
|
+
const validation = convertValidationRules(fieldName, fieldDef, allFields);
|
|
231
|
+
|
|
232
|
+
// Mark computed fields as disabled so they cannot be manually edited
|
|
233
|
+
if (isComputed) {
|
|
234
|
+
validation.disabled = true;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
return {
|
|
238
|
+
name: fieldName,
|
|
239
|
+
type: mapFieldType(fieldDef.Type, fieldName) as any,
|
|
240
|
+
label: fieldDef.Name || generateLabel(fieldName),
|
|
241
|
+
required: fieldDef.Required || false,
|
|
242
|
+
computed: isComputed,
|
|
243
|
+
defaultValue: processDefaultValue(fieldDef, formValues),
|
|
244
|
+
options: processFieldOptions(fieldDef),
|
|
245
|
+
validation,
|
|
246
|
+
description: fieldDef.Description,
|
|
247
|
+
backendField: fieldDef,
|
|
248
|
+
permission: permission || defaultPermission,
|
|
249
|
+
rules: fieldRules,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ============================================================
|
|
254
|
+
// SCHEMA PROCESSING
|
|
255
|
+
// ============================================================
|
|
256
|
+
|
|
257
|
+
/**
|
|
258
|
+
* Process complete backend schema
|
|
259
|
+
*/
|
|
260
|
+
export function processSchema(
|
|
261
|
+
schema: BackendSchema | BDOSchema,
|
|
262
|
+
formValues: Record<string, any> = {},
|
|
263
|
+
userRole?: string
|
|
264
|
+
): ProcessedSchema {
|
|
265
|
+
// Convert legacy schema to BDO format if needed
|
|
266
|
+
let bdoSchema =
|
|
267
|
+
"Kind" in schema && schema.Kind === "BusinessObject"
|
|
268
|
+
? (schema as BDOSchema)
|
|
269
|
+
: convertLegacySchema(schema as BackendSchema);
|
|
270
|
+
|
|
271
|
+
// Normalize BDO schema to ensure inline validation rules are centralized
|
|
272
|
+
bdoSchema = normalizeBDOSchema(bdoSchema);
|
|
273
|
+
|
|
274
|
+
const fields: Record<string, ProcessedField> = {};
|
|
275
|
+
const fieldOrder: string[] = [];
|
|
276
|
+
const computedFields: string[] = [];
|
|
277
|
+
const requiredFields: string[] = [];
|
|
278
|
+
const crossFieldValidation: ValidationRule[] = [];
|
|
279
|
+
|
|
280
|
+
// Classify rules by type
|
|
281
|
+
const classifiedRules = classifyRules(bdoSchema);
|
|
282
|
+
|
|
283
|
+
// Create field-to-rule mapping
|
|
284
|
+
const fieldRules = createFieldRuleMapping(bdoSchema, classifiedRules);
|
|
285
|
+
|
|
286
|
+
// Calculate field permissions
|
|
287
|
+
const fieldPermissions = calculateFieldPermissions(bdoSchema, userRole);
|
|
288
|
+
|
|
289
|
+
// Process each field
|
|
290
|
+
for (const [fieldName, fieldDef] of Object.entries(bdoSchema.Fields)) {
|
|
291
|
+
// Skip system fields that shouldn't be in forms
|
|
292
|
+
if (fieldName.startsWith("_") && !["_id"].includes(fieldName)) {
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
// Skip hidden fields
|
|
297
|
+
const permission = fieldPermissions[fieldName];
|
|
298
|
+
if (permission.hidden) {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const processedField = processField(
|
|
303
|
+
fieldName,
|
|
304
|
+
fieldDef,
|
|
305
|
+
bdoSchema.Fields,
|
|
306
|
+
formValues,
|
|
307
|
+
permission,
|
|
308
|
+
fieldRules[fieldName] || {
|
|
309
|
+
validation: [],
|
|
310
|
+
computation: [],
|
|
311
|
+
businessLogic: [],
|
|
312
|
+
}
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
fields[fieldName] = processedField;
|
|
316
|
+
fieldOrder.push(fieldName);
|
|
317
|
+
|
|
318
|
+
if (processedField.computed) {
|
|
319
|
+
computedFields.push(fieldName);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (processedField.required) {
|
|
323
|
+
requiredFields.push(fieldName);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
fields,
|
|
329
|
+
fieldOrder,
|
|
330
|
+
computedFields,
|
|
331
|
+
requiredFields,
|
|
332
|
+
crossFieldValidation,
|
|
333
|
+
rules: classifiedRules,
|
|
334
|
+
fieldRules,
|
|
335
|
+
rolePermissions: bdoSchema.RolePermission,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ============================================================
|
|
340
|
+
// COMPUTED FIELD UPDATES
|
|
341
|
+
// ============================================================
|
|
342
|
+
|
|
343
|
+
/**
|
|
344
|
+
* Update computed field values based on current form values
|
|
345
|
+
*/
|
|
346
|
+
export function updateComputedFields(
|
|
347
|
+
processedSchema: ProcessedSchema,
|
|
348
|
+
currentValues: Record<string, any>
|
|
349
|
+
): Record<string, any> {
|
|
350
|
+
const computedValues: Record<string, any> = {};
|
|
351
|
+
|
|
352
|
+
for (const fieldName of processedSchema.computedFields) {
|
|
353
|
+
const field = processedSchema.fields[fieldName];
|
|
354
|
+
const backendField = field.backendField;
|
|
355
|
+
|
|
356
|
+
if (backendField.Formula) {
|
|
357
|
+
try {
|
|
358
|
+
const computedValue = calculateComputedValue(
|
|
359
|
+
backendField.Formula.ExpressionTree,
|
|
360
|
+
currentValues
|
|
361
|
+
);
|
|
362
|
+
|
|
363
|
+
computedValues[fieldName] = computedValue;
|
|
364
|
+
} catch (error) {
|
|
365
|
+
console.warn(`Failed to compute value for ${fieldName}:`, error);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
return computedValues;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
// ============================================================
|
|
374
|
+
// FIELD DEPENDENCIES
|
|
375
|
+
// ============================================================
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Extract field dependencies from expression trees
|
|
379
|
+
*/
|
|
380
|
+
function extractFieldDependencies(expressionTree: any): string[] {
|
|
381
|
+
const dependencies: Set<string> = new Set();
|
|
382
|
+
|
|
383
|
+
function traverse(node: any): void {
|
|
384
|
+
if (!node || typeof node !== "object") {
|
|
385
|
+
return;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
if (node.Type === "Identifier" && node.Name) {
|
|
389
|
+
dependencies.add(node.Name);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
if (node.Arguments && Array.isArray(node.Arguments)) {
|
|
393
|
+
node.Arguments.forEach(traverse);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
if (node.Property) {
|
|
397
|
+
traverse(node.Property);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
traverse(expressionTree);
|
|
402
|
+
return Array.from(dependencies);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
/**
|
|
406
|
+
* Build field dependency map
|
|
407
|
+
*/
|
|
408
|
+
export function buildDependencyMap(
|
|
409
|
+
processedSchema: ProcessedSchema
|
|
410
|
+
): Record<string, string[]> {
|
|
411
|
+
const dependencyMap: Record<string, string[]> = {};
|
|
412
|
+
|
|
413
|
+
for (const [fieldName, field] of Object.entries(processedSchema.fields)) {
|
|
414
|
+
const dependencies: Set<string> = new Set();
|
|
415
|
+
|
|
416
|
+
// Check formula dependencies
|
|
417
|
+
if (field.backendField.Formula) {
|
|
418
|
+
const formulaDeps = extractFieldDependencies(
|
|
419
|
+
field.backendField.Formula.ExpressionTree
|
|
420
|
+
);
|
|
421
|
+
formulaDeps.forEach((dep) => dependencies.add(dep));
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Check validation dependencies
|
|
425
|
+
// Note: field.backendField.Validation contains rule IDs (string[])
|
|
426
|
+
// To extract validation dependencies, we would need access to the full schema rules
|
|
427
|
+
// This is skipped for now as validation dependencies are tracked separately
|
|
428
|
+
if (field.backendField.Validation) {
|
|
429
|
+
// Validation rule dependencies would be extracted here if we had access to the rules
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
// Check default value dependencies
|
|
433
|
+
if (field.backendField.DefaultValue) {
|
|
434
|
+
const defaultDeps = extractFieldDependencies(
|
|
435
|
+
field.backendField.DefaultValue.ExpressionTree
|
|
436
|
+
);
|
|
437
|
+
defaultDeps.forEach((dep) => dependencies.add(dep));
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
dependencyMap[fieldName] = Array.from(dependencies);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return dependencyMap;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
// ============================================================
|
|
447
|
+
// VALIDATION HELPERS
|
|
448
|
+
// ============================================================
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Validate processed schema
|
|
452
|
+
*/
|
|
453
|
+
export function validateSchema(processedSchema: ProcessedSchema): {
|
|
454
|
+
isValid: boolean;
|
|
455
|
+
errors: string[];
|
|
456
|
+
} {
|
|
457
|
+
const errors: string[] = [];
|
|
458
|
+
|
|
459
|
+
// Check for required fields
|
|
460
|
+
if (processedSchema.fieldOrder.length === 0) {
|
|
461
|
+
errors.push("Schema contains no fields");
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Check for circular dependencies in computed fields
|
|
465
|
+
const dependencyMap = buildDependencyMap(processedSchema);
|
|
466
|
+
|
|
467
|
+
for (const [fieldName, dependencies] of Object.entries(dependencyMap)) {
|
|
468
|
+
if (dependencies.includes(fieldName)) {
|
|
469
|
+
errors.push(`Field ${fieldName} has circular dependency`);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return {
|
|
474
|
+
isValid: errors.length === 0,
|
|
475
|
+
errors,
|
|
476
|
+
};
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// ============================================================
|
|
480
|
+
// REFERENCE FIELD PROCESSING
|
|
481
|
+
// ============================================================
|
|
482
|
+
|
|
483
|
+
/**
|
|
484
|
+
* Build reference field configuration for API calls
|
|
485
|
+
*/
|
|
486
|
+
export function buildReferenceFieldConfig(field: ProcessedField): any {
|
|
487
|
+
if (field.type !== "reference" || !field.backendField.Values?.Reference) {
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const ref = field.backendField.Values.Reference;
|
|
492
|
+
|
|
493
|
+
return {
|
|
494
|
+
businessObject: ref.BusinessObject,
|
|
495
|
+
fields: ref.Fields || ["_id"], // Default to ID field
|
|
496
|
+
filters: ref.Filters,
|
|
497
|
+
sort: ref.Sort,
|
|
498
|
+
};
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
/**
|
|
502
|
+
* Extract all reference field configurations from schema
|
|
503
|
+
*/
|
|
504
|
+
export function extractReferenceFields(
|
|
505
|
+
processedSchema: ProcessedSchema
|
|
506
|
+
): Record<string, any> {
|
|
507
|
+
const referenceFields: Record<string, any> = {};
|
|
508
|
+
|
|
509
|
+
for (const [fieldName, field] of Object.entries(processedSchema.fields)) {
|
|
510
|
+
if (field.type === "reference") {
|
|
511
|
+
const config = buildReferenceFieldConfig(field);
|
|
512
|
+
if (config) {
|
|
513
|
+
referenceFields[fieldName] = config;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
return referenceFields;
|
|
519
|
+
}
|