@ram_28/kf-ai-sdk 1.0.9 → 1.0.10
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/dist/components/hooks/useForm/types.d.ts +44 -19
- package/dist/components/hooks/useForm/types.d.ts.map +1 -1
- package/dist/components/hooks/useForm/useForm.d.ts.map +1 -1
- package/dist/index.cjs +12 -12
- package/dist/index.mjs +1767 -1746
- package/package.json +1 -1
- package/sdk/components/hooks/useForm/types.ts +49 -28
- package/sdk/components/hooks/useForm/useForm.ts +104 -91
package/package.json
CHANGED
|
@@ -6,8 +6,6 @@ import type {
|
|
|
6
6
|
UseFormRegister,
|
|
7
7
|
Mode,
|
|
8
8
|
FieldValues as RHFFieldValues,
|
|
9
|
-
SubmitHandler,
|
|
10
|
-
SubmitErrorHandler,
|
|
11
9
|
FieldErrors,
|
|
12
10
|
Path,
|
|
13
11
|
PathValue,
|
|
@@ -21,15 +19,23 @@ import type {
|
|
|
21
19
|
// ============================================================
|
|
22
20
|
|
|
23
21
|
/**
|
|
24
|
-
*
|
|
25
|
-
*
|
|
22
|
+
* HandleSubmit follows React Hook Form's signature pattern
|
|
23
|
+
*
|
|
24
|
+
* - onSuccess: Called with the API response data on successful submission
|
|
25
|
+
* - onError: Called with either FieldErrors (validation failed) or Error (API failed)
|
|
26
|
+
*
|
|
27
|
+
* Internal flow:
|
|
28
|
+
* 1. RHF validation + Cross-field validation → FAILS → onError(fieldErrors)
|
|
29
|
+
* 2. Clean data & call API → FAILS → onError(apiError)
|
|
30
|
+
* 3. SUCCESS → onSuccess(responseData)
|
|
26
31
|
*/
|
|
27
|
-
export type
|
|
28
|
-
(
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
32
|
+
export type HandleSubmit<T extends RHFFieldValues> = (
|
|
33
|
+
onSuccess?: (data: T, e?: React.BaseSyntheticEvent) => void | Promise<void>,
|
|
34
|
+
onError?: (
|
|
35
|
+
error: FieldErrors<T> | Error,
|
|
36
|
+
e?: React.BaseSyntheticEvent
|
|
37
|
+
) => void | Promise<void>
|
|
38
|
+
) => (e?: React.BaseSyntheticEvent) => Promise<void>;
|
|
33
39
|
|
|
34
40
|
// ============================================================
|
|
35
41
|
// EXPRESSION TREE TYPES
|
|
@@ -276,18 +282,9 @@ export interface UseFormOptions<
|
|
|
276
282
|
/** User role for permission enforcement */
|
|
277
283
|
userRole?: string;
|
|
278
284
|
|
|
279
|
-
/**
|
|
280
|
-
onSuccess?: (data: T) => void;
|
|
281
|
-
|
|
282
|
-
/** Error callback */
|
|
283
|
-
onError?: (error: Error) => void;
|
|
284
|
-
|
|
285
|
-
/** Schema load error callback */
|
|
285
|
+
/** Schema load error callback (separate concern from form submission) */
|
|
286
286
|
onSchemaError?: (error: Error) => void;
|
|
287
287
|
|
|
288
|
-
/** Submit error callback */
|
|
289
|
-
onSubmitError?: (error: Error) => void;
|
|
290
|
-
|
|
291
288
|
/** Skip schema fetching (use for testing) */
|
|
292
289
|
skipSchemaFetch?: boolean;
|
|
293
290
|
|
|
@@ -448,8 +445,38 @@ export interface UseFormReturn<
|
|
|
448
445
|
options?: RegisterOptions<T, K>
|
|
449
446
|
) => ReturnType<UseFormRegister<T>>;
|
|
450
447
|
|
|
451
|
-
/**
|
|
452
|
-
|
|
448
|
+
/**
|
|
449
|
+
* Handle form submission with optional callbacks
|
|
450
|
+
*
|
|
451
|
+
* @example
|
|
452
|
+
* // Basic usage - no callbacks
|
|
453
|
+
* <form onSubmit={form.handleSubmit()}>
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* // With success callback
|
|
457
|
+
* <form onSubmit={form.handleSubmit((data) => {
|
|
458
|
+
* toast.success("Saved!");
|
|
459
|
+
* navigate("/products/" + data._id);
|
|
460
|
+
* })}>
|
|
461
|
+
*
|
|
462
|
+
* @example
|
|
463
|
+
* // With both callbacks
|
|
464
|
+
* <form onSubmit={form.handleSubmit(
|
|
465
|
+
* (data) => toast.success("Saved!"),
|
|
466
|
+
* (error) => {
|
|
467
|
+
* if (error instanceof Error) {
|
|
468
|
+
* toast.error(error.message); // API error
|
|
469
|
+
* } else {
|
|
470
|
+
* toast.error("Please fix the form errors"); // Validation errors
|
|
471
|
+
* }
|
|
472
|
+
* }
|
|
473
|
+
* )}>
|
|
474
|
+
*
|
|
475
|
+
* @example
|
|
476
|
+
* // Programmatic submission
|
|
477
|
+
* await form.handleSubmit(onSuccess, onError)();
|
|
478
|
+
*/
|
|
479
|
+
handleSubmit: HandleSubmit<T>;
|
|
453
480
|
|
|
454
481
|
/** Watch field values with strict typing */
|
|
455
482
|
watch: <K extends Path<T> | readonly Path<T>[]>(
|
|
@@ -516,9 +543,6 @@ export interface UseFormReturn<
|
|
|
516
543
|
/** Schema fetch error */
|
|
517
544
|
loadError: Error | null;
|
|
518
545
|
|
|
519
|
-
/** Form submission error */
|
|
520
|
-
submitError: Error | null;
|
|
521
|
-
|
|
522
546
|
/** Any error active */
|
|
523
547
|
hasError: boolean;
|
|
524
548
|
|
|
@@ -561,9 +585,6 @@ export interface UseFormReturn<
|
|
|
561
585
|
// OPERATIONS
|
|
562
586
|
// ============================================================
|
|
563
587
|
|
|
564
|
-
/** Submit form manually */
|
|
565
|
-
submit: () => Promise<void>;
|
|
566
|
-
|
|
567
588
|
/** Refresh schema */
|
|
568
589
|
refreshSchema: () => Promise<void>;
|
|
569
590
|
|
|
@@ -49,10 +49,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
49
49
|
mode = "onBlur", // Validation mode - controls when errors are shown (see types.ts for details)
|
|
50
50
|
enabled = true,
|
|
51
51
|
userRole,
|
|
52
|
-
onSuccess,
|
|
53
|
-
onError,
|
|
54
52
|
onSchemaError,
|
|
55
|
-
onSubmitError,
|
|
56
53
|
skipSchemaFetch = false,
|
|
57
54
|
schema: manualSchema,
|
|
58
55
|
draftOnEveryChange = false,
|
|
@@ -65,7 +62,6 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
65
62
|
const [schemaConfig, setSchemaConfig] =
|
|
66
63
|
useState<FormSchemaConfig | null>(null);
|
|
67
64
|
const [referenceData, setReferenceData] = useState<Record<string, any[]>>({});
|
|
68
|
-
const [submitError, setSubmitError] = useState<Error | null>(null);
|
|
69
65
|
const [isSubmitting, setIsSubmitting] = useState(false);
|
|
70
66
|
const [lastFormValues] = useState<Partial<T>>({});
|
|
71
67
|
|
|
@@ -77,25 +73,10 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
77
73
|
// This allows us to detect changes since the last draft, not since form init
|
|
78
74
|
const lastSyncedValuesRef = useRef<Partial<T> | null>(null);
|
|
79
75
|
|
|
80
|
-
// Stable callback
|
|
81
|
-
const onSuccessRef = useRef(onSuccess);
|
|
82
|
-
const onErrorRef = useRef(onError);
|
|
83
|
-
const onSubmitErrorRef = useRef(onSubmitError);
|
|
76
|
+
// Stable callback ref to prevent dependency loops
|
|
84
77
|
const onSchemaErrorRef = useRef(onSchemaError);
|
|
85
78
|
|
|
86
|
-
// Update
|
|
87
|
-
useEffect(() => {
|
|
88
|
-
onSuccessRef.current = onSuccess;
|
|
89
|
-
}, [onSuccess]);
|
|
90
|
-
|
|
91
|
-
useEffect(() => {
|
|
92
|
-
onErrorRef.current = onError;
|
|
93
|
-
}, [onError]);
|
|
94
|
-
|
|
95
|
-
useEffect(() => {
|
|
96
|
-
onSubmitErrorRef.current = onSubmitError;
|
|
97
|
-
}, [onSubmitError]);
|
|
98
|
-
|
|
79
|
+
// Update ref when callback changes
|
|
99
80
|
useEffect(() => {
|
|
100
81
|
onSchemaErrorRef.current = onSchemaError;
|
|
101
82
|
}, [onSchemaError]);
|
|
@@ -201,19 +182,13 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
201
182
|
}
|
|
202
183
|
}, [schema, userRole]); // Removed onSchemaError - using ref instead
|
|
203
184
|
|
|
204
|
-
// Handle schema
|
|
185
|
+
// Handle schema error
|
|
205
186
|
useEffect(() => {
|
|
206
187
|
if (schemaError) {
|
|
207
188
|
onSchemaErrorRef.current?.(schemaError);
|
|
208
189
|
}
|
|
209
190
|
}, [schemaError]);
|
|
210
191
|
|
|
211
|
-
useEffect(() => {
|
|
212
|
-
if (recordError) {
|
|
213
|
-
onErrorRef.current?.(recordError);
|
|
214
|
-
}
|
|
215
|
-
}, [recordError]);
|
|
216
|
-
|
|
217
192
|
// ============================================================
|
|
218
193
|
// COMPUTED FIELD DEPENDENCY TRACKING AND OPTIMIZATION
|
|
219
194
|
// ============================================================
|
|
@@ -495,76 +470,117 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
495
470
|
]);
|
|
496
471
|
|
|
497
472
|
// ============================================================
|
|
498
|
-
//
|
|
473
|
+
// HANDLE SUBMIT - RHF-style API with internal submission
|
|
499
474
|
// ============================================================
|
|
500
475
|
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
476
|
+
/**
|
|
477
|
+
* handleSubmit follows React Hook Form's signature pattern:
|
|
478
|
+
*
|
|
479
|
+
* handleSubmit(onSuccess?, onError?) => (e?) => Promise<void>
|
|
480
|
+
*
|
|
481
|
+
* Internal flow:
|
|
482
|
+
* 1. RHF validation + Cross-field validation → FAILS → onError(fieldErrors)
|
|
483
|
+
* 2. Clean data & call API → FAILS → onError(apiError)
|
|
484
|
+
* 3. SUCCESS → onSuccess(responseData)
|
|
485
|
+
*/
|
|
486
|
+
const handleSubmit = useCallback(
|
|
487
|
+
(
|
|
488
|
+
onSuccess?: (data: T, e?: React.BaseSyntheticEvent) => void | Promise<void>,
|
|
489
|
+
onError?: (
|
|
490
|
+
error: import("react-hook-form").FieldErrors<T> | Error,
|
|
491
|
+
e?: React.BaseSyntheticEvent
|
|
492
|
+
) => void | Promise<void>
|
|
493
|
+
) => {
|
|
494
|
+
return rhfForm.handleSubmit(
|
|
495
|
+
// RHF onValid handler - validation passed, now do cross-field + API
|
|
496
|
+
async (data, event) => {
|
|
497
|
+
if (!schemaConfig) {
|
|
498
|
+
const error = new Error("Schema not loaded");
|
|
499
|
+
onError?.(error, event);
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
508
502
|
|
|
509
|
-
|
|
510
|
-
// Validate form including cross-field validation
|
|
511
|
-
const isValid = await validateForm();
|
|
512
|
-
if (!isValid) {
|
|
513
|
-
throw new Error("Form validation failed");
|
|
514
|
-
}
|
|
503
|
+
setIsSubmitting(true);
|
|
515
504
|
|
|
516
|
-
|
|
505
|
+
try {
|
|
506
|
+
// Cross-field validation
|
|
507
|
+
const transformedRules = schemaConfig.crossFieldValidation.map(
|
|
508
|
+
(rule) => ({
|
|
509
|
+
Id: rule.Id,
|
|
510
|
+
Condition: { ExpressionTree: rule.ExpressionTree },
|
|
511
|
+
Message: rule.Message || `Validation failed for ${rule.Name}`,
|
|
512
|
+
})
|
|
513
|
+
);
|
|
517
514
|
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
schemaConfig.computedFields,
|
|
524
|
-
operation,
|
|
525
|
-
recordData as Partial<T> | undefined
|
|
526
|
-
);
|
|
515
|
+
const crossFieldErrors = validateCrossField(
|
|
516
|
+
transformedRules,
|
|
517
|
+
data as any,
|
|
518
|
+
referenceData
|
|
519
|
+
);
|
|
527
520
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
521
|
+
if (crossFieldErrors.length > 0) {
|
|
522
|
+
// Set cross-field errors in form state
|
|
523
|
+
crossFieldErrors.forEach((error, index) => {
|
|
524
|
+
rhfForm.setError(`root.crossField${index}` as any, {
|
|
525
|
+
type: "validate",
|
|
526
|
+
message: error.message,
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
// Call onError with current form errors
|
|
530
|
+
onError?.(rhfForm.formState.errors, event);
|
|
531
|
+
return;
|
|
532
|
+
}
|
|
535
533
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
534
|
+
// Clean data for submission
|
|
535
|
+
const cleanedData = cleanFormData(
|
|
536
|
+
data as any,
|
|
537
|
+
schemaConfig.computedFields,
|
|
538
|
+
operation,
|
|
539
|
+
recordData as Partial<T> | undefined
|
|
540
|
+
);
|
|
539
541
|
|
|
540
|
-
|
|
541
|
-
|
|
542
|
+
// Submit data to API
|
|
543
|
+
const result = await submitFormData<T>(
|
|
544
|
+
source,
|
|
545
|
+
operation,
|
|
546
|
+
cleanedData,
|
|
547
|
+
recordId
|
|
548
|
+
);
|
|
542
549
|
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
}
|
|
547
|
-
} catch (error) {
|
|
548
|
-
const submitError = error as Error;
|
|
549
|
-
setSubmitError(submitError);
|
|
550
|
-
onSubmitErrorRef.current?.(submitError);
|
|
551
|
-
onErrorRef.current?.(submitError);
|
|
552
|
-
throw error;
|
|
553
|
-
} finally {
|
|
554
|
-
setIsSubmitting(false);
|
|
555
|
-
}
|
|
556
|
-
}, [schemaConfig, validateForm, rhfForm, source, operation, recordId, recordData]);
|
|
550
|
+
if (!result.success) {
|
|
551
|
+
throw result.error || new Error("Submission failed");
|
|
552
|
+
}
|
|
557
553
|
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
554
|
+
// Reset form for create operations
|
|
555
|
+
if (operation === "create") {
|
|
556
|
+
rhfForm.reset();
|
|
557
|
+
}
|
|
561
558
|
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
559
|
+
// Success callback with response data
|
|
560
|
+
await onSuccess?.(result.data || data, event);
|
|
561
|
+
} catch (error) {
|
|
562
|
+
// API error - call onError with Error object
|
|
563
|
+
onError?.(error as Error, event);
|
|
564
|
+
} finally {
|
|
565
|
+
setIsSubmitting(false);
|
|
566
|
+
}
|
|
567
|
+
},
|
|
568
|
+
// RHF onInvalid handler - validation failed
|
|
569
|
+
(errors, event) => {
|
|
570
|
+
onError?.(errors, event);
|
|
571
|
+
}
|
|
572
|
+
);
|
|
573
|
+
},
|
|
574
|
+
[
|
|
575
|
+
rhfForm,
|
|
576
|
+
schemaConfig,
|
|
577
|
+
referenceData,
|
|
578
|
+
source,
|
|
579
|
+
operation,
|
|
580
|
+
recordId,
|
|
581
|
+
recordData,
|
|
582
|
+
]
|
|
583
|
+
);
|
|
568
584
|
|
|
569
585
|
// ============================================================
|
|
570
586
|
// FIELD HELPERS
|
|
@@ -623,7 +639,6 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
623
639
|
|
|
624
640
|
const clearErrors = useCallback((): void => {
|
|
625
641
|
rhfForm.clearErrors();
|
|
626
|
-
setSubmitError(null);
|
|
627
642
|
}, [rhfForm]);
|
|
628
643
|
|
|
629
644
|
// ============================================================
|
|
@@ -634,7 +649,7 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
634
649
|
isLoadingSchema || (operation === "update" && isLoadingRecord);
|
|
635
650
|
const isLoading = isLoadingInitialData || isSubmitting;
|
|
636
651
|
const loadError = schemaError || recordError;
|
|
637
|
-
const hasError = !!
|
|
652
|
+
const hasError = !!loadError;
|
|
638
653
|
|
|
639
654
|
const computedFields = useMemo<Array<keyof T>>(
|
|
640
655
|
() => (schemaConfig?.computedFields as Array<keyof T>) || [],
|
|
@@ -796,7 +811,6 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
796
811
|
|
|
797
812
|
// Error handling
|
|
798
813
|
loadError: loadError as Error | null,
|
|
799
|
-
submitError,
|
|
800
814
|
hasError,
|
|
801
815
|
|
|
802
816
|
// Schema information
|
|
@@ -813,7 +827,6 @@ export function useForm<T extends Record<string, any> = Record<string, any>>(
|
|
|
813
827
|
isFieldComputed,
|
|
814
828
|
|
|
815
829
|
// Operations
|
|
816
|
-
submit,
|
|
817
830
|
refreshSchema,
|
|
818
831
|
validateForm,
|
|
819
832
|
clearErrors,
|