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/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
- addValidation: (errors: ValidationError[]) => void;
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
- validateZodSchema: () => void;
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
- cogsSync?: (stateObject: StateObject<T>) => SyncApi;
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; // For diff strategy - which field to use as unique identifier
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>; // Add this
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: enhancedOptions,
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
- apiParams?: StateKey extends keyof TApiParamsMap
690
- ? TApiParamsMap[StateKey]
691
- : never;
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 // If the full path exists, infer the parameter type P.
708
- : never; // Otherwise, the type is 'never', meaning no params.
709
- // Fixed createCogsStateFromSync return type
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
- getValidationErrors,
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
- apiParamsSchema,
994
+ __useSync,
995
+ syncOptions,
1085
996
  }: {
1086
997
  stateKey?: string;
1087
998
  componentId?: string;
1088
999
  defaultState?: TStateObject;
1089
- apiParamsSchema?: z.ZodObject<any>; // Add this type
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 || 'id';
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
- console.log(
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 = latestInitialOptionsRef.current?.cogsSync;
1805
+ const cogsSyncFn = __useSync;
1806
+ const syncOpt = latestInitialOptionsRef.current?.syncOptions;
1807
+
1899
1808
  if (cogsSyncFn) {
1900
- syncApiRef.current = cogsSyncFn(updaterFinal);
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
- const errorPath = [validationKey, ...error.path].join('.');
2175
- getGlobalStore
2176
- .getState()
2177
- .addValidationError(errorPath, error.message);
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 === 'addValidation') {
3570
- return (errors: ValidationError[]) => {
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
- removeValidationError(init.key);
3576
- errors.forEach((error) => {
3577
- const fullErrorPath = [init.key, ...error.path].join('.');
3578
- addValidationError(fullErrorPath, error.message);
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
- notifyComponents(stateKey);
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
- console.log('pathMeta', pathMeta);
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
  }
@@ -1,3 +1,4 @@
1
+ //@ts-nocheck
1
2
  import { observable } from '@trpc/server/observable';
2
3
  import type { AnyRouter } from '@trpc/server';
3
4
  import type { TRPCLink } from '@trpc/client';
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
  },