cogsbox-state 0.5.459 → 0.5.460
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/CogsState.d.ts +21 -10
- package/dist/CogsState.d.ts.map +1 -1
- package/dist/CogsState.jsx +1218 -1221
- package/dist/CogsState.jsx.map +1 -1
- package/dist/TRPCValidationLink.d.ts.map +1 -1
- package/dist/TRPCValidationLink.js.map +1 -1
- package/dist/store.d.ts +0 -4
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +78 -133
- package/dist/store.js.map +1 -1
- package/package.json +1 -1
- package/src/CogsState.tsx +132 -232
- package/src/TRPCValidationLink.ts +1 -0
- package/src/store.ts +0 -93
package/src/CogsState.tsx
CHANGED
|
@@ -251,7 +251,8 @@ export type ValidationError = {
|
|
|
251
251
|
};
|
|
252
252
|
type EffectFunction<T, R> = (state: T, deps: any[]) => R;
|
|
253
253
|
export type EndType<T, IsArrayElement = false> = {
|
|
254
|
-
|
|
254
|
+
addZodValidation: (errors: ValidationError[]) => void;
|
|
255
|
+
clearZodValidation: (paths?: string[]) => void;
|
|
255
256
|
applyJsonPatch: (patches: any[]) => void;
|
|
256
257
|
update: UpdateType<T>;
|
|
257
258
|
_path: string[];
|
|
@@ -297,7 +298,7 @@ export type StateObject<T> = (T extends any[]
|
|
|
297
298
|
getAllFormRefs: () => Map<string, React.RefObject<any>>;
|
|
298
299
|
_componentId: string | null;
|
|
299
300
|
getComponents: () => ComponentsType;
|
|
300
|
-
|
|
301
|
+
|
|
301
302
|
_initialState: T;
|
|
302
303
|
updateInitialState: (newState: T | null) => {
|
|
303
304
|
fetchId: (field: keyof T) => string | number;
|
|
@@ -375,12 +376,23 @@ type ValidationOptionsType = {
|
|
|
375
376
|
|
|
376
377
|
onBlur?: boolean;
|
|
377
378
|
};
|
|
379
|
+
type UseSyncType<T> = (state: T, a: SyncOptionsType<any>) => SyncApi;
|
|
380
|
+
type SyncOptionsType<TApiParams> = {
|
|
381
|
+
apiParams: TApiParams;
|
|
382
|
+
stateKey?: string;
|
|
383
|
+
stateRoom:
|
|
384
|
+
| number
|
|
385
|
+
| string
|
|
386
|
+
| (({ clientId }: { clientId: string }) => string | null);
|
|
387
|
+
connect?: boolean;
|
|
388
|
+
inMemoryState?: boolean;
|
|
389
|
+
};
|
|
378
390
|
export type OptionsType<T extends unknown = unknown, TApiParams = never> = {
|
|
379
391
|
log?: boolean;
|
|
380
392
|
componentId?: string;
|
|
381
|
-
|
|
382
|
-
validation?: ValidationOptionsType;
|
|
393
|
+
syncOptions?: SyncOptionsType<TApiParams>;
|
|
383
394
|
|
|
395
|
+
validation?: ValidationOptionsType;
|
|
384
396
|
serverState?: {
|
|
385
397
|
id?: string | number;
|
|
386
398
|
data?: T;
|
|
@@ -390,9 +402,10 @@ export type OptionsType<T extends unknown = unknown, TApiParams = never> = {
|
|
|
390
402
|
| boolean
|
|
391
403
|
| {
|
|
392
404
|
strategy: 'append' | 'prepend' | 'diff';
|
|
393
|
-
key?: string;
|
|
405
|
+
key?: string;
|
|
394
406
|
};
|
|
395
407
|
};
|
|
408
|
+
|
|
396
409
|
sync?: {
|
|
397
410
|
action: (state: T) => Promise<{
|
|
398
411
|
success: boolean;
|
|
@@ -481,7 +494,6 @@ function setAndMergeOptions(stateKey: string, newOptions: OptionsType<any>) {
|
|
|
481
494
|
...newOptions,
|
|
482
495
|
});
|
|
483
496
|
}
|
|
484
|
-
|
|
485
497
|
function setOptions<StateKey, Opt>({
|
|
486
498
|
stateKey,
|
|
487
499
|
options,
|
|
@@ -525,10 +537,19 @@ function setOptions<StateKey, Opt>({
|
|
|
525
537
|
}
|
|
526
538
|
}
|
|
527
539
|
|
|
540
|
+
// Always preserve syncOptions if it exists in mergedOptions but not in options
|
|
541
|
+
if (
|
|
542
|
+
mergedOptions.syncOptions &&
|
|
543
|
+
(!options || !options.hasOwnProperty('syncOptions'))
|
|
544
|
+
) {
|
|
545
|
+
needToAdd = true;
|
|
546
|
+
}
|
|
547
|
+
|
|
528
548
|
if (needToAdd) {
|
|
529
549
|
setInitialStateOptions(stateKey as string, mergedOptions);
|
|
530
550
|
}
|
|
531
551
|
}
|
|
552
|
+
|
|
532
553
|
export function addStateOptions<T extends unknown>(
|
|
533
554
|
initialState: T,
|
|
534
555
|
{ formElements, validation }: OptionsType<T>
|
|
@@ -551,11 +572,12 @@ export const createCogsState = <State extends Record<StateKeys, unknown>>(
|
|
|
551
572
|
validation?: ValidationOptionsType;
|
|
552
573
|
__fromSyncSchema?: boolean;
|
|
553
574
|
__syncNotifications?: Record<string, Function>;
|
|
554
|
-
__apiParamsMap?: Record<string, any>;
|
|
575
|
+
__apiParamsMap?: Record<string, any>;
|
|
576
|
+
__useSync?: UseSyncType<State>;
|
|
555
577
|
}
|
|
556
578
|
) => {
|
|
557
579
|
let newInitialState = initialState;
|
|
558
|
-
|
|
580
|
+
console.log('optsc', opt?.__useSync);
|
|
559
581
|
const [statePart, initialOptionsPart] =
|
|
560
582
|
transformStateFunc<State>(newInitialState);
|
|
561
583
|
|
|
@@ -626,17 +648,10 @@ export const createCogsState = <State extends Record<StateKeys, unknown>>(
|
|
|
626
648
|
options?: Prettify<OptionsType<(typeof statePart)[StateKey]>>
|
|
627
649
|
) => {
|
|
628
650
|
const [componentId] = useState(options?.componentId ?? uuidv4());
|
|
629
|
-
const apiParamsSchema = opt?.__apiParamsMap?.[stateKey as string];
|
|
630
|
-
|
|
631
|
-
// Merge apiParams into options
|
|
632
|
-
const enhancedOptions = {
|
|
633
|
-
...options,
|
|
634
|
-
apiParamsSchema, // Add the schema here
|
|
635
|
-
} as any;
|
|
636
651
|
|
|
637
652
|
setOptions({
|
|
638
653
|
stateKey,
|
|
639
|
-
options
|
|
654
|
+
options,
|
|
640
655
|
initialOptionsPart,
|
|
641
656
|
});
|
|
642
657
|
const thiState =
|
|
@@ -657,6 +672,8 @@ export const createCogsState = <State extends Record<StateKeys, unknown>>(
|
|
|
657
672
|
defaultState: options?.defaultState as any,
|
|
658
673
|
dependencies: options?.dependencies,
|
|
659
674
|
serverState: options?.serverState,
|
|
675
|
+
syncOptions: options?.syncOptions,
|
|
676
|
+
__useSync: opt?.__useSync as UseSyncType<(typeof statePart)[StateKey]>,
|
|
660
677
|
});
|
|
661
678
|
|
|
662
679
|
return updater;
|
|
@@ -682,17 +699,27 @@ export const createCogsState = <State extends Record<StateKeys, unknown>>(
|
|
|
682
699
|
type UseCogsStateHook<
|
|
683
700
|
T extends Record<string, any>,
|
|
684
701
|
TApiParamsMap extends Record<string, any> = Record<string, never>,
|
|
685
|
-
> = <StateKey extends keyof TransformedStateType<T
|
|
702
|
+
> = <StateKey extends keyof TransformedStateType<T> & string>(
|
|
686
703
|
stateKey: StateKey,
|
|
687
704
|
options?: Prettify<
|
|
688
|
-
OptionsType<TransformedStateType<T>[StateKey]> &
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
705
|
+
OptionsType<TransformedStateType<T>[StateKey], TApiParamsMap[StateKey]> &
|
|
706
|
+
// This is the conditional part that solves the problem
|
|
707
|
+
([keyof TApiParamsMap] extends [never]
|
|
708
|
+
? // If TApiParamsMap has NO keys, intersect with an empty object.
|
|
709
|
+
// This means `syncOptions` is NOT a valid property.
|
|
710
|
+
{}
|
|
711
|
+
: // If TApiParamsMap HAS keys, intersect with a type that REQUIRES `syncOptions`.
|
|
712
|
+
{
|
|
713
|
+
syncOptions: Prettify<
|
|
714
|
+
SyncOptionsType<
|
|
715
|
+
StateKey extends keyof TApiParamsMap
|
|
716
|
+
? TApiParamsMap[StateKey]
|
|
717
|
+
: never
|
|
718
|
+
>
|
|
719
|
+
>;
|
|
720
|
+
})
|
|
693
721
|
>
|
|
694
722
|
) => StateObject<TransformedStateType<T>[StateKey]>;
|
|
695
|
-
|
|
696
723
|
// Updated CogsApi type
|
|
697
724
|
type CogsApi<
|
|
698
725
|
T extends Record<string, any>,
|
|
@@ -704,9 +731,9 @@ type CogsApi<
|
|
|
704
731
|
type GetParamType<SchemaEntry> = SchemaEntry extends {
|
|
705
732
|
api?: { queryData?: { _paramType?: infer P } };
|
|
706
733
|
}
|
|
707
|
-
? P
|
|
708
|
-
: never;
|
|
709
|
-
|
|
734
|
+
? P
|
|
735
|
+
: never;
|
|
736
|
+
|
|
710
737
|
export function createCogsStateFromSync<
|
|
711
738
|
TSyncSchema extends {
|
|
712
739
|
schemas: Record<
|
|
@@ -722,7 +749,8 @@ export function createCogsStateFromSync<
|
|
|
722
749
|
notifications: Record<string, any>;
|
|
723
750
|
},
|
|
724
751
|
>(
|
|
725
|
-
syncSchema: TSyncSchema
|
|
752
|
+
syncSchema: TSyncSchema,
|
|
753
|
+
useSync: UseSyncType<any>
|
|
726
754
|
): CogsApi<
|
|
727
755
|
{
|
|
728
756
|
[K in keyof TSyncSchema['schemas']]: TSyncSchema['schemas'][K]['schemas']['defaultValues'];
|
|
@@ -748,20 +776,18 @@ export function createCogsStateFromSync<
|
|
|
748
776
|
}
|
|
749
777
|
}
|
|
750
778
|
|
|
751
|
-
// Pass the sync schema metadata to createCogsState
|
|
752
779
|
return createCogsState(initialState, {
|
|
753
780
|
__fromSyncSchema: true,
|
|
754
781
|
__syncNotifications: syncSchema.notifications,
|
|
755
782
|
__apiParamsMap: apiParamsMap,
|
|
783
|
+
__useSync: useSync,
|
|
756
784
|
}) as any;
|
|
757
785
|
}
|
|
758
786
|
const {
|
|
759
787
|
getInitialOptions,
|
|
760
|
-
|
|
788
|
+
|
|
761
789
|
setStateLog,
|
|
762
790
|
updateInitialStateGlobal,
|
|
763
|
-
addValidationError,
|
|
764
|
-
removeValidationError,
|
|
765
791
|
} = getGlobalStore.getState();
|
|
766
792
|
const saveToLocalStorage = <T,>(
|
|
767
793
|
state: T,
|
|
@@ -951,122 +977,6 @@ function markEntireStateAsServerSynced(
|
|
|
951
977
|
}
|
|
952
978
|
}
|
|
953
979
|
|
|
954
|
-
const _notifySubscribedComponents = (
|
|
955
|
-
stateKey: string,
|
|
956
|
-
path: string[],
|
|
957
|
-
updateType: 'update' | 'insert' | 'cut',
|
|
958
|
-
oldValue: any,
|
|
959
|
-
newValue: any
|
|
960
|
-
) => {
|
|
961
|
-
const store = getGlobalStore.getState();
|
|
962
|
-
const rootMeta = store.getShadowMetadata(stateKey, []);
|
|
963
|
-
if (!rootMeta?.components) {
|
|
964
|
-
return;
|
|
965
|
-
}
|
|
966
|
-
|
|
967
|
-
const notifiedComponents = new Set<string>();
|
|
968
|
-
const shadowMeta = store.getShadowMetadata(stateKey, path);
|
|
969
|
-
|
|
970
|
-
// --- PASS 1: Notify specific subscribers based on update type ---
|
|
971
|
-
|
|
972
|
-
if (updateType === 'update') {
|
|
973
|
-
if (shadowMeta?.pathComponents) {
|
|
974
|
-
shadowMeta.pathComponents.forEach((componentId) => {
|
|
975
|
-
if (notifiedComponents.has(componentId)) return;
|
|
976
|
-
const component = rootMeta.components?.get(componentId);
|
|
977
|
-
if (component) {
|
|
978
|
-
const reactiveTypes = Array.isArray(component.reactiveType)
|
|
979
|
-
? component.reactiveType
|
|
980
|
-
: [component.reactiveType || 'component'];
|
|
981
|
-
if (!reactiveTypes.includes('none')) {
|
|
982
|
-
component.forceUpdate();
|
|
983
|
-
notifiedComponents.add(componentId);
|
|
984
|
-
}
|
|
985
|
-
}
|
|
986
|
-
});
|
|
987
|
-
}
|
|
988
|
-
|
|
989
|
-
if (
|
|
990
|
-
newValue &&
|
|
991
|
-
typeof newValue === 'object' &&
|
|
992
|
-
!isArray(newValue) &&
|
|
993
|
-
oldValue &&
|
|
994
|
-
typeof oldValue === 'object' &&
|
|
995
|
-
!isArray(oldValue)
|
|
996
|
-
) {
|
|
997
|
-
const changedSubPaths = getDifferences(newValue, oldValue);
|
|
998
|
-
changedSubPaths.forEach((subPathString) => {
|
|
999
|
-
const subPath = subPathString.split('.');
|
|
1000
|
-
const fullSubPath = [...path, ...subPath];
|
|
1001
|
-
const subPathMeta = store.getShadowMetadata(stateKey, fullSubPath);
|
|
1002
|
-
if (subPathMeta?.pathComponents) {
|
|
1003
|
-
subPathMeta.pathComponents.forEach((componentId) => {
|
|
1004
|
-
if (notifiedComponents.has(componentId)) return;
|
|
1005
|
-
const component = rootMeta.components?.get(componentId);
|
|
1006
|
-
if (component) {
|
|
1007
|
-
const reactiveTypes = Array.isArray(component.reactiveType)
|
|
1008
|
-
? component.reactiveType
|
|
1009
|
-
: [component.reactiveType || 'component'];
|
|
1010
|
-
if (!reactiveTypes.includes('none')) {
|
|
1011
|
-
component.forceUpdate();
|
|
1012
|
-
notifiedComponents.add(componentId);
|
|
1013
|
-
}
|
|
1014
|
-
}
|
|
1015
|
-
});
|
|
1016
|
-
}
|
|
1017
|
-
});
|
|
1018
|
-
}
|
|
1019
|
-
} else if (updateType === 'insert' || updateType === 'cut') {
|
|
1020
|
-
const parentArrayPath = updateType === 'insert' ? path : path.slice(0, -1);
|
|
1021
|
-
const parentMeta = store.getShadowMetadata(stateKey, parentArrayPath);
|
|
1022
|
-
if (parentMeta?.pathComponents) {
|
|
1023
|
-
parentMeta.pathComponents.forEach((componentId) => {
|
|
1024
|
-
if (!notifiedComponents.has(componentId)) {
|
|
1025
|
-
const component = rootMeta.components?.get(componentId);
|
|
1026
|
-
if (component) {
|
|
1027
|
-
component.forceUpdate();
|
|
1028
|
-
notifiedComponents.add(componentId);
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
});
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
|
|
1035
|
-
// --- PASS 2: Notify global subscribers ('all', 'deps') ---
|
|
1036
|
-
|
|
1037
|
-
rootMeta.components.forEach((component, componentId) => {
|
|
1038
|
-
if (notifiedComponents.has(componentId)) {
|
|
1039
|
-
return;
|
|
1040
|
-
}
|
|
1041
|
-
|
|
1042
|
-
const reactiveTypes = Array.isArray(component.reactiveType)
|
|
1043
|
-
? component.reactiveType
|
|
1044
|
-
: [component.reactiveType || 'component'];
|
|
1045
|
-
|
|
1046
|
-
if (reactiveTypes.includes('all')) {
|
|
1047
|
-
component.forceUpdate();
|
|
1048
|
-
notifiedComponents.add(componentId);
|
|
1049
|
-
return;
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
if (reactiveTypes.includes('deps')) {
|
|
1053
|
-
if (component.depsFunction) {
|
|
1054
|
-
const currentState = store.getShadowValue(stateKey);
|
|
1055
|
-
const newDeps = component.depsFunction(currentState);
|
|
1056
|
-
let shouldUpdate = false;
|
|
1057
|
-
if (newDeps === true || !isDeepEqual(component.prevDeps, newDeps)) {
|
|
1058
|
-
if (Array.isArray(newDeps)) component.prevDeps = newDeps;
|
|
1059
|
-
shouldUpdate = true;
|
|
1060
|
-
}
|
|
1061
|
-
if (shouldUpdate) {
|
|
1062
|
-
component.forceUpdate();
|
|
1063
|
-
notifiedComponents.add(componentId);
|
|
1064
|
-
}
|
|
1065
|
-
}
|
|
1066
|
-
}
|
|
1067
|
-
});
|
|
1068
|
-
};
|
|
1069
|
-
|
|
1070
980
|
export function useCogsStateFn<TStateObject extends unknown>(
|
|
1071
981
|
stateObject: TStateObject,
|
|
1072
982
|
{
|
|
@@ -1081,12 +991,14 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1081
991
|
syncUpdate,
|
|
1082
992
|
dependencies,
|
|
1083
993
|
serverState,
|
|
1084
|
-
|
|
994
|
+
__useSync,
|
|
995
|
+
syncOptions,
|
|
1085
996
|
}: {
|
|
1086
997
|
stateKey?: string;
|
|
1087
998
|
componentId?: string;
|
|
1088
999
|
defaultState?: TStateObject;
|
|
1089
|
-
|
|
1000
|
+
__useSync?: UseSyncType<TStateObject>;
|
|
1001
|
+
syncOptions?: SyncOptionsType<any>;
|
|
1090
1002
|
} & OptionsType<TStateObject> = {}
|
|
1091
1003
|
) {
|
|
1092
1004
|
const [reactiveForce, forceUpdate] = useState({}); //this is the key to reactivity
|
|
@@ -1189,7 +1101,7 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1189
1101
|
.subscribeToPath(thisKey, (event) => {
|
|
1190
1102
|
if (event?.type === 'SERVER_STATE_UPDATE') {
|
|
1191
1103
|
const serverStateData = event.serverState;
|
|
1192
|
-
|
|
1104
|
+
console.log('SERVER_STATE_UPDATE', event);
|
|
1193
1105
|
if (
|
|
1194
1106
|
serverStateData?.status === 'success' &&
|
|
1195
1107
|
serverStateData.data !== undefined
|
|
@@ -1208,13 +1120,12 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1208
1120
|
.getState()
|
|
1209
1121
|
.getShadowValue(thisKey);
|
|
1210
1122
|
const incomingData = serverStateData.data;
|
|
1211
|
-
|
|
1212
1123
|
if (
|
|
1213
1124
|
mergeConfig &&
|
|
1214
1125
|
Array.isArray(currentState) &&
|
|
1215
1126
|
Array.isArray(incomingData)
|
|
1216
1127
|
) {
|
|
1217
|
-
const keyField = mergeConfig.key
|
|
1128
|
+
const keyField = mergeConfig.key;
|
|
1218
1129
|
const existingIds = new Set(
|
|
1219
1130
|
currentState.map((item: any) => item[keyField])
|
|
1220
1131
|
);
|
|
@@ -1656,11 +1567,7 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1656
1567
|
const newState = getGlobalStore.getState().getShadowValue(thisKey);
|
|
1657
1568
|
const rootMeta = getGlobalStore.getState().getShadowMetadata(thisKey, []);
|
|
1658
1569
|
const notifiedComponents = new Set<string>();
|
|
1659
|
-
|
|
1660
|
-
'rootMeta',
|
|
1661
|
-
thisKey,
|
|
1662
|
-
getGlobalStore.getState().shadowStateStore
|
|
1663
|
-
);
|
|
1570
|
+
|
|
1664
1571
|
if (!rootMeta?.components) {
|
|
1665
1572
|
return newState;
|
|
1666
1573
|
}
|
|
@@ -1895,9 +1802,14 @@ export function useCogsStateFn<TStateObject extends unknown>(
|
|
|
1895
1802
|
);
|
|
1896
1803
|
}, [thisKey, sessionId]);
|
|
1897
1804
|
|
|
1898
|
-
const cogsSyncFn =
|
|
1805
|
+
const cogsSyncFn = __useSync;
|
|
1806
|
+
const syncOpt = latestInitialOptionsRef.current?.syncOptions;
|
|
1807
|
+
|
|
1899
1808
|
if (cogsSyncFn) {
|
|
1900
|
-
syncApiRef.current = cogsSyncFn(
|
|
1809
|
+
syncApiRef.current = cogsSyncFn(
|
|
1810
|
+
updaterFinal as any,
|
|
1811
|
+
syncOpt ?? ({} as any)
|
|
1812
|
+
);
|
|
1901
1813
|
}
|
|
1902
1814
|
|
|
1903
1815
|
return updaterFinal;
|
|
@@ -2169,14 +2081,14 @@ function createProxyHandler<T>(
|
|
|
2169
2081
|
response.errors &&
|
|
2170
2082
|
validationKey
|
|
2171
2083
|
) {
|
|
2172
|
-
getGlobalStore.getState().removeValidationError(validationKey);
|
|
2173
|
-
response.errors.forEach((error) => {
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
});
|
|
2179
|
-
notifyComponents(stateKey);
|
|
2084
|
+
// getGlobalStore.getState().removeValidationError(validationKey);
|
|
2085
|
+
// response.errors.forEach((error) => {
|
|
2086
|
+
// const errorPath = [validationKey, ...error.path].join('.');
|
|
2087
|
+
// getGlobalStore
|
|
2088
|
+
// .getState()
|
|
2089
|
+
// .addValidationError(errorPath, error.message);
|
|
2090
|
+
// });
|
|
2091
|
+
// notifyComponents(stateKey);
|
|
2180
2092
|
}
|
|
2181
2093
|
|
|
2182
2094
|
if (response?.success) {
|
|
@@ -3566,18 +3478,61 @@ function createProxyHandler<T>(
|
|
|
3566
3478
|
return componentId;
|
|
3567
3479
|
}
|
|
3568
3480
|
if (path.length == 0) {
|
|
3569
|
-
if (prop === '
|
|
3570
|
-
return (
|
|
3481
|
+
if (prop === 'addZodValidation') {
|
|
3482
|
+
return (zodErrors: any[]) => {
|
|
3571
3483
|
const init = getGlobalStore
|
|
3572
3484
|
.getState()
|
|
3573
3485
|
.getInitialOptions(stateKey)?.validation;
|
|
3574
3486
|
if (!init?.key) throw new Error('Validation key not found');
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3487
|
+
|
|
3488
|
+
// For each error, set shadow metadata
|
|
3489
|
+
zodErrors.forEach((error) => {
|
|
3490
|
+
const currentMeta =
|
|
3491
|
+
getGlobalStore
|
|
3492
|
+
.getState()
|
|
3493
|
+
.getShadowMetadata(stateKey, error.path) || {};
|
|
3494
|
+
|
|
3495
|
+
getGlobalStore
|
|
3496
|
+
.getState()
|
|
3497
|
+
.setShadowMetadata(stateKey, error.path, {
|
|
3498
|
+
...currentMeta,
|
|
3499
|
+
validation: {
|
|
3500
|
+
status: 'VALIDATION_FAILED',
|
|
3501
|
+
message: error.message,
|
|
3502
|
+
validatedValue: undefined,
|
|
3503
|
+
},
|
|
3504
|
+
});
|
|
3505
|
+
getGlobalStore.getState().notifyPathSubscribers(error.path, {
|
|
3506
|
+
type: 'VALIDATION_FAILED',
|
|
3507
|
+
message: error.message,
|
|
3508
|
+
validatedValue: undefined,
|
|
3509
|
+
});
|
|
3579
3510
|
});
|
|
3580
|
-
|
|
3511
|
+
};
|
|
3512
|
+
}
|
|
3513
|
+
if (prop === 'clearZodValidation') {
|
|
3514
|
+
return (path?: string[]) => {
|
|
3515
|
+
// Clear specific paths
|
|
3516
|
+
if (!path) {
|
|
3517
|
+
throw new Error('clearZodValidation requires a path');
|
|
3518
|
+
return;
|
|
3519
|
+
}
|
|
3520
|
+
const currentMeta =
|
|
3521
|
+
getGlobalStore.getState().getShadowMetadata(stateKey, path) ||
|
|
3522
|
+
{};
|
|
3523
|
+
|
|
3524
|
+
if (currentMeta.validation) {
|
|
3525
|
+
getGlobalStore.getState().setShadowMetadata(stateKey, path, {
|
|
3526
|
+
...currentMeta,
|
|
3527
|
+
validation: undefined,
|
|
3528
|
+
});
|
|
3529
|
+
|
|
3530
|
+
getGlobalStore
|
|
3531
|
+
.getState()
|
|
3532
|
+
.notifyPathSubscribers([stateKey, ...path].join('.'), {
|
|
3533
|
+
type: 'VALIDATION_CLEARED',
|
|
3534
|
+
});
|
|
3535
|
+
}
|
|
3581
3536
|
};
|
|
3582
3537
|
}
|
|
3583
3538
|
if (prop === 'applyJsonPatch') {
|
|
@@ -3617,7 +3572,7 @@ function createProxyHandler<T>(
|
|
|
3617
3572
|
stateKey,
|
|
3618
3573
|
currentPath
|
|
3619
3574
|
);
|
|
3620
|
-
|
|
3575
|
+
|
|
3621
3576
|
if (pathMeta?.pathComponents) {
|
|
3622
3577
|
pathMeta.pathComponents.forEach((componentId) => {
|
|
3623
3578
|
if (!notifiedComponents.has(componentId)) {
|
|
@@ -3670,51 +3625,6 @@ function createProxyHandler<T>(
|
|
|
3670
3625
|
}
|
|
3671
3626
|
};
|
|
3672
3627
|
}
|
|
3673
|
-
if (prop === 'validateZodSchema') {
|
|
3674
|
-
return () => {
|
|
3675
|
-
const init = getGlobalStore
|
|
3676
|
-
.getState()
|
|
3677
|
-
.getInitialOptions(stateKey)?.validation;
|
|
3678
|
-
|
|
3679
|
-
// UPDATED: Select v4 schema, with a fallback to v3
|
|
3680
|
-
const zodSchema = init?.zodSchemaV4 || init?.zodSchemaV3;
|
|
3681
|
-
|
|
3682
|
-
if (!zodSchema || !init?.key) {
|
|
3683
|
-
throw new Error(
|
|
3684
|
-
'Zod schema (v3 or v4) or validation key not found'
|
|
3685
|
-
);
|
|
3686
|
-
}
|
|
3687
|
-
|
|
3688
|
-
removeValidationError(init.key);
|
|
3689
|
-
const thisObject = getGlobalStore
|
|
3690
|
-
.getState()
|
|
3691
|
-
.getShadowValue(stateKey);
|
|
3692
|
-
|
|
3693
|
-
// Use the selected schema for parsing
|
|
3694
|
-
const result = zodSchema.safeParse(thisObject);
|
|
3695
|
-
|
|
3696
|
-
if (!result.success) {
|
|
3697
|
-
// This logic already handles both v3 and v4 error types correctly
|
|
3698
|
-
if ('issues' in result.error) {
|
|
3699
|
-
// Zod v4 error
|
|
3700
|
-
result.error.issues.forEach((error) => {
|
|
3701
|
-
const fullErrorPath = [init.key, ...error.path].join('.');
|
|
3702
|
-
addValidationError(fullErrorPath, error.message);
|
|
3703
|
-
});
|
|
3704
|
-
} else {
|
|
3705
|
-
// Zod v3 error
|
|
3706
|
-
(result.error as any).errors.forEach((error: any) => {
|
|
3707
|
-
const fullErrorPath = [init.key, ...error.path].join('.');
|
|
3708
|
-
addValidationError(fullErrorPath, error.message);
|
|
3709
|
-
});
|
|
3710
|
-
}
|
|
3711
|
-
|
|
3712
|
-
notifyComponents(stateKey);
|
|
3713
|
-
return false;
|
|
3714
|
-
}
|
|
3715
|
-
return true;
|
|
3716
|
-
};
|
|
3717
|
-
}
|
|
3718
3628
|
|
|
3719
3629
|
if (prop === 'getComponents')
|
|
3720
3630
|
return () =>
|
|
@@ -3834,22 +3744,10 @@ function createProxyHandler<T>(
|
|
|
3834
3744
|
}
|
|
3835
3745
|
|
|
3836
3746
|
const baseObj = {
|
|
3837
|
-
removeValidation: (obj?: { validationKey?: string }) => {
|
|
3838
|
-
if (obj?.validationKey) {
|
|
3839
|
-
removeValidationError(obj.validationKey);
|
|
3840
|
-
}
|
|
3841
|
-
},
|
|
3842
3747
|
revertToInitialState: (obj?: { validationKey?: string }) => {
|
|
3843
3748
|
const init = getGlobalStore
|
|
3844
3749
|
.getState()
|
|
3845
3750
|
.getInitialOptions(stateKey)?.validation;
|
|
3846
|
-
if (init?.key) {
|
|
3847
|
-
removeValidationError(init.key);
|
|
3848
|
-
}
|
|
3849
|
-
|
|
3850
|
-
if (obj?.validationKey) {
|
|
3851
|
-
removeValidationError(obj.validationKey);
|
|
3852
|
-
}
|
|
3853
3751
|
|
|
3854
3752
|
const shadowMeta = getGlobalStore
|
|
3855
3753
|
.getState()
|
|
@@ -4486,6 +4384,7 @@ function FormElementWrapper({
|
|
|
4486
4384
|
validation: {
|
|
4487
4385
|
status: 'VALID_LIVE',
|
|
4488
4386
|
validatedValue: newValue,
|
|
4387
|
+
message: undefined,
|
|
4489
4388
|
},
|
|
4490
4389
|
});
|
|
4491
4390
|
}
|
|
@@ -4496,6 +4395,7 @@ function FormElementWrapper({
|
|
|
4496
4395
|
validation: {
|
|
4497
4396
|
status: 'VALID_LIVE',
|
|
4498
4397
|
validatedValue: newValue,
|
|
4398
|
+
message: undefined,
|
|
4499
4399
|
},
|
|
4500
4400
|
});
|
|
4501
4401
|
}
|
package/src/store.ts
CHANGED
|
@@ -251,10 +251,6 @@ export type CogsGlobalState = {
|
|
|
251
251
|
setInitialStateOptions: (key: string, value: OptionsType) => void;
|
|
252
252
|
|
|
253
253
|
// --- Validation ---
|
|
254
|
-
validationErrors: Map<string, string[]>;
|
|
255
|
-
addValidationError: (path: string, message: string) => void;
|
|
256
|
-
getValidationErrors: (path: string) => string[];
|
|
257
|
-
removeValidationError: (path: string) => void;
|
|
258
254
|
|
|
259
255
|
// --- Server Sync and Logging ---
|
|
260
256
|
|
|
@@ -863,95 +859,6 @@ export const getGlobalStore = create<CogsGlobalState>((set, get) => ({
|
|
|
863
859
|
});
|
|
864
860
|
},
|
|
865
861
|
|
|
866
|
-
addValidationError: (path, message) => {
|
|
867
|
-
set((prev) => {
|
|
868
|
-
const updatedErrors = new Map(prev.validationErrors);
|
|
869
|
-
const existingMessages = updatedErrors.get(path) || [];
|
|
870
|
-
console.log('addValidationError', path, message, existingMessages);
|
|
871
|
-
// Append the new message instead of replacing
|
|
872
|
-
updatedErrors.set(path, [...existingMessages, message]);
|
|
873
|
-
return { validationErrors: updatedErrors };
|
|
874
|
-
});
|
|
875
|
-
},
|
|
876
|
-
removeValidationError: (path) => {
|
|
877
|
-
set((prev) => {
|
|
878
|
-
const updatedErrors = new Map(prev.validationErrors);
|
|
879
|
-
|
|
880
|
-
let doSomething = false;
|
|
881
|
-
const pathArray = path.split('.');
|
|
882
|
-
Array.from(updatedErrors.keys()).forEach((key) => {
|
|
883
|
-
const keyArray = key.split('.');
|
|
884
|
-
if (keyArray.length >= pathArray.length) {
|
|
885
|
-
let match = true;
|
|
886
|
-
for (let i = 0; i < pathArray.length; i++) {
|
|
887
|
-
if (keyArray[i] !== pathArray[i]) {
|
|
888
|
-
match = false;
|
|
889
|
-
break;
|
|
890
|
-
}
|
|
891
|
-
}
|
|
892
|
-
|
|
893
|
-
if (match) {
|
|
894
|
-
doSomething = true;
|
|
895
|
-
updatedErrors.delete(key);
|
|
896
|
-
}
|
|
897
|
-
}
|
|
898
|
-
});
|
|
899
|
-
|
|
900
|
-
return doSomething ? { validationErrors: updatedErrors } : prev;
|
|
901
|
-
});
|
|
902
|
-
},
|
|
903
|
-
getValidationErrors: (path: string) => {
|
|
904
|
-
const errors: string[] = [];
|
|
905
|
-
const valErrors = get().validationErrors;
|
|
906
|
-
const pathArray = path.split('.');
|
|
907
|
-
|
|
908
|
-
// Helper to check if an index matches either a wildcard or is in an array of indices
|
|
909
|
-
const isIndexMatch = (pathSegment: string, keySegment: string) => {
|
|
910
|
-
if (pathSegment === '[*]') return true;
|
|
911
|
-
if (Array.isArray(pathSegment)) {
|
|
912
|
-
return pathSegment.includes(parseInt(keySegment));
|
|
913
|
-
}
|
|
914
|
-
return pathSegment === keySegment;
|
|
915
|
-
};
|
|
916
|
-
|
|
917
|
-
Array.from(valErrors.keys()).forEach((key) => {
|
|
918
|
-
const keyArray = key.split('.');
|
|
919
|
-
if (keyArray.length >= pathArray.length) {
|
|
920
|
-
let match = true;
|
|
921
|
-
for (let i = 0; i < pathArray.length; i++) {
|
|
922
|
-
const pathSegment = pathArray[i];
|
|
923
|
-
const keySegment = keyArray[i]!;
|
|
924
|
-
|
|
925
|
-
// If current path segment is a number or [*], we need special handling
|
|
926
|
-
if (pathSegment === '[*]' || Array.isArray(pathSegment)) {
|
|
927
|
-
// Key segment should be a number if we're using [*] or array indices
|
|
928
|
-
const keyIndex = parseInt(keySegment);
|
|
929
|
-
if (isNaN(keyIndex)) {
|
|
930
|
-
match = false;
|
|
931
|
-
break;
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
if (!isIndexMatch(pathSegment, keySegment)) {
|
|
935
|
-
match = false;
|
|
936
|
-
break;
|
|
937
|
-
}
|
|
938
|
-
} else if (pathSegment !== keySegment) {
|
|
939
|
-
match = false;
|
|
940
|
-
break;
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
if (match) {
|
|
945
|
-
const errorMessages = valErrors.get(key);
|
|
946
|
-
if (errorMessages) {
|
|
947
|
-
errors.push(...errorMessages);
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
}
|
|
951
|
-
});
|
|
952
|
-
|
|
953
|
-
return errors;
|
|
954
|
-
},
|
|
955
862
|
getInitialOptions: (key) => {
|
|
956
863
|
return get().initialStateOptions[key];
|
|
957
864
|
},
|