@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,441 @@
|
|
|
1
|
+
// ============================================================
|
|
2
|
+
// API CLIENT FOR FORM OPERATIONS
|
|
3
|
+
// ============================================================
|
|
4
|
+
// Handles schema fetching and form submissions
|
|
5
|
+
|
|
6
|
+
import { api, getBdoSchema } from "../../../api";
|
|
7
|
+
import type { BackendSchema, FormOperation, SubmissionResult } from "./types";
|
|
8
|
+
|
|
9
|
+
// ============================================================
|
|
10
|
+
// SCHEMA FETCHING
|
|
11
|
+
// ============================================================
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Fetch BDO schema from backend metadata endpoint
|
|
15
|
+
*/
|
|
16
|
+
export async function fetchFormSchema(source: string): Promise<BackendSchema> {
|
|
17
|
+
try {
|
|
18
|
+
// Use the new metadata API client to fetch BDO schema
|
|
19
|
+
const bdoResp = await getBdoSchema(source);
|
|
20
|
+
const bdoSchema = bdoResp.BOBlob;
|
|
21
|
+
|
|
22
|
+
// Validate that response is a valid BDO schema object
|
|
23
|
+
if (!bdoSchema || typeof bdoSchema !== "object" || !bdoSchema.Fields) {
|
|
24
|
+
throw new Error(`Invalid BDO schema response for ${source}`);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Return the full BDO schema - the form processor will extract what it needs
|
|
28
|
+
return bdoSchema as BackendSchema;
|
|
29
|
+
} catch (error) {
|
|
30
|
+
console.error(`Schema fetch error for ${source}:`, error);
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Failed to load form schema: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Fetch form schema with retry logic
|
|
39
|
+
*/
|
|
40
|
+
export async function fetchFormSchemaWithRetry(
|
|
41
|
+
source: string,
|
|
42
|
+
maxRetries: number = 3
|
|
43
|
+
): Promise<BackendSchema> {
|
|
44
|
+
let lastError: Error;
|
|
45
|
+
|
|
46
|
+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
|
|
47
|
+
try {
|
|
48
|
+
return await fetchFormSchema(source);
|
|
49
|
+
} catch (error) {
|
|
50
|
+
lastError = error as Error;
|
|
51
|
+
|
|
52
|
+
if (attempt < maxRetries) {
|
|
53
|
+
// Wait before retrying (exponential backoff)
|
|
54
|
+
const delay = Math.min(1000 * Math.pow(2, attempt - 1), 5000);
|
|
55
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
throw lastError!;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ============================================================
|
|
65
|
+
// RECORD OPERATIONS
|
|
66
|
+
// ============================================================
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Fetch existing record for update operations
|
|
70
|
+
*/
|
|
71
|
+
export async function fetchRecord<T = any>(
|
|
72
|
+
source: string,
|
|
73
|
+
recordId: string
|
|
74
|
+
): Promise<T> {
|
|
75
|
+
try {
|
|
76
|
+
const record = await api<T>(source).get(recordId);
|
|
77
|
+
return record;
|
|
78
|
+
} catch (error) {
|
|
79
|
+
console.error(`Record fetch error for ${source}/${recordId}:`, error);
|
|
80
|
+
throw new Error(
|
|
81
|
+
`Failed to load record: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Submit form data (create or update)
|
|
88
|
+
*/
|
|
89
|
+
export async function submitFormData<T = any>(
|
|
90
|
+
source: string,
|
|
91
|
+
operation: FormOperation,
|
|
92
|
+
data: Partial<T>,
|
|
93
|
+
recordId?: string
|
|
94
|
+
): Promise<SubmissionResult> {
|
|
95
|
+
try {
|
|
96
|
+
let result;
|
|
97
|
+
|
|
98
|
+
if (operation === "create") {
|
|
99
|
+
result = await api<T>(source).create(data);
|
|
100
|
+
return {
|
|
101
|
+
success: true,
|
|
102
|
+
data: result,
|
|
103
|
+
recordId: result._id || recordId,
|
|
104
|
+
};
|
|
105
|
+
} else if (operation === "update") {
|
|
106
|
+
if (!recordId) {
|
|
107
|
+
throw new Error("Record ID is required for update operations");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
result = await api<T>(source).update(recordId, data);
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
data: result,
|
|
114
|
+
recordId: result._id || recordId,
|
|
115
|
+
};
|
|
116
|
+
} else {
|
|
117
|
+
throw new Error(`Unsupported operation: ${operation}`);
|
|
118
|
+
}
|
|
119
|
+
} catch (error) {
|
|
120
|
+
console.error(`Form submission error:`, error);
|
|
121
|
+
const submissionError = new Error(
|
|
122
|
+
`Failed to ${operation} record: ${error instanceof Error ? error.message : "Unknown error"}`
|
|
123
|
+
);
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
success: false,
|
|
127
|
+
error: submissionError,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// ============================================================
|
|
133
|
+
// REFERENCE DATA FETCHING
|
|
134
|
+
// ============================================================
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Fetch reference field data
|
|
138
|
+
*/
|
|
139
|
+
export async function fetchReferenceData(
|
|
140
|
+
businessObject: string,
|
|
141
|
+
fields: string[] = ["_id"],
|
|
142
|
+
filters?: any,
|
|
143
|
+
sort?: any
|
|
144
|
+
): Promise<any[]> {
|
|
145
|
+
try {
|
|
146
|
+
const listOptions: any = {};
|
|
147
|
+
|
|
148
|
+
// Add filters if provided
|
|
149
|
+
if (filters) {
|
|
150
|
+
listOptions.Filter = filters;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Add sorting if provided
|
|
154
|
+
if (sort) {
|
|
155
|
+
listOptions.Sort = sort;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Limit fields to what's needed
|
|
159
|
+
if (fields.length > 0) {
|
|
160
|
+
listOptions.Field = fields;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const response = await api(businessObject).list(listOptions);
|
|
164
|
+
return response.Data || [];
|
|
165
|
+
} catch (error) {
|
|
166
|
+
console.error(`Reference data fetch error for ${businessObject}:`, error);
|
|
167
|
+
// Don't throw, return empty array to allow form to continue working
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Fetch all reference data for a schema
|
|
174
|
+
*/
|
|
175
|
+
export async function fetchAllReferenceData(
|
|
176
|
+
referenceFields: Record<string, any>
|
|
177
|
+
): Promise<Record<string, any[]>> {
|
|
178
|
+
const referenceData: Record<string, any[]> = {};
|
|
179
|
+
|
|
180
|
+
// Fetch all reference data in parallel
|
|
181
|
+
const fetchPromises = Object.entries(referenceFields).map(
|
|
182
|
+
async ([fieldName, config]) => {
|
|
183
|
+
try {
|
|
184
|
+
const data = await fetchReferenceData(
|
|
185
|
+
config.businessObject,
|
|
186
|
+
config.fields,
|
|
187
|
+
config.filters,
|
|
188
|
+
config.sort
|
|
189
|
+
);
|
|
190
|
+
return [fieldName, data];
|
|
191
|
+
} catch (error) {
|
|
192
|
+
console.warn(`Failed to fetch reference data for ${fieldName}:`, error);
|
|
193
|
+
return [fieldName, []];
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
const results = await Promise.allSettled(fetchPromises);
|
|
199
|
+
|
|
200
|
+
results.forEach((result) => {
|
|
201
|
+
if (result.status === "fulfilled") {
|
|
202
|
+
const [fieldName, data] = result.value;
|
|
203
|
+
referenceData[fieldName as string] = Array.isArray(data) ? data : [];
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
return referenceData;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
// ============================================================
|
|
211
|
+
// VALIDATION HELPERS
|
|
212
|
+
// ============================================================
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* Validate form data before submission
|
|
216
|
+
*/
|
|
217
|
+
export function validateFormData<T>(
|
|
218
|
+
data: Partial<T>,
|
|
219
|
+
requiredFields: string[]
|
|
220
|
+
): { isValid: boolean; errors: string[] } {
|
|
221
|
+
const errors: string[] = [];
|
|
222
|
+
|
|
223
|
+
// Check required fields
|
|
224
|
+
for (const field of requiredFields) {
|
|
225
|
+
const value = data[field as keyof T];
|
|
226
|
+
if (value === undefined || value === null || value === "") {
|
|
227
|
+
errors.push(`${field} is required`);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
return {
|
|
232
|
+
isValid: errors.length === 0,
|
|
233
|
+
errors,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Clean form data before submission
|
|
239
|
+
* - For create: returns all non-computed, non-undefined fields
|
|
240
|
+
* - For update: returns only fields that changed from originalData
|
|
241
|
+
*/
|
|
242
|
+
export function cleanFormData<T>(
|
|
243
|
+
data: Partial<T>,
|
|
244
|
+
computedFields: string[],
|
|
245
|
+
operation: FormOperation = "create",
|
|
246
|
+
originalData?: Partial<T>
|
|
247
|
+
): Partial<T> {
|
|
248
|
+
const cleanedData: Partial<T> = {};
|
|
249
|
+
|
|
250
|
+
Object.keys(data).forEach((key) => {
|
|
251
|
+
const fieldKey = key as keyof T;
|
|
252
|
+
const value = data[fieldKey];
|
|
253
|
+
|
|
254
|
+
// Skip computed fields
|
|
255
|
+
if (computedFields.includes(key)) {
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Skip undefined values
|
|
260
|
+
if (value === undefined) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// For create: include all non-computed, non-undefined fields
|
|
265
|
+
if (operation === "create") {
|
|
266
|
+
cleanedData[fieldKey] = value;
|
|
267
|
+
return;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// For update: only include fields that changed from original
|
|
271
|
+
if (operation === "update") {
|
|
272
|
+
if (!originalData) {
|
|
273
|
+
// No original data to compare - include the field
|
|
274
|
+
cleanedData[fieldKey] = value;
|
|
275
|
+
return;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const originalValue = originalData[fieldKey];
|
|
279
|
+
const hasChanged =
|
|
280
|
+
JSON.stringify(value) !== JSON.stringify(originalValue);
|
|
281
|
+
|
|
282
|
+
if (hasChanged) {
|
|
283
|
+
cleanedData[fieldKey] = value;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
return cleanedData;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ============================================================
|
|
292
|
+
// ERROR HANDLING
|
|
293
|
+
// ============================================================
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Parse API error response
|
|
297
|
+
*/
|
|
298
|
+
export function parseApiError(error: any): string {
|
|
299
|
+
if (typeof error === "string") {
|
|
300
|
+
return error;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (error?.message) {
|
|
304
|
+
return error.message;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (error?.response?.data?.message) {
|
|
308
|
+
return error.response.data.message;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
if (error?.response?.data?.error) {
|
|
312
|
+
return error.response.data.error;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return "An unexpected error occurred";
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Check if error is a network error
|
|
320
|
+
*/
|
|
321
|
+
export function isNetworkError(error: any): boolean {
|
|
322
|
+
return (
|
|
323
|
+
!navigator.onLine ||
|
|
324
|
+
error?.code === "NETWORK_ERROR" ||
|
|
325
|
+
error?.message?.toLowerCase().includes("network") ||
|
|
326
|
+
error?.message?.toLowerCase().includes("fetch")
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Check if error is a validation error
|
|
332
|
+
*/
|
|
333
|
+
export function isValidationError(error: any): boolean {
|
|
334
|
+
return (
|
|
335
|
+
error?.status === 400 ||
|
|
336
|
+
error?.response?.status === 400 ||
|
|
337
|
+
error?.message?.toLowerCase().includes("validation") ||
|
|
338
|
+
error?.message?.toLowerCase().includes("invalid")
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
// ============================================================
|
|
343
|
+
// CACHE MANAGEMENT
|
|
344
|
+
// ============================================================
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* Simple in-memory cache for schemas and reference data
|
|
348
|
+
*/
|
|
349
|
+
const cache = new Map<string, { data: any; timestamp: number; ttl: number }>();
|
|
350
|
+
|
|
351
|
+
/**
|
|
352
|
+
* Cache data with TTL
|
|
353
|
+
*/
|
|
354
|
+
export function setCacheData(
|
|
355
|
+
key: string,
|
|
356
|
+
data: any,
|
|
357
|
+
ttlMinutes: number = 10
|
|
358
|
+
): void {
|
|
359
|
+
cache.set(key, {
|
|
360
|
+
data,
|
|
361
|
+
timestamp: Date.now(),
|
|
362
|
+
ttl: ttlMinutes * 60 * 1000,
|
|
363
|
+
});
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/**
|
|
367
|
+
* Get cached data if not expired
|
|
368
|
+
*/
|
|
369
|
+
export function getCacheData(key: string): any | null {
|
|
370
|
+
const item = cache.get(key);
|
|
371
|
+
|
|
372
|
+
if (!item) {
|
|
373
|
+
return null;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
if (Date.now() - item.timestamp > item.ttl) {
|
|
377
|
+
cache.delete(key);
|
|
378
|
+
return null;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
return item.data;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Clear cache
|
|
386
|
+
*/
|
|
387
|
+
export function clearCache(keyPrefix?: string): void {
|
|
388
|
+
if (keyPrefix) {
|
|
389
|
+
const keysToDelete = Array.from(cache.keys()).filter((key) =>
|
|
390
|
+
key.startsWith(keyPrefix)
|
|
391
|
+
);
|
|
392
|
+
keysToDelete.forEach((key) => cache.delete(key));
|
|
393
|
+
} else {
|
|
394
|
+
cache.clear();
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// ============================================================
|
|
399
|
+
// CACHED API FUNCTIONS
|
|
400
|
+
// ============================================================
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Fetch schema with caching
|
|
404
|
+
*/
|
|
405
|
+
export async function fetchFormSchemaWithCache(
|
|
406
|
+
source: string
|
|
407
|
+
): Promise<BackendSchema> {
|
|
408
|
+
const cacheKey = `schema:${source}`;
|
|
409
|
+
const cached = getCacheData(cacheKey);
|
|
410
|
+
|
|
411
|
+
if (cached) {
|
|
412
|
+
return cached;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
const schema = await fetchFormSchemaWithRetry(source);
|
|
416
|
+
setCacheData(cacheKey, schema, 30); // Cache for 30 minutes
|
|
417
|
+
|
|
418
|
+
return schema;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Fetch reference data with caching
|
|
423
|
+
*/
|
|
424
|
+
export async function fetchReferenceDataWithCache(
|
|
425
|
+
businessObject: string,
|
|
426
|
+
fields: string[] = ["_id"],
|
|
427
|
+
filters?: any,
|
|
428
|
+
sort?: any
|
|
429
|
+
): Promise<any[]> {
|
|
430
|
+
const cacheKey = `reference:${businessObject}:${JSON.stringify({ fields, filters, sort })}`;
|
|
431
|
+
const cached = getCacheData(cacheKey);
|
|
432
|
+
|
|
433
|
+
if (cached) {
|
|
434
|
+
return cached;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
const data = await fetchReferenceData(businessObject, fields, filters, sort);
|
|
438
|
+
setCacheData(cacheKey, data, 5); // Cache for 5 minutes
|
|
439
|
+
|
|
440
|
+
return data;
|
|
441
|
+
}
|