cogsbox-state 0.5.471 → 0.5.473

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.
Files changed (46) hide show
  1. package/README.md +2 -5
  2. package/dist/CogsState.d.ts +105 -79
  3. package/dist/CogsState.d.ts.map +1 -1
  4. package/dist/CogsState.jsx +1082 -987
  5. package/dist/CogsState.jsx.map +1 -1
  6. package/dist/Components.d.ts.map +1 -1
  7. package/dist/Components.jsx +293 -243
  8. package/dist/Components.jsx.map +1 -1
  9. package/dist/PluginRunner.d.ts +10 -0
  10. package/dist/PluginRunner.d.ts.map +1 -0
  11. package/dist/PluginRunner.jsx +128 -0
  12. package/dist/PluginRunner.jsx.map +1 -0
  13. package/dist/index.d.ts +2 -0
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +33 -26
  16. package/dist/index.js.map +1 -1
  17. package/dist/pluginStore.d.ts +43 -0
  18. package/dist/pluginStore.d.ts.map +1 -0
  19. package/dist/pluginStore.js +52 -0
  20. package/dist/pluginStore.js.map +1 -0
  21. package/dist/plugins.d.ts +1326 -0
  22. package/dist/plugins.d.ts.map +1 -0
  23. package/dist/plugins.js +76 -0
  24. package/dist/plugins.js.map +1 -0
  25. package/dist/store.d.ts +69 -26
  26. package/dist/store.d.ts.map +1 -1
  27. package/dist/store.js +436 -152
  28. package/dist/store.js.map +1 -1
  29. package/dist/utility.d.ts +1 -1
  30. package/dist/utility.d.ts.map +1 -1
  31. package/dist/utility.js +12 -12
  32. package/dist/utility.js.map +1 -1
  33. package/dist/validation.d.ts +7 -0
  34. package/dist/validation.d.ts.map +1 -0
  35. package/dist/validation.js +39 -0
  36. package/dist/validation.js.map +1 -0
  37. package/package.json +18 -13
  38. package/src/CogsState.tsx +719 -458
  39. package/src/Components.tsx +304 -180
  40. package/src/PluginRunner.tsx +208 -0
  41. package/src/index.ts +2 -0
  42. package/src/pluginStore.ts +159 -0
  43. package/src/plugins.ts +548 -0
  44. package/src/store.ts +881 -189
  45. package/src/utility.ts +31 -31
  46. package/src/validation.ts +84 -0
package/src/CogsState.tsx CHANGED
@@ -1,5 +1,6 @@
1
1
  'use client';
2
-
2
+ import { pluginStore } from './pluginStore';
3
+ import { FormWrapperParams, type CogsPlugin } from './plugins';
3
4
  import {
4
5
  createElement,
5
6
  startTransition,
@@ -15,10 +16,11 @@ import {
15
16
  } from 'react';
16
17
 
17
18
  import {
18
- getDifferences,
19
- isArray,
19
+ transformStateFunc,
20
20
  isFunction,
21
21
  type GenericObject,
22
+ isArray,
23
+ getDifferences,
22
24
  } from './utility.js';
23
25
  import {
24
26
  FormElementWrapper,
@@ -26,13 +28,13 @@ import {
26
28
  MemoizedCogsItemWrapper,
27
29
  ValidationWrapper,
28
30
  } from './Components.js';
29
- import { isDeepEqual, transformStateFunc } from './utility.js';
31
+ import { isDeepEqual } from './utility.js';
30
32
  import superjson from 'superjson';
31
33
  import { v4 as uuidv4 } from 'uuid';
32
34
 
33
35
  import {
34
- formRefStore,
35
36
  getGlobalStore,
37
+ updateShadowTypeInfo,
36
38
  ValidationError,
37
39
  ValidationSeverity,
38
40
  ValidationStatus,
@@ -44,7 +46,9 @@ import { Operation } from 'fast-json-patch';
44
46
  import * as z3 from 'zod/v3';
45
47
  import * as z4 from 'zod/v4';
46
48
 
47
- type Prettify<T> = T extends any ? { [K in keyof T]: T[K] } : never;
49
+ import { runValidation } from './validation';
50
+
51
+ export type Prettify<T> = T extends any ? { [K in keyof T]: T[K] } : never;
48
52
 
49
53
  export type VirtualViewOptions = {
50
54
  itemHeight?: number;
@@ -80,7 +84,11 @@ export type FormElementParams<T> = StateObject<T> & {
80
84
  HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
81
85
  >
82
86
  ) => void;
83
- onBlur?: () => void;
87
+ onBlur?: (
88
+ event: React.FocusEvent<
89
+ HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement
90
+ >
91
+ ) => void;
84
92
  };
85
93
  };
86
94
 
@@ -97,37 +105,7 @@ type CutFunctionType<T> = (
97
105
  ) => StateObject<T>;
98
106
 
99
107
  export type InferArrayElement<T> = T extends (infer U)[] ? U : never;
100
- type ArraySpecificPrototypeKeys =
101
- | 'concat'
102
- | 'copyWithin'
103
- | 'fill'
104
- | 'find'
105
- | 'findIndex'
106
- | 'flat'
107
- | 'flatMap'
108
- | 'includes'
109
- | 'indexOf'
110
- | 'join'
111
- | 'keys'
112
- | 'lastIndexOf'
113
- | 'map'
114
- | 'pop'
115
- | 'push'
116
- | 'reduce'
117
- | 'reduceRight'
118
- | 'reverse'
119
- | 'shift'
120
- | 'slice'
121
- | 'some'
122
- | 'sort'
123
- | 'splice'
124
- | 'unshift'
125
- | 'values'
126
- | 'entries'
127
- | 'every'
128
- | 'filter'
129
- | 'forEach'
130
- | 'with';
108
+
131
109
  export type StreamOptions<T, R = T> = {
132
110
  bufferSize?: number;
133
111
  flushInterval?: number;
@@ -158,6 +136,7 @@ export type ArrayEndType<TShape extends unknown> = {
158
136
  $_index: number;
159
137
  } & EndType<Prettify<InferArrayElement<TShape>>>;
160
138
  $insert: InsertType<Prettify<InferArrayElement<TShape>>>;
139
+ $insertMany: (payload: InferArrayElement<TShape>[]) => void;
161
140
  $cut: CutFunctionType<TShape>;
162
141
  $cutSelected: () => void;
163
142
  $cutByValue: (value: string | number | boolean) => void;
@@ -243,8 +222,20 @@ export type InsertTypeObj<T> = (payload: InsertParams<T>) => void;
243
222
 
244
223
  type EffectFunction<T, R> = (state: T, deps: any[]) => R;
245
224
  export type EndType<T, IsArrayElement = false> = {
246
- $addZodValidation: (errors: ValidationError[]) => void;
225
+ $getPluginMetaData: (pluginName: string) => Record<string, any>;
226
+ $addPluginMetaData: (key: string, data: Record<string, any>) => void;
227
+ $removePluginMetaData: (key: string) => void;
228
+ $setOptions: (options: OptionsType<T>) => void;
229
+
230
+ $addZodValidation: (
231
+ errors: ValidationError[],
232
+ source?: 'client' | 'sync_engine' | 'api'
233
+ ) => void;
247
234
  $clearZodValidation: (paths?: string[]) => void;
235
+ $applyOperation: (
236
+ operation: UpdateTypeDetail,
237
+ metaData?: Record<string, any>
238
+ ) => void;
248
239
  $applyJsonPatch: (patches: any[]) => void;
249
240
  $update: UpdateType<T>;
250
241
  $_path: string[];
@@ -265,7 +256,14 @@ export type EndType<T, IsArrayElement = false> = {
265
256
  $isSelected: boolean;
266
257
  $setSelected: (value: boolean) => void;
267
258
  $toggleSelected: () => void;
268
- $getFormRef: () => React.RefObject<any> | undefined;
259
+ $formInput: {
260
+ setDisabled: (isDisabled: boolean) => void;
261
+ focus: () => void;
262
+ blur: () => void;
263
+ scrollIntoView: (options?: ScrollIntoViewOptions) => void;
264
+ click: () => void;
265
+ selectText: () => void;
266
+ };
269
267
  $removeStorage: () => void;
270
268
  $sync: () => void;
271
269
  $validationWrapper: ({
@@ -287,7 +285,7 @@ export type StateObject<T> = (T extends any[]
287
285
  : never) &
288
286
  EndType<T, true> & {
289
287
  $toggle: T extends boolean ? () => void : never;
290
- $getAllFormRefs: () => Map<string, React.RefObject<any>>;
288
+
291
289
  $_componentId: string | null;
292
290
  $getComponents: () => ComponentsType;
293
291
 
@@ -295,6 +293,7 @@ export type StateObject<T> = (T extends any[]
295
293
  $updateInitialState: (newState: T | null) => {
296
294
  fetchId: (field: keyof T) => string | number;
297
295
  };
296
+ $initializeAndMergeShadowState: (newState: any | null) => void;
298
297
  $_isLoading: boolean;
299
298
  $_serverState: T;
300
299
  $revertToInitialState: (obj?: { validationKey?: string }) => T;
@@ -321,10 +320,13 @@ type EffectiveSetStateArg<
321
320
  ? InsertParams<InferArrayElement<T>>
322
321
  : never
323
322
  : UpdateArg<T>;
324
- type UpdateOptions = {
325
- updateType: 'insert' | 'cut' | 'update';
326
323
 
327
- sync?: boolean;
324
+ type UpdateOptions = {
325
+ updateType: 'insert' | 'cut' | 'update' | 'insert_many';
326
+ validationTrigger?: 'onBlur' | 'onChange' | 'programmatic';
327
+ itemId?: string;
328
+ index?: number;
329
+ metaData?: Record<string, any>;
328
330
  };
329
331
  type EffectiveSetState<TStateObject> = (
330
332
  newStateOrFunction:
@@ -332,21 +334,24 @@ type EffectiveSetState<TStateObject> = (
332
334
  | EffectiveSetStateArg<TStateObject, 'insert'>
333
335
  | null,
334
336
  path: string[],
335
- updateObj: UpdateOptions,
337
+ updateObj: UpdateOptions, // Now includes itemId
336
338
  validationKey?: string
337
339
  ) => void;
338
340
 
339
341
  export type UpdateTypeDetail = {
340
342
  timeStamp: number;
341
343
  stateKey: string;
342
- updateType: 'update' | 'insert' | 'cut';
344
+ updateType: 'update' | 'insert' | 'cut' | 'insert_many';
343
345
  path: string[];
344
346
  status: 'new' | 'sent' | 'synced';
345
347
  oldValue: any;
346
348
  newValue: any;
347
349
  userId?: number;
348
- };
349
350
 
351
+ itemId?: string;
352
+ insertAfterId?: string;
353
+ metaData?: Record<string, any>;
354
+ };
350
355
  export type ReactivityUnion = 'none' | 'component' | 'deps' | 'all';
351
356
  export type ReactivityType =
352
357
  | 'none'
@@ -356,20 +361,16 @@ export type ReactivityType =
356
361
  | Array<Prettify<'none' | 'component' | 'deps' | 'all'>>;
357
362
 
358
363
  // Define the return type of the sync hook locally
359
- type SyncApi = {
360
- updateState: (data: { operation: any }) => void;
361
- connected: boolean;
362
- clientId: string | null;
363
- subscribers: string[];
364
- };
364
+
365
365
  type ValidationOptionsType = {
366
366
  key?: string;
367
367
  zodSchemaV3?: z3.ZodType<any, any, any>;
368
368
  zodSchemaV4?: z4.ZodType<any, any, any>;
369
-
370
- onBlur?: boolean;
369
+ onBlur?: 'error' | 'warning';
370
+ onChange?: 'error' | 'warning';
371
+ blockSync?: boolean;
371
372
  };
372
- type UseSyncType<T> = (state: T, a: SyncOptionsType<any>) => SyncApi;
373
+
373
374
  type SyncOptionsType<TApiParams> = {
374
375
  apiParams: TApiParams;
375
376
  stateKey?: string;
@@ -380,12 +381,23 @@ type SyncOptionsType<TApiParams> = {
380
381
  connect?: boolean;
381
382
  inMemoryState?: boolean;
382
383
  };
383
- export type OptionsType<T extends unknown = unknown, TApiParams = never> = {
384
+
385
+ export type CreateStateOptionsType<
386
+ T extends unknown = unknown,
387
+ TPlugins extends readonly CogsPlugin<any, any, any, any, any>[] = [],
388
+ > = {
389
+ formElements?: FormsElementsType<T, TPlugins>;
390
+ validation?: ValidationOptionsType;
391
+ plugins?: TPlugins;
392
+ };
393
+ export type OptionsType<
394
+ T extends unknown = unknown,
395
+ TApiParams = never,
396
+ > = CreateStateOptionsType & {
384
397
  log?: boolean;
385
398
  componentId?: string;
386
399
  syncOptions?: SyncOptionsType<TApiParams>;
387
400
 
388
- validation?: ValidationOptionsType;
389
401
  serverState?: {
390
402
  id?: string | number;
391
403
  data?: T;
@@ -414,12 +426,10 @@ export type OptionsType<T extends unknown = unknown, TApiParams = never> = {
414
426
  };
415
427
  middleware?: ({ update }: { update: UpdateTypeDetail }) => void;
416
428
 
417
- modifyState?: (state: T) => T;
418
429
  localStorage?: {
419
430
  key: string | ((state: T) => string);
420
431
  onChange?: (state: T) => void;
421
432
  };
422
- formElements?: FormsElementsType<T>;
423
433
 
424
434
  reactiveDeps?: (state: T) => any[] | true;
425
435
  reactiveType?: ReactivityType;
@@ -430,14 +440,11 @@ export type OptionsType<T extends unknown = unknown, TApiParams = never> = {
430
440
  dependencies?: any[];
431
441
  };
432
442
 
433
- export type SyncRenderOptions<T extends unknown = unknown> = {
434
- children: React.ReactNode;
435
- time: number;
436
- data?: T;
437
- key?: string;
438
- };
439
-
440
- type FormsElementsType<T> = {
443
+ export type FormsElementsType<
444
+ TState,
445
+ TPlugins extends readonly CogsPlugin<any, any, any, any, any>[] = [],
446
+ > = {
447
+ // These optional, built-in wrappers are unchanged.
441
448
  validation?: (options: {
442
449
  children: React.ReactNode;
443
450
  status: ValidationStatus;
@@ -445,28 +452,32 @@ type FormsElementsType<T> = {
445
452
  hasErrors: boolean;
446
453
  hasWarnings: boolean;
447
454
  allErrors: ValidationError[];
448
-
449
455
  path: string[];
450
456
  message?: string;
451
- getData?: () => T;
457
+ getData?: () => TState;
452
458
  }) => React.ReactNode;
453
- syncRender?: (options: SyncRenderOptions<T>) => React.ReactNode;
454
- };
455
-
456
- export type InitialStateInnerType<T extends unknown = unknown> = {
457
- initialState: T;
458
- } & OptionsType<T>;
459
-
460
- export type InitialStateType<T> = {
461
- [key: string]: InitialStateInnerType<T>;
462
- };
463
-
464
- export type AllStateTypes<T extends unknown> = Record<string, T>;
465
-
466
- export type CogsInitialState<T> = {
467
- initialState: T;
468
- formElements?: FormsElementsType<T>;
459
+ syncRender?: (options: {
460
+ children: React.ReactNode;
461
+ time: number;
462
+ data?: TState;
463
+ key?: string;
464
+ }) => React.ReactNode;
465
+ } & {
466
+ // For each plugin `P` in the TPlugins array...
467
+ [P in TPlugins[number] as P['name']]?: P['formWrapper'] extends (
468
+ // ...check if its `formWrapper` property is a function...
469
+ arg: any
470
+ ) => any
471
+ ? // ...if it is, infer the type of its FIRST PARAMETER. This is the key.
472
+ Parameters<P['formWrapper']>[0]
473
+ : // Otherwise, the type is invalid.
474
+ never;
469
475
  };
476
+ export type CogsInitialState<T> =
477
+ | {
478
+ initialState: T;
479
+ }
480
+ | CreateStateOptionsType<T>;
470
481
 
471
482
  export type TransformedStateType<T> = {
472
483
  [P in keyof T]: T[P] extends CogsInitialState<infer U> ? U : T[P];
@@ -479,6 +490,7 @@ const {
479
490
  setShadowMetadata,
480
491
  getShadowValue,
481
492
  initializeShadowState,
493
+ initializeAndMergeShadowState,
482
494
  updateShadowAtPath,
483
495
  insertShadowArrayElement,
484
496
  insertManyShadowArrayElements,
@@ -493,8 +505,14 @@ const {
493
505
  clearSelectedIndex,
494
506
  getSyncInfo,
495
507
  notifyPathSubscribers,
508
+ getPluginMetaDataMap,
509
+ setPluginMetaData,
510
+ removePluginMetaData,
496
511
  // Note: The old functions are no longer imported under their original names
497
512
  } = getGlobalStore.getState();
513
+
514
+ const { notifyUpdate } = pluginStore.getState();
515
+
498
516
  function getArrayData(stateKey: string, path: string[], meta?: MetaData) {
499
517
  const shadowMeta = getShadowMetadata(stateKey, path);
500
518
  const isArray = !!shadowMeta?.arrayKeys;
@@ -537,11 +555,23 @@ function findArrayItem(
537
555
  function setAndMergeOptions(stateKey: string, newOptions: OptionsType<any>) {
538
556
  const initialOptions = getInitialOptions(stateKey as string) || {};
539
557
 
540
- setInitialStateOptions(stateKey as string, {
558
+ const mergedOptions = {
541
559
  ...initialOptions,
542
560
  ...newOptions,
543
- });
561
+ };
562
+
563
+ // FIX: Apply the same default onBlur logic here too
564
+ if (
565
+ (mergedOptions.validation?.zodSchemaV4 ||
566
+ mergedOptions.validation?.zodSchemaV3) &&
567
+ !mergedOptions.validation?.onBlur
568
+ ) {
569
+ mergedOptions.validation.onBlur = 'error';
570
+ }
571
+
572
+ setInitialStateOptions(stateKey as string, mergedOptions);
544
573
  }
574
+
545
575
  function setOptions<StateKey, Opt>({
546
576
  stateKey,
547
577
  options,
@@ -554,92 +584,134 @@ function setOptions<StateKey, Opt>({
554
584
  const initialOptions = getInitialOptions(stateKey as string) || {};
555
585
  const initialOptionsPartState = initialOptionsPart[stateKey as string] || {};
556
586
 
557
- const mergedOptions = { ...initialOptionsPartState, ...initialOptions };
558
-
587
+ let mergedOptions = { ...initialOptionsPartState, ...initialOptions };
559
588
  let needToAdd = false;
589
+
560
590
  if (options) {
561
- for (const key in options) {
562
- if (!mergedOptions.hasOwnProperty(key)) {
563
- needToAdd = true;
564
- mergedOptions[key] = options[key as keyof typeof options];
565
- } else {
566
- if (
567
- key == 'localStorage' &&
568
- options[key] &&
569
- mergedOptions[key].key !== options[key]?.key
570
- ) {
571
- needToAdd = true;
572
- mergedOptions[key] = options[key];
573
- }
574
- if (
575
- key == 'defaultState' &&
576
- options[key] &&
577
- mergedOptions[key] !== options[key] &&
578
- !isDeepEqual(mergedOptions[key], options[key])
579
- ) {
580
- needToAdd = true;
581
- mergedOptions[key] = options[key];
591
+ const deepMerge = (target: any, source: any) => {
592
+ for (const key in source) {
593
+ if (source.hasOwnProperty(key)) {
594
+ if (
595
+ source[key] instanceof Object &&
596
+ !Array.isArray(source[key]) &&
597
+ target[key] instanceof Object
598
+ ) {
599
+ if (!isDeepEqual(target[key], source[key])) {
600
+ deepMerge(target[key], source[key]);
601
+ needToAdd = true;
602
+ }
603
+ } else {
604
+ if (target[key] !== source[key]) {
605
+ target[key] = source[key];
606
+ needToAdd = true;
607
+ }
608
+ }
582
609
  }
583
610
  }
584
- }
611
+ return target;
612
+ };
613
+
614
+ mergedOptions = deepMerge(mergedOptions, options);
585
615
  }
586
616
 
587
- // Always preserve syncOptions if it exists in mergedOptions but not in options
588
- if (
589
- mergedOptions.syncOptions &&
590
- (!options || !options.hasOwnProperty('syncOptions'))
591
- ) {
592
- needToAdd = true;
617
+ // Set default onBlur
618
+ if (mergedOptions.validation) {
619
+ const onBlurProvided =
620
+ options?.validation?.hasOwnProperty('onBlur') ||
621
+ initialOptions?.validation?.hasOwnProperty('onBlur') ||
622
+ initialOptionsPartState?.validation?.hasOwnProperty('onBlur');
623
+
624
+ if (!onBlurProvided) {
625
+ mergedOptions.validation.onBlur = 'error';
626
+ needToAdd = true;
627
+ }
593
628
  }
594
629
 
595
630
  if (needToAdd) {
596
631
  setInitialStateOptions(stateKey as string, mergedOptions);
632
+
633
+ // NEW: Update type info if validation schema was added
634
+ const hadSchema =
635
+ initialOptions?.validation?.zodSchemaV4 ||
636
+ initialOptions?.validation?.zodSchemaV3;
637
+ const hasNewSchemaV4 =
638
+ mergedOptions.validation?.zodSchemaV4 &&
639
+ !initialOptions?.validation?.zodSchemaV4;
640
+ const hasNewSchemaV3 =
641
+ mergedOptions.validation?.zodSchemaV3 &&
642
+ !initialOptions?.validation?.zodSchemaV3;
643
+
644
+ if (!hadSchema && (hasNewSchemaV4 || hasNewSchemaV3)) {
645
+ if (hasNewSchemaV4) {
646
+ updateShadowTypeInfo(
647
+ stateKey as string,
648
+ mergedOptions.validation.zodSchemaV4,
649
+ 'zod4'
650
+ );
651
+ } else if (hasNewSchemaV3) {
652
+ updateShadowTypeInfo(
653
+ stateKey as string,
654
+ mergedOptions.validation.zodSchemaV3,
655
+ 'zod3'
656
+ );
657
+ }
658
+
659
+ // Notify components to re-render with updated validation
660
+ notifyComponents(stateKey as string);
661
+ }
597
662
  }
598
- }
599
663
 
600
- export function addStateOptions<T extends unknown>(
664
+ return mergedOptions;
665
+ }
666
+ export function addStateOptions<T>(
601
667
  initialState: T,
602
- { formElements, validation }: OptionsType<T>
668
+ options: CreateStateOptionsType<T>
603
669
  ) {
604
- return { initialState: initialState, formElements, validation } as T;
670
+ return {
671
+ ...options,
672
+ initialState,
673
+ _addStateOptions: true,
674
+ };
605
675
  }
676
+ export type PluginData = {
677
+ plugin: CogsPlugin<any, any, any, any, any>;
678
+ options: any;
679
+ hookData?: any;
680
+ };
606
681
 
607
- // Define the type for the options setter using the Transformed state
608
- type SetCogsOptionsFunc<T extends Record<string, any>> = <
609
- StateKey extends keyof TransformedStateType<T>,
610
- >(
611
- stateKey: StateKey,
612
- options: OptionsType<TransformedStateType<T>[StateKey]>
613
- ) => void;
682
+ ////////////////////////////////
614
683
 
615
- export const createCogsState = <State extends Record<StateKeys, unknown>>(
684
+ export const createCogsState = <
685
+ State extends Record<string, unknown>,
686
+ const TPlugins extends readonly CogsPlugin<string, any, any, any, any>[] = [],
687
+ >(
616
688
  initialState: State,
617
689
  opt?: {
618
- formElements?: FormsElementsType<State>;
690
+ plugins?: TPlugins;
691
+ formElements?: FormsElementsType<State, TPlugins>;
619
692
  validation?: ValidationOptionsType;
620
- __fromSyncSchema?: boolean;
621
- __syncNotifications?: Record<string, Function>;
622
- __apiParamsMap?: Record<string, any>;
623
- __useSync?: UseSyncType<State>;
624
- __syncSchemas?: Record<string, any>;
625
693
  }
626
694
  ) => {
627
- let newInitialState = initialState;
628
- const [statePart, initialOptionsPart] =
629
- transformStateFunc<State>(newInitialState);
695
+ type PluginOptions = {
696
+ [K in TPlugins[number] as K['name']]?: K extends CogsPlugin<
697
+ string,
698
+ infer O,
699
+ any,
700
+ any,
701
+ any
702
+ >
703
+ ? O
704
+ : never;
705
+ };
630
706
 
631
- if (opt?.__fromSyncSchema && opt?.__syncNotifications) {
632
- getGlobalStore
633
- .getState()
634
- .setInitialStateOptions('__notifications', opt.__syncNotifications);
707
+ if (opt?.plugins) {
708
+ pluginStore.getState().setRegisteredPlugins(opt.plugins as any);
635
709
  }
636
710
 
637
- if (opt?.__fromSyncSchema && opt?.__apiParamsMap) {
638
- getGlobalStore
639
- .getState()
640
- .setInitialStateOptions('__apiParamsMap', opt.__apiParamsMap);
641
- }
711
+ const [statePart, initialOptionsPart] =
712
+ transformStateFunc<State>(initialState);
642
713
 
714
+ // FIX: Store options INCLUDING validation for each state key
643
715
  Object.keys(statePart).forEach((key) => {
644
716
  let existingOptions = initialOptionsPart[key] || {};
645
717
 
@@ -654,61 +726,77 @@ export const createCogsState = <State extends Record<StateKeys, unknown>>(
654
726
  };
655
727
  }
656
728
 
657
- if (opt?.validation) {
658
- mergedOptions.validation = {
659
- ...opt.validation,
660
- ...(existingOptions.validation || {}),
661
- };
729
+ mergedOptions.validation = {
730
+ onBlur: 'error',
731
+ ...opt?.validation,
732
+ ...(existingOptions.validation || {}),
733
+ };
662
734
 
663
- if (opt.validation.key && !existingOptions.validation?.key) {
664
- mergedOptions.validation.key = `${opt.validation.key}.${key}`;
665
- }
666
- }
667
- if (opt?.__syncSchemas?.[key]?.schemas?.validation) {
668
- mergedOptions.validation = {
669
- zodSchemaV4: opt.__syncSchemas[key].schemas.validation,
670
- ...existingOptions.validation,
671
- };
735
+ if (opt?.validation?.key && !existingOptions.validation?.key) {
736
+ mergedOptions.validation.key = `${opt.validation.key}.${key}`;
672
737
  }
673
- if (Object.keys(mergedOptions).length > 0) {
674
- const existingGlobalOptions = getInitialOptions(key);
675
738
 
676
- if (!existingGlobalOptions) {
677
- setInitialStateOptions(key, mergedOptions);
678
- } else {
679
- // Merge with existing global options
680
- setInitialStateOptions(key, {
739
+ const existingGlobalOptions = getInitialOptions(key);
740
+
741
+ const finalOptions = existingGlobalOptions
742
+ ? {
681
743
  ...existingGlobalOptions,
682
744
  ...mergedOptions,
683
- });
684
- }
685
- }
745
+ formElements: {
746
+ ...existingGlobalOptions.formElements,
747
+ ...mergedOptions.formElements,
748
+ },
749
+ validation: {
750
+ ...existingGlobalOptions.validation,
751
+ ...mergedOptions.validation,
752
+ },
753
+ }
754
+ : mergedOptions;
755
+
756
+ setInitialStateOptions(key, finalOptions);
686
757
  });
687
758
 
688
759
  Object.keys(statePart).forEach((key) => {
689
760
  initializeShadowState(key, statePart[key]);
690
761
  });
691
-
762
+ type Prettify<T> = {
763
+ [K in keyof T]: T[K];
764
+ } & {};
692
765
  type StateKeys = keyof typeof statePart;
693
766
 
694
767
  const useCogsState = <StateKey extends StateKeys>(
695
768
  stateKey: StateKey,
696
- options?: Prettify<OptionsType<(typeof statePart)[StateKey]>>
769
+ options?: Prettify<
770
+ OptionsType<(typeof statePart)[StateKey], never> & {
771
+ [PName in keyof PluginOptions]?: PluginOptions[PName] extends infer P
772
+ ? P extends Record<string, any>
773
+ ? {
774
+ [K in keyof P]: P[K] extends { __key: 'keyed'; map: infer TMap }
775
+ ? StateKey extends keyof TMap
776
+ ? TMap[StateKey]
777
+ : never
778
+ : P[K];
779
+ }
780
+ : P
781
+ : never;
782
+ }
783
+ >
697
784
  ) => {
698
785
  const [componentId] = useState(options?.componentId ?? uuidv4());
699
786
 
700
- setOptions({
787
+ const currentOptions = setOptions({
701
788
  stateKey,
702
789
  options,
703
790
  initialOptionsPart,
704
791
  });
792
+
793
+ const optionsRef = useRef(currentOptions);
794
+ optionsRef.current = currentOptions;
795
+
705
796
  const thiState =
706
797
  getShadowValue(stateKey as string, []) || statePart[stateKey as string];
707
- const partialState = options?.modifyState
708
- ? options.modifyState(thiState)
709
- : thiState;
710
798
 
711
- const updater = useCogsStateFn<(typeof statePart)[StateKey]>(partialState, {
799
+ const updater = useCogsStateFn<(typeof statePart)[StateKey]>(thiState, {
712
800
  stateKey: stateKey as string,
713
801
  syncUpdate: options?.syncUpdate,
714
802
  componentId,
@@ -719,14 +807,30 @@ export const createCogsState = <State extends Record<StateKeys, unknown>>(
719
807
  defaultState: options?.defaultState as any,
720
808
  dependencies: options?.dependencies,
721
809
  serverState: options?.serverState,
722
- syncOptions: options?.syncOptions,
723
- __useSync: opt?.__useSync as UseSyncType<(typeof statePart)[StateKey]>,
724
810
  });
725
811
 
812
+ useEffect(() => {
813
+ if (options) {
814
+ pluginStore
815
+ .getState()
816
+ .setPluginOptionsForState(stateKey as string, options);
817
+ }
818
+ }, [stateKey, options]);
819
+ useEffect(() => {
820
+ console.log('adding handler 1', stateKey, updater);
821
+ pluginStore
822
+ .getState()
823
+ .stateHandlers.set(stateKey as string, updater as any);
824
+
825
+ return () => {
826
+ pluginStore.getState().stateHandlers.delete(stateKey as string);
827
+ };
828
+ }, [stateKey, updater]);
829
+
726
830
  return updater;
727
831
  };
728
832
 
729
- function setCogsOptions<StateKey extends StateKeys>(
833
+ function setCogsOptionsByKey<StateKey extends StateKeys>(
730
834
  stateKey: StateKey,
731
835
  options: OptionsType<(typeof statePart)[StateKey]>
732
836
  ) {
@@ -739,93 +843,53 @@ export const createCogsState = <State extends Record<StateKeys, unknown>>(
739
843
  notifyComponents(stateKey as string);
740
844
  }
741
845
 
742
- return { useCogsState, setCogsOptions } as CogsApi<State, never>;
743
- };
744
- type UseCogsStateHook<
745
- T extends Record<string, any>,
746
- TApiParamsMap extends Record<string, any> = never,
747
- > = <StateKey extends keyof TransformedStateType<T> & string>(
748
- stateKey: StateKey,
749
- options?: [TApiParamsMap] extends [never]
750
- ? // When TApiParamsMap is never (no sync)
751
- Prettify<OptionsType<TransformedStateType<T>[StateKey]>>
752
- : // When TApiParamsMap exists (sync enabled)
753
- StateKey extends keyof TApiParamsMap
754
- ? Prettify<
755
- OptionsType<
756
- TransformedStateType<T>[StateKey],
757
- TApiParamsMap[StateKey]
758
- > & {
759
- syncOptions: Prettify<SyncOptionsType<TApiParamsMap[StateKey]>>;
760
- }
761
- >
762
- : Prettify<OptionsType<TransformedStateType<T>[StateKey]>>
763
- ) => StateObject<TransformedStateType<T>[StateKey]>;
764
-
765
- // Update CogsApi to default to never instead of Record<string, never>
766
- type CogsApi<
767
- T extends Record<string, any>,
768
- TApiParamsMap extends Record<string, any> = never,
769
- > = {
770
- useCogsState: UseCogsStateHook<T, TApiParamsMap>;
771
- setCogsOptions: SetCogsOptionsFunc<T>;
772
- };
773
- type GetParamType<SchemaEntry> = SchemaEntry extends {
774
- api?: { queryData?: { _paramType?: infer P } };
775
- }
776
- ? P
777
- : never;
778
-
779
- export function createCogsStateFromSync<
780
- TSyncSchema extends {
781
- schemas: Record<
782
- string,
783
- {
784
- schemas: { defaultValues: any };
785
- api?: {
786
- queryData?: any;
846
+ function setCogsFormElements(
847
+ formElements: FormsElementsType<State, TPlugins>
848
+ ) {
849
+ // Get the current list of registered plugins from the store.
850
+ const currentPlugins = pluginStore.getState().registeredPlugins;
851
+
852
+ // Create a new array by mapping over the current plugins.
853
+ // This is crucial for immutability and ensuring Zustand detects the change.
854
+ const updatedPlugins = currentPlugins.map((plugin) => {
855
+ // Check if the formElements object has a wrapper for this specific plugin by name.
856
+ if (formElements.hasOwnProperty(plugin.name)) {
857
+ // If it does, return a *new* plugin object.
858
+ // Spread the existing plugin properties and add/overwrite the formWrapper.
859
+ return {
860
+ ...plugin,
861
+ formWrapper: formElements[plugin.name as keyof typeof formElements],
787
862
  };
788
- [key: string]: any;
789
863
  }
790
- >;
791
- notifications: Record<string, any>;
792
- },
793
- >(
794
- syncSchema: TSyncSchema,
795
- useSync: UseSyncType<any>
796
- ): CogsApi<
797
- {
798
- [K in keyof TSyncSchema['schemas']]: TSyncSchema['schemas'][K]['schemas']['defaultValues'];
799
- },
800
- {
801
- [K in keyof TSyncSchema['schemas']]: GetParamType<
802
- TSyncSchema['schemas'][K]
803
- >;
804
- }
805
- > {
806
- const schemas = syncSchema.schemas;
807
- const initialState: any = {};
808
- const apiParamsMap: any = {};
809
-
810
- // Extract defaultValues AND apiParams from each entry
811
- for (const key in schemas) {
812
- const entry = schemas[key];
813
- initialState[key] = entry?.schemas?.defaultValues || {};
814
-
815
- // Extract apiParams from the api.queryData._paramType
816
- if (entry?.api?.queryData?._paramType) {
817
- apiParamsMap[key] = entry.api.queryData._paramType;
818
- }
819
- }
864
+ // If there's no new wrapper for this plugin, return the original object.
865
+ return plugin;
866
+ });
820
867
 
821
- return createCogsState(initialState, {
822
- __fromSyncSchema: true,
823
- __syncNotifications: syncSchema.notifications,
824
- __apiParamsMap: apiParamsMap,
825
- __useSync: useSync,
826
- __syncSchemas: schemas,
827
- }) as any;
828
- }
868
+ // Use the store's dedicated setter function to update the registered plugins list.
869
+ // This will trigger a state update that components listening to the store will react to.
870
+ pluginStore.getState().setRegisteredPlugins(updatedPlugins as any);
871
+
872
+ // For good measure and consistency, we should still update the formElements
873
+ // in the initial options, in case any other part of the system relies on it.
874
+ const allStateKeys = Object.keys(statePart);
875
+ allStateKeys.forEach((key) => {
876
+ const existingOptions = getInitialOptions(key) || {};
877
+ const finalOptions = {
878
+ ...existingOptions,
879
+ formElements: {
880
+ ...(existingOptions.formElements || {}),
881
+ ...formElements,
882
+ },
883
+ };
884
+ setInitialStateOptions(key, finalOptions);
885
+ });
886
+ }
887
+ return {
888
+ useCogsState,
889
+ setCogsOptionsByKey,
890
+ setCogsFormElements,
891
+ };
892
+ };
829
893
 
830
894
  const saveToLocalStorage = <T,>(
831
895
  state: T,
@@ -997,35 +1061,14 @@ let isFlushScheduled = false;
997
1061
  function scheduleFlush() {
998
1062
  if (!isFlushScheduled) {
999
1063
  isFlushScheduled = true;
1000
- queueMicrotask(flushQueue);
1064
+ console.log('Scheduling flush');
1065
+ queueMicrotask(() => {
1066
+ console.log('Actually flushing');
1067
+ flushQueue();
1068
+ });
1001
1069
  }
1002
1070
  }
1003
- function handleUpdate(
1004
- stateKey: string,
1005
- path: string[],
1006
- payload: any
1007
- ): { type: 'update'; oldValue: any; newValue: any; shadowMeta: any } {
1008
- // ✅ FIX: Get the old value before the update.
1009
- const oldValue = getGlobalStore.getState().getShadowValue(stateKey, path);
1010
-
1011
- const newValue = isFunction(payload) ? payload(oldValue) : payload;
1012
-
1013
- // ✅ FIX: The new `updateShadowAtPath` handles metadata preservation automatically.
1014
- // The manual loop has been removed.
1015
- updateShadowAtPath(stateKey, path, newValue);
1016
-
1017
- markAsDirty(stateKey, path, { bubble: true });
1018
1071
 
1019
- // Return the metadata of the node *after* the update.
1020
- const newShadowMeta = getShadowMetadata(stateKey, path);
1021
-
1022
- return {
1023
- type: 'update',
1024
- oldValue: oldValue,
1025
- newValue,
1026
- shadowMeta: newShadowMeta,
1027
- };
1028
- }
1029
1072
  // 2. Update signals
1030
1073
  function updateSignals(shadowMeta: any, displayValue: any) {
1031
1074
  if (!shadowMeta?.signals?.length) return;
@@ -1127,79 +1170,165 @@ function getComponentNotifications(
1127
1170
  }
1128
1171
  });
1129
1172
  }
1130
- } else if (result.type === 'insert' || result.type === 'cut') {
1131
- // For array structural changes (add/remove), notify components listening to the parent array.
1173
+ } else if (
1174
+ result.type === 'insert' ||
1175
+ result.type === 'cut' ||
1176
+ result.type === 'insert_many'
1177
+ ) {
1178
+ // This path is the array itself (e.g., ['pets'])
1132
1179
  const parentArrayPath = result.type === 'insert' ? path : path.slice(0, -1);
1133
- const parentMeta = getShadowMetadata(stateKey, parentArrayPath);
1134
-
1135
- if (parentMeta?.pathComponents) {
1136
- parentMeta.pathComponents.forEach((componentId: string) => {
1137
- const component = rootMeta.components?.get(componentId);
1138
- // NEW: Add component to the set
1139
- if (component) {
1140
- componentsToNotify.add(component);
1141
- }
1142
- });
1180
+
1181
+ // --- FIX: ADD BUBBLE-UP LOGIC HERE ---
1182
+ // Start from the array's path and go up to the root
1183
+ let currentPath = [...parentArrayPath];
1184
+ while (true) {
1185
+ const pathMeta = getShadowMetadata(stateKey, currentPath);
1186
+
1187
+ if (pathMeta?.pathComponents) {
1188
+ pathMeta.pathComponents.forEach((componentId: string) => {
1189
+ const component = rootMeta.components?.get(componentId);
1190
+ if (component) {
1191
+ componentsToNotify.add(component);
1192
+ }
1193
+ });
1194
+ }
1195
+
1196
+ if (currentPath.length === 0) break;
1197
+ currentPath.pop(); // Go up one level
1143
1198
  }
1199
+ // --- END FIX ---
1144
1200
  }
1145
1201
 
1146
1202
  // --- PASS 2: Handle 'all' and 'deps' reactivity types ---
1147
1203
  // Iterate over all components for this stateKey that haven't been notified yet.
1148
- rootMeta.components.forEach((component, componentId) => {
1149
- // If we've already added this component, skip it.
1150
- if (componentsToNotify.has(component)) {
1151
- return;
1152
- }
1153
-
1154
- const reactiveTypes = Array.isArray(component.reactiveType)
1155
- ? component.reactiveType
1156
- : [component.reactiveType || 'component'];
1157
-
1158
- if (reactiveTypes.includes('all')) {
1159
- componentsToNotify.add(component);
1160
- } else if (reactiveTypes.includes('deps') && component.depsFunction) {
1161
- const currentState = getShadowValue(stateKey, []);
1162
- const newDeps = component.depsFunction(currentState);
1163
-
1164
- if (
1165
- newDeps === true ||
1166
- (Array.isArray(newDeps) && !isDeepEqual(component.prevDeps, newDeps))
1167
- ) {
1168
- component.prevDeps = newDeps as any; // Update the dependencies for the next check
1169
- componentsToNotify.add(component);
1170
- }
1171
- }
1172
- });
1204
+ // rootMeta.components.forEach((component, componentId) => {
1205
+ // // If we've already added this component, skip it.
1206
+ // if (componentsToNotify.has(component)) {
1207
+ // return;
1208
+ // }
1209
+
1210
+ // const reactiveTypes = Array.isArray(component.reactiveType)
1211
+ // ? component.reactiveType
1212
+ // : [component.reactiveType || 'component'];
1213
+
1214
+ // if (reactiveTypes.includes('all')) {
1215
+ // componentsToNotify.add(component);
1216
+ // } else if (reactiveTypes.includes('deps') && component.depsFunction) {
1217
+ // const currentState = getShadowValue(stateKey, []);
1218
+ // const newDeps = component.depsFunction(currentState);
1219
+
1220
+ // if (
1221
+ // newDeps === true ||
1222
+ // (Array.isArray(newDeps) && !isDeepEqual(component.prevDeps, newDeps))
1223
+ // ) {
1224
+ // component.prevDeps = newDeps as any; // Update the dependencies for the next check
1225
+ // componentsToNotify.add(component);
1226
+ // }
1227
+ // }
1228
+ // });
1173
1229
 
1174
1230
  return componentsToNotify;
1175
1231
  }
1176
1232
 
1177
- function handleInsert(
1233
+ function handleUpdate(
1178
1234
  stateKey: string,
1179
1235
  path: string[],
1180
1236
  payload: any
1181
- ): { type: 'insert'; newValue: any; shadowMeta: any } {
1237
+ ): { type: 'update'; oldValue: any; newValue: any; shadowMeta: any } {
1238
+ // ✅ FIX: Get the old value before the update.
1239
+ const oldValue = getGlobalStore.getState().getShadowValue(stateKey, path);
1240
+
1241
+ const newValue = isFunction(payload) ? payload(oldValue) : payload;
1242
+
1243
+ // ✅ FIX: The new `updateShadowAtPath` handles metadata preservation automatically.
1244
+ // The manual loop has been removed.
1245
+ updateShadowAtPath(stateKey, path, newValue);
1246
+
1247
+ markAsDirty(stateKey, path, { bubble: true });
1248
+
1249
+ // Return the metadata of the node *after* the update.
1250
+ const newShadowMeta = getShadowMetadata(stateKey, path);
1251
+
1252
+ return {
1253
+ type: 'update',
1254
+ oldValue: oldValue,
1255
+ newValue,
1256
+ shadowMeta: newShadowMeta,
1257
+ };
1258
+ }
1259
+ function handleInsertMany(
1260
+ stateKey: string,
1261
+ path: string[],
1262
+ payload: any[]
1263
+ ): {
1264
+ type: 'insert_many';
1265
+ count: number;
1266
+ shadowMeta: any;
1267
+ path: string[];
1268
+ } {
1269
+ // Use the existing, optimized global store function to perform the state update
1270
+ insertManyShadowArrayElements(stateKey, path, payload);
1271
+
1272
+ markAsDirty(stateKey, path, { bubble: true });
1273
+ const updatedMeta = getShadowMetadata(stateKey, path);
1274
+
1275
+ return {
1276
+ type: 'insert_many',
1277
+ count: payload.length,
1278
+ shadowMeta: updatedMeta,
1279
+ path: path,
1280
+ };
1281
+ }
1282
+ function handleInsert(
1283
+ stateKey: string,
1284
+ path: string[],
1285
+ payload: any,
1286
+ index?: number,
1287
+ itemId?: string
1288
+ ): {
1289
+ type: 'insert';
1290
+ newValue: any;
1291
+ shadowMeta: any;
1292
+ path: string[];
1293
+ itemId: string;
1294
+ insertAfterId?: string;
1295
+ } {
1182
1296
  let newValue;
1297
+
1183
1298
  if (isFunction(payload)) {
1184
1299
  const { value: currentValue } = getScopedData(stateKey, path);
1185
- newValue = payload({ state: currentValue, uuid: uuidv4() });
1300
+ newValue = payload({ state: currentValue });
1186
1301
  } else {
1187
1302
  newValue = payload;
1188
1303
  }
1304
+ //console.time('insertShadowArrayElement');
1305
+ // Pass itemId to insertShadowArrayElement
1306
+ const actualItemId = insertShadowArrayElement(
1307
+ stateKey,
1308
+ path,
1309
+ newValue,
1310
+ index,
1311
+ itemId
1312
+ );
1313
+ //console.timeEnd('insertShadowArrayElement');
1189
1314
 
1190
- insertShadowArrayElement(stateKey, path, newValue);
1191
1315
  markAsDirty(stateKey, path, { bubble: true });
1192
1316
 
1193
1317
  const updatedMeta = getShadowMetadata(stateKey, path);
1194
- if (updatedMeta?.arrayKeys) {
1195
- const newItemKey = updatedMeta.arrayKeys[updatedMeta.arrayKeys.length - 1];
1196
- if (newItemKey) {
1197
- const newItemPath = newItemKey.split('.').slice(1);
1198
- markAsDirty(stateKey, newItemPath, { bubble: false });
1199
- }
1318
+
1319
+ let insertAfterId: string | undefined;
1320
+ if (updatedMeta?.arrayKeys && index !== undefined && index > 0) {
1321
+ insertAfterId = updatedMeta.arrayKeys[index - 1];
1200
1322
  }
1201
1323
 
1202
- return { type: 'insert', newValue, shadowMeta: updatedMeta };
1324
+ return {
1325
+ type: 'insert',
1326
+ newValue,
1327
+ shadowMeta: updatedMeta,
1328
+ path: path,
1329
+ itemId: actualItemId,
1330
+ insertAfterId: insertAfterId,
1331
+ };
1203
1332
  }
1204
1333
 
1205
1334
  function handleCut(
@@ -1231,7 +1360,7 @@ function flushQueue() {
1231
1360
  if (result.shadowMeta?.signals?.length > 0) {
1232
1361
  signalUpdates.push({ shadowMeta: result.shadowMeta, displayValue });
1233
1362
  }
1234
-
1363
+ // console.time('getComponentNotifications');
1235
1364
  const componentNotifications = getComponentNotifications(
1236
1365
  result.stateKey,
1237
1366
  result.path,
@@ -1241,60 +1370,64 @@ function flushQueue() {
1241
1370
  componentNotifications.forEach((component) => {
1242
1371
  allComponentsToNotify.add(component);
1243
1372
  });
1373
+ // console.timeEnd('getComponentNotifications');
1244
1374
  }
1245
-
1375
+ //console.time('logs');
1246
1376
  if (logsToAdd.length > 0) {
1247
1377
  addStateLog(logsToAdd);
1248
1378
  }
1249
-
1379
+ //console.timeEnd('logs');
1250
1380
  signalUpdates.forEach(({ shadowMeta, displayValue }) => {
1251
1381
  updateSignals(shadowMeta, displayValue);
1252
1382
  });
1253
1383
 
1384
+ // console.time('updateComponents');
1254
1385
  allComponentsToNotify.forEach((component) => {
1255
1386
  component.forceUpdate();
1256
1387
  });
1388
+ //console.timeEnd('updateComponents');
1257
1389
 
1258
1390
  // --- Step 3: CLEANUP ---
1259
1391
  // Clear the queue for the next batch of updates.
1260
1392
  updateBatchQueue = [];
1261
1393
  isFlushScheduled = false;
1262
1394
  }
1263
-
1264
1395
  function createEffectiveSetState<T>(
1265
1396
  thisKey: string,
1266
- syncApiRef: React.MutableRefObject<any>,
1267
1397
  sessionId: string | undefined,
1268
1398
  latestInitialOptionsRef: React.MutableRefObject<OptionsType<T> | null>
1269
1399
  ): EffectiveSetState<T> {
1270
- // The returned function is the core setter that gets called by all state operations.
1271
- // It is now much simpler, delegating all work to the executeUpdate function.
1272
- return (newStateOrFunction, path, updateObj, validationKey?) => {
1400
+ return (newStateOrFunction, path, updateObj) => {
1273
1401
  executeUpdate(thisKey, path, newStateOrFunction, updateObj);
1274
1402
  };
1275
1403
 
1276
- // This inner function handles the logic for a single state update.
1277
1404
  function executeUpdate(
1278
1405
  stateKey: string,
1279
1406
  path: string[],
1280
1407
  payload: any,
1281
- options: UpdateOptions
1408
+ options: UpdateOptions // Now includes itemId
1282
1409
  ) {
1283
- // --- Step 1: Execute the core state change (Synchronous & Fast) ---
1284
- // This part modifies the in-memory state representation immediately.
1285
1410
  let result: any;
1286
1411
  switch (options.updateType) {
1287
1412
  case 'update':
1288
1413
  result = handleUpdate(stateKey, path, payload);
1289
1414
  break;
1290
1415
  case 'insert':
1291
- result = handleInsert(stateKey, path, payload);
1416
+ result = handleInsert(
1417
+ stateKey,
1418
+ path,
1419
+ payload,
1420
+ options.index,
1421
+ options.itemId
1422
+ );
1423
+ break;
1424
+ case 'insert_many':
1425
+ result = handleInsertMany(stateKey, path, payload);
1292
1426
  break;
1293
1427
  case 'cut':
1294
1428
  result = handleCut(stateKey, path);
1295
1429
  break;
1296
1430
  }
1297
-
1298
1431
  result.stateKey = stateKey;
1299
1432
  result.path = path;
1300
1433
  updateBatchQueue.push(result);
@@ -1308,6 +1441,9 @@ function createEffectiveSetState<T>(
1308
1441
  status: 'new',
1309
1442
  oldValue: result.oldValue,
1310
1443
  newValue: result.newValue ?? null,
1444
+ itemId: result.itemId,
1445
+ insertAfterId: result.insertAfterId,
1446
+ metaData: options.metaData,
1311
1447
  };
1312
1448
 
1313
1449
  updateBatchQueue.push(newUpdate);
@@ -1324,10 +1460,8 @@ function createEffectiveSetState<T>(
1324
1460
  if (latestInitialOptionsRef.current?.middleware) {
1325
1461
  latestInitialOptionsRef.current.middleware({ update: newUpdate });
1326
1462
  }
1327
-
1328
- if (options.sync !== false && syncApiRef.current?.connected) {
1329
- syncApiRef.current.updateState({ operation: newUpdate });
1330
- }
1463
+ runValidation(newUpdate, options.validationTrigger || 'programmatic');
1464
+ notifyUpdate(newUpdate);
1331
1465
  }
1332
1466
  }
1333
1467
 
@@ -1335,22 +1469,18 @@ export function useCogsStateFn<TStateObject extends unknown>(
1335
1469
  stateObject: TStateObject,
1336
1470
  {
1337
1471
  stateKey,
1338
-
1339
1472
  localStorage,
1340
1473
  formElements,
1341
1474
  reactiveDeps,
1342
1475
  reactiveType,
1343
1476
  componentId,
1344
1477
  defaultState,
1345
- syncUpdate,
1346
1478
  dependencies,
1347
1479
  serverState,
1348
- __useSync,
1349
1480
  }: {
1350
1481
  stateKey?: string;
1351
1482
  componentId?: string;
1352
1483
  defaultState?: TStateObject;
1353
- __useSync?: UseSyncType<TStateObject>;
1354
1484
  syncOptions?: SyncOptionsType<any>;
1355
1485
  } & OptionsType<TStateObject> = {}
1356
1486
  ) {
@@ -1365,16 +1495,6 @@ export function useCogsStateFn<TStateObject extends unknown>(
1365
1495
  latestInitialOptionsRef.current = (getInitialOptions(thisKey as string) ??
1366
1496
  null) as OptionsType<TStateObject> | null;
1367
1497
 
1368
- useEffect(() => {
1369
- if (syncUpdate && syncUpdate.stateKey === thisKey && syncUpdate.path?.[0]) {
1370
- const syncKey = `${syncUpdate.stateKey}:${syncUpdate.path.join('.')}`;
1371
- setSyncInfo(syncKey, {
1372
- timeStamp: syncUpdate.timeStamp!,
1373
- userId: syncUpdate.userId!,
1374
- });
1375
- }
1376
- }, [syncUpdate]);
1377
-
1378
1498
  const resolveInitialState = useCallback(
1379
1499
  (
1380
1500
  overrideOptions?: OptionsType<TStateObject>
@@ -1438,9 +1558,13 @@ export function useCogsStateFn<TStateObject extends unknown>(
1438
1558
 
1439
1559
  // Effect 1: When this component's serverState prop changes, broadcast it
1440
1560
  useEffect(() => {
1441
- setServerStateUpdate(thisKey, serverState);
1442
- }, [serverState, thisKey]);
1561
+ if (!serverState) return;
1443
1562
 
1563
+ // Only broadcast if we have valid server data
1564
+ if (serverState.status === 'success' && serverState.data !== undefined) {
1565
+ setServerStateUpdate(thisKey, serverState);
1566
+ }
1567
+ }, [serverState, thisKey]);
1444
1568
  // Effect 2: Listen for server state updates from ANY component
1445
1569
  useEffect(() => {
1446
1570
  const unsubscribe = getGlobalStore
@@ -1456,13 +1580,14 @@ export function useCogsStateFn<TStateObject extends unknown>(
1456
1580
  return; // Ignore if no valid data
1457
1581
  }
1458
1582
 
1583
+ // Store the server state in options for future reference
1459
1584
  setAndMergeOptions(thisKey, { serverState: serverStateData });
1460
1585
 
1461
1586
  const mergeConfig =
1462
1587
  typeof serverStateData.merge === 'object'
1463
1588
  ? serverStateData.merge
1464
1589
  : serverStateData.merge === true
1465
- ? { strategy: 'append' }
1590
+ ? { strategy: 'append' as const, key: 'id' }
1466
1591
  : null;
1467
1592
 
1468
1593
  const currentState = getShadowValue(thisKey, []);
@@ -1471,7 +1596,7 @@ export function useCogsStateFn<TStateObject extends unknown>(
1471
1596
  if (
1472
1597
  mergeConfig &&
1473
1598
  mergeConfig.strategy === 'append' &&
1474
- 'key' in mergeConfig && // Type guard for key
1599
+ 'key' in mergeConfig &&
1475
1600
  Array.isArray(currentState) &&
1476
1601
  Array.isArray(incomingData)
1477
1602
  ) {
@@ -1483,88 +1608,93 @@ export function useCogsStateFn<TStateObject extends unknown>(
1483
1608
  return;
1484
1609
  }
1485
1610
 
1611
+ // Get existing IDs to check for duplicates
1486
1612
  const existingIds = new Set(
1487
1613
  currentState.map((item: any) => item[keyField])
1488
1614
  );
1489
1615
 
1616
+ // Filter out duplicates from incoming data
1490
1617
  const newUniqueItems = incomingData.filter(
1491
1618
  (item: any) => !existingIds.has(item[keyField])
1492
1619
  );
1493
1620
 
1494
1621
  if (newUniqueItems.length > 0) {
1622
+ // Insert only the new unique items
1495
1623
  insertManyShadowArrayElements(thisKey, [], newUniqueItems);
1496
1624
  }
1497
1625
 
1498
- // Mark the entire final state as synced
1626
+ // Mark the entire merged state as synced
1499
1627
  const finalState = getShadowValue(thisKey, []);
1500
1628
  markEntireStateAsServerSynced(
1501
1629
  thisKey,
1502
1630
  [],
1503
1631
  finalState,
1504
- serverStateData.timestamp
1632
+ serverStateData.timestamp || Date.now()
1505
1633
  );
1506
1634
  } else {
1507
- // This handles the "replace" strategy (initial load)
1635
+ // Replace strategy (default) - completely replace the state
1508
1636
  initializeShadowState(thisKey, incomingData);
1509
1637
 
1638
+ // Mark as synced from server
1510
1639
  markEntireStateAsServerSynced(
1511
1640
  thisKey,
1512
1641
  [],
1513
1642
  incomingData,
1514
- serverStateData.timestamp
1643
+ serverStateData.timestamp || Date.now()
1515
1644
  );
1516
1645
  }
1646
+
1647
+ // Notify all components subscribed to this state
1648
+ notifyComponents(thisKey);
1517
1649
  }
1518
1650
  });
1519
1651
 
1520
1652
  return unsubscribe;
1521
- }, [thisKey, resolveInitialState]);
1522
-
1653
+ }, [thisKey]);
1523
1654
  useEffect(() => {
1524
1655
  const existingMeta = getGlobalStore
1525
1656
  .getState()
1526
1657
  .getShadowMetadata(thisKey, []);
1658
+
1659
+ // Skip if already initialized
1527
1660
  if (existingMeta && existingMeta.stateSource) {
1528
- return; // Already initialized, bail out.
1661
+ return;
1529
1662
  }
1530
1663
 
1531
1664
  const options = getInitialOptions(thisKey as string);
1532
1665
 
1533
1666
  const features = {
1534
- syncEnabled: !!cogsSyncFn && !!syncOpt,
1535
- validationEnabled: !!(
1536
- options?.validation?.zodSchemaV4 || options?.validation?.zodSchemaV3
1537
- ),
1538
1667
  localStorageEnabled: !!options?.localStorage?.key,
1539
1668
  };
1669
+
1540
1670
  setShadowMetadata(thisKey, [], {
1541
1671
  ...existingMeta,
1542
1672
  features,
1543
1673
  });
1674
+
1544
1675
  if (options?.defaultState !== undefined || defaultState !== undefined) {
1545
1676
  const finalDefaultState = options?.defaultState || defaultState;
1546
-
1547
- // Only set defaultState if it's not already set
1548
1677
  if (!options?.defaultState) {
1549
1678
  setAndMergeOptions(thisKey as string, {
1550
1679
  defaultState: finalDefaultState,
1551
1680
  });
1552
1681
  }
1682
+ }
1553
1683
 
1554
- const { value: resolvedState, source, timestamp } = resolveInitialState();
1555
-
1556
- initializeShadowState(thisKey, resolvedState);
1557
-
1558
- // Set shadow metadata with the correct source info
1559
- setShadowMetadata(thisKey, [], {
1560
- stateSource: source,
1561
- lastServerSync: source === 'server' ? timestamp : undefined,
1562
- isDirty: false,
1563
- baseServerState: source === 'server' ? resolvedState : undefined,
1564
- });
1684
+ const { value: resolvedState, source, timestamp } = resolveInitialState();
1685
+ initializeShadowState(thisKey, resolvedState);
1686
+ setShadowMetadata(thisKey, [], {
1687
+ stateSource: source,
1688
+ lastServerSync: source === 'server' ? timestamp : undefined,
1689
+ isDirty: source === 'server' ? false : undefined,
1690
+ baseServerState: source === 'server' ? resolvedState : undefined,
1691
+ });
1565
1692
 
1566
- notifyComponents(thisKey);
1693
+ if (source === 'server' && serverState) {
1694
+ setServerStateUpdate(thisKey, serverState);
1567
1695
  }
1696
+
1697
+ notifyComponents(thisKey);
1568
1698
  }, [thisKey, ...(dependencies || [])]);
1569
1699
 
1570
1700
  useLayoutEffect(() => {
@@ -1633,10 +1763,9 @@ export function useCogsStateFn<TStateObject extends unknown>(
1633
1763
  };
1634
1764
  }, []);
1635
1765
 
1636
- const syncApiRef = useRef<SyncApi | null>(null);
1637
1766
  const effectiveSetState = createEffectiveSetState(
1638
1767
  thisKey,
1639
- syncApiRef,
1768
+
1640
1769
  sessionId,
1641
1770
  latestInitialOptionsRef
1642
1771
  );
@@ -1656,16 +1785,6 @@ export function useCogsStateFn<TStateObject extends unknown>(
1656
1785
  return handler;
1657
1786
  }, [thisKey, sessionId]);
1658
1787
 
1659
- const cogsSyncFn = __useSync;
1660
- const syncOpt = latestInitialOptionsRef.current?.syncOptions;
1661
-
1662
- if (cogsSyncFn) {
1663
- syncApiRef.current = cogsSyncFn(
1664
- updaterFinal as any,
1665
- syncOpt ?? ({} as any)
1666
- );
1667
- }
1668
-
1669
1788
  return updaterFinal;
1670
1789
  }
1671
1790
 
@@ -1812,7 +1931,6 @@ function createProxyHandler<T>(
1812
1931
  sessionId?: string
1813
1932
  ): StateObject<T> {
1814
1933
  const proxyCache = new Map<string, any>();
1815
- let stateVersion = 0;
1816
1934
 
1817
1935
  function rebuildStateShape({
1818
1936
  path = [],
@@ -1826,6 +1944,7 @@ function createProxyHandler<T>(
1826
1944
  const derivationSignature = meta
1827
1945
  ? JSON.stringify(meta.arrayViews || meta.transforms)
1828
1946
  : '';
1947
+
1829
1948
  const cacheKey =
1830
1949
  path.join('.') + ':' + componentId + ':' + derivationSignature;
1831
1950
  if (proxyCache.has(cacheKey)) {
@@ -1838,10 +1957,17 @@ function createProxyHandler<T>(
1838
1957
 
1839
1958
  const handler = {
1840
1959
  get(target: any, prop: string) {
1960
+ if (typeof prop !== 'string') {
1961
+ // This is a Symbol. Let the default "get" behavior happen.
1962
+ // This allows internal JS operations and dev tools to work correctly
1963
+ // without interfering with your state logic.
1964
+ return Reflect.get(target, prop);
1965
+ }
1841
1966
  if (path.length === 0 && prop in rootLevelMethods) {
1842
1967
  return rootLevelMethods[prop as keyof typeof rootLevelMethods];
1843
1968
  }
1844
- if (!prop.startsWith('$')) {
1969
+
1970
+ if (typeof prop === 'string' && !prop.startsWith('$')) {
1845
1971
  const nextPath = [...path, prop];
1846
1972
  return rebuildStateShape({
1847
1973
  path: nextPath,
@@ -1919,15 +2045,11 @@ function createProxyHandler<T>(
1919
2045
  const getStatusFunc = () => {
1920
2046
  // ✅ Use the optimized helper to get all data in one efficient call
1921
2047
  const { shadowMeta, value } = getScopedData(stateKey, path, meta);
1922
-
1923
- // Priority 1: Explicitly dirty items. This is the most important status.
2048
+ console.log('getStatusFunc', path, shadowMeta, value);
1924
2049
  if (shadowMeta?.isDirty === true) {
1925
2050
  return 'dirty';
1926
2051
  }
1927
2052
 
1928
- // ✅ Priority 2: Synced items. This condition is now cleaner.
1929
- // An item is considered synced if it came from the server OR was explicitly
1930
- // marked as not dirty (isDirty: false), covering all sync-related cases.
1931
2053
  if (
1932
2054
  shadowMeta?.stateSource === 'server' ||
1933
2055
  shadowMeta?.isDirty === false
@@ -1935,20 +2057,15 @@ function createProxyHandler<T>(
1935
2057
  return 'synced';
1936
2058
  }
1937
2059
 
1938
- // Priority 3: Items restored from localStorage.
1939
2060
  if (shadowMeta?.stateSource === 'localStorage') {
1940
2061
  return 'restored';
1941
2062
  }
1942
2063
 
1943
- // Priority 4: Items from default/initial state.
1944
2064
  if (shadowMeta?.stateSource === 'default') {
1945
2065
  return 'fresh';
1946
2066
  }
1947
2067
 
1948
- // REMOVED the redundant "root" check. The item's own `stateSource` is sufficient.
1949
-
1950
- // Priority 5: A value exists but has no metadata. This is a fallback.
1951
- if (value !== undefined && !shadowMeta) {
2068
+ if (value !== undefined) {
1952
2069
  return 'fresh';
1953
2070
  }
1954
2071
 
@@ -2425,7 +2542,7 @@ function createProxyHandler<T>(
2425
2542
  path,
2426
2543
  meta
2427
2544
  );
2428
-
2545
+ registerComponentDependency(stateKey, componentId, path);
2429
2546
  if (!arrayKeys || !Array.isArray(shadowValue)) {
2430
2547
  return []; // It's valid to map over an empty array.
2431
2548
  }
@@ -2776,8 +2893,6 @@ function createProxyHandler<T>(
2776
2893
 
2777
2894
  if (!Array.isArray(currentState)) return [];
2778
2895
 
2779
- stateVersion++;
2780
-
2781
2896
  return rebuildStateShape({
2782
2897
  path: [...path, '[*]', fieldName],
2783
2898
  componentId: componentId!,
@@ -2839,7 +2954,18 @@ function createProxyHandler<T>(
2839
2954
  payload: InsertParams<InferArrayElement<T>>,
2840
2955
  index?: number
2841
2956
  ) => {
2842
- effectiveSetState(payload as any, path, { updateType: 'insert' });
2957
+ effectiveSetState(payload as any, path, {
2958
+ updateType: 'insert',
2959
+ index,
2960
+ });
2961
+ };
2962
+ }
2963
+ if (prop === '$insertMany') {
2964
+ return (payload: InferArrayElement<T>[]) => {
2965
+ // Call the one true path for all state changes.
2966
+ effectiveSetState(payload as any, path, {
2967
+ updateType: 'insert_many',
2968
+ });
2843
2969
  };
2844
2970
  }
2845
2971
  if (prop === '$uniqueInsert') {
@@ -2887,6 +3013,7 @@ function createProxyHandler<T>(
2887
3013
  if (prop === '$cut') {
2888
3014
  return (index?: number, options?: { waitForSync?: boolean }) => {
2889
3015
  const shadowMeta = getShadowMetadata(stateKey, path);
3016
+ console.log('shadowMeta ->>>>>>>>>>>>>>>>', shadowMeta);
2890
3017
  if (!shadowMeta?.arrayKeys || shadowMeta.arrayKeys.length === 0)
2891
3018
  return;
2892
3019
 
@@ -2897,8 +3024,11 @@ function createProxyHandler<T>(
2897
3024
  ? index
2898
3025
  : shadowMeta.arrayKeys.length - 1;
2899
3026
 
3027
+ console.log('indexToCut ->>>>>>>>>>>>>>>>', indexToCut);
3028
+
2900
3029
  const idToCut = shadowMeta.arrayKeys[indexToCut];
2901
3030
  if (!idToCut) return;
3031
+ console.log('idToCut ->>>>>>>>>>>>>>>>', idToCut);
2902
3032
 
2903
3033
  effectiveSetState(null, [...path, idToCut], {
2904
3034
  updateType: 'cut',
@@ -3000,11 +3130,7 @@ function createProxyHandler<T>(
3000
3130
  });
3001
3131
  }
3002
3132
 
3003
- return rebuildStateShape({
3004
- path: [...path, `not_found_${uuidv4()}`],
3005
- componentId: componentId!,
3006
- meta,
3007
- });
3133
+ return null;
3008
3134
  };
3009
3135
  }
3010
3136
  if (prop === '$cutThis') {
@@ -3034,10 +3160,59 @@ function createProxyHandler<T>(
3034
3160
  });
3035
3161
  }
3036
3162
 
3163
+ // if (prop === '$formInput') {
3164
+ // const _getFormElement = (path: string[]): HTMLElement | null => {
3165
+ // const metadata = getShadowMetadata(stateKey, path);
3166
+ // if (metadata?.formRef?.current) {
3167
+ // return metadata.formRef.current;
3168
+ // }
3169
+ // // This warning is helpful for debugging if a ref is missing.
3170
+ // console.warn(
3171
+ // `Form element ref not found for stateKey "${stateKey}" at path "${path.join('.')}"`
3172
+ // );
3173
+ // return null;
3174
+ // };
3175
+ // return {
3176
+ // setDisabled: (isDisabled: boolean) => {
3177
+ // const element = _getFormElement(path) as HTMLInputElement | null;
3178
+ // if (element) {
3179
+ // element.disabled = isDisabled;
3180
+ // }
3181
+ // },
3182
+ // focus: () => {
3183
+ // const element = _getFormElement(path);
3184
+ // element?.focus();
3185
+ // },
3186
+ // blur: () => {
3187
+ // const element = _getFormElement(path);
3188
+ // element?.blur();
3189
+ // },
3190
+ // scrollIntoView: (options?: ScrollIntoViewOptions) => {
3191
+ // const element = _getFormElement(path);
3192
+ // element?.scrollIntoView(
3193
+ // options ?? { behavior: 'smooth', block: 'center' }
3194
+ // );
3195
+ // },
3196
+ // click: () => {
3197
+ // const element = _getFormElement(path);
3198
+ // element?.click();
3199
+ // },
3200
+ // selectText: () => {
3201
+ // const element = _getFormElement(path) as
3202
+ // | HTMLInputElement
3203
+ // | HTMLTextAreaElement
3204
+ // | null;
3205
+ // if (element && typeof element.select === 'function') {
3206
+ // element.select();
3207
+ // }
3208
+ // },
3209
+ // };
3210
+ // }
3037
3211
  if (prop === '$$get') {
3038
3212
  return () =>
3039
3213
  $cogsSignal({ _stateKey: stateKey, _path: path, _meta: meta });
3040
3214
  }
3215
+
3041
3216
  if (prop === '$lastSynced') {
3042
3217
  const syncKey = `${stateKey}:${path.join('.')}`;
3043
3218
  return getSyncInfo(syncKey);
@@ -3109,8 +3284,43 @@ function createProxyHandler<T>(
3109
3284
  return componentId;
3110
3285
  }
3111
3286
  if (path.length == 0) {
3287
+ if (prop === '$setOptions') {
3288
+ return (options: OptionsType<T>) => {
3289
+ setOptions({ stateKey, options, initialOptionsPart: {} });
3290
+ };
3291
+ }
3292
+
3293
+ if (prop === '$_applyUpdate') {
3294
+ return (
3295
+ value: any,
3296
+ path: string[],
3297
+ updateType: 'update' | 'insert' | 'cut' = 'update'
3298
+ ) => {
3299
+ effectiveSetState(value, path, { updateType });
3300
+ };
3301
+ }
3302
+
3303
+ if (prop === '$_getEffectiveSetState') {
3304
+ return effectiveSetState;
3305
+ }
3306
+ if (prop === '$getPluginMetaData') {
3307
+ return (pluginName: string) =>
3308
+ getPluginMetaDataMap(stateKey, path)?.get(pluginName);
3309
+ }
3310
+ if (prop === '$addPluginMetaData') {
3311
+ console.log('$addPluginMetaDat');
3312
+ return (pluginName: string, data: Record<string, any>) =>
3313
+ setPluginMetaData(stateKey, path, pluginName, data);
3314
+ }
3315
+ if (prop === '$removePluginMetaData') {
3316
+ return (pluginName: string) =>
3317
+ removePluginMetaData(stateKey, path, pluginName);
3318
+ }
3112
3319
  if (prop === '$addZodValidation') {
3113
- return (zodErrors: any[]) => {
3320
+ return (
3321
+ zodErrors: any[],
3322
+ source: 'client' | 'sync_engine' | 'api'
3323
+ ) => {
3114
3324
  zodErrors.forEach((error) => {
3115
3325
  const currentMeta =
3116
3326
  getGlobalStore
@@ -3125,7 +3335,7 @@ function createProxyHandler<T>(
3125
3335
  status: 'INVALID',
3126
3336
  errors: [
3127
3337
  {
3128
- source: 'client',
3338
+ source: source || 'client',
3129
3339
  message: error.message,
3130
3340
  severity: 'error',
3131
3341
  code: error.code,
@@ -3156,6 +3366,72 @@ function createProxyHandler<T>(
3156
3366
  });
3157
3367
  };
3158
3368
  }
3369
+
3370
+ if (prop === '$applyOperation') {
3371
+ return (
3372
+ operation: UpdateTypeDetail & {
3373
+ validation?: any[];
3374
+ version?: string;
3375
+ },
3376
+ metaData?: Record<string, any>
3377
+ ) => {
3378
+ // const validationErrorsFromServer = operation.validation || [];
3379
+
3380
+ // if (!operation || !operation.path) {
3381
+ // console.error(
3382
+ // 'Invalid operation received by $applyOperation:',
3383
+ // operation
3384
+ // );
3385
+ // return;
3386
+ // }
3387
+
3388
+ // const newErrors: ValidationError[] =
3389
+ // validationErrorsFromServer.map((err) => ({
3390
+ // source: 'sync_engine',
3391
+ // message: err.message,
3392
+ // severity: 'warning',
3393
+ // code: err.code,
3394
+ // }));
3395
+ // console.log('newErrors', newErrors);
3396
+ // getGlobalStore
3397
+ // .getState()
3398
+ // .setShadowMetadata(stateKey, operation.path, {
3399
+ // validation: {
3400
+ // status: newErrors.length > 0 ? 'INVALID' : 'VALID',
3401
+ // errors: newErrors,
3402
+ // lastValidated: Date.now(),
3403
+ // },
3404
+ // });
3405
+ console.log(
3406
+ 'getGlobalStore',
3407
+ getGlobalStore
3408
+ .getState()
3409
+ .getShadowMetadata(stateKey, operation.path)
3410
+ );
3411
+ let index: number | undefined;
3412
+ if (
3413
+ operation.insertAfterId &&
3414
+ operation.updateType === 'insert'
3415
+ ) {
3416
+ const shadowMeta = getShadowMetadata(stateKey, operation.path);
3417
+ if (shadowMeta?.arrayKeys) {
3418
+ const afterIndex = shadowMeta.arrayKeys.indexOf(
3419
+ operation.insertAfterId
3420
+ );
3421
+ if (afterIndex !== -1) {
3422
+ index = afterIndex + 1; // Insert after the found item
3423
+ }
3424
+ }
3425
+ }
3426
+
3427
+ effectiveSetState(operation.newValue, operation.path, {
3428
+ updateType: operation.updateType,
3429
+ itemId: operation.itemId,
3430
+ index, // Pass the calculated index
3431
+ metaData,
3432
+ });
3433
+ };
3434
+ }
3159
3435
  if (prop === '$applyJsonPatch') {
3160
3436
  return (patches: Operation[]) => {
3161
3437
  const store = getGlobalStore.getState();
@@ -3250,14 +3526,8 @@ function createProxyHandler<T>(
3250
3526
 
3251
3527
  if (prop === '$getComponents')
3252
3528
  return () => getShadowMetadata(stateKey, [])?.components;
3253
- if (prop === '$getAllFormRefs')
3254
- return () =>
3255
- formRefStore.getState().getFormRefsByStateKey(stateKey);
3256
- }
3257
- if (prop === '$getFormRef') {
3258
- return () =>
3259
- formRefStore.getState().getFormRef(stateKey + '.' + path.join('.'));
3260
3529
  }
3530
+
3261
3531
  if (prop === '$validationWrapper') {
3262
3532
  return ({
3263
3533
  children,
@@ -3277,6 +3547,7 @@ function createProxyHandler<T>(
3277
3547
  </ValidationWrapper>
3278
3548
  );
3279
3549
  }
3550
+
3280
3551
  if (prop === '$_stateKey') return stateKey;
3281
3552
  if (prop === '$_path') return path;
3282
3553
  if (prop === '$update') {
@@ -3289,7 +3560,6 @@ function createProxyHandler<T>(
3289
3560
  .getState()
3290
3561
  .getShadowMetadata(stateKey, path);
3291
3562
 
3292
- // Update the metadata for this specific path
3293
3563
  setShadowMetadata(stateKey, path, {
3294
3564
  ...shadowMeta,
3295
3565
  isDirty: false,
@@ -3297,7 +3567,6 @@ function createProxyHandler<T>(
3297
3567
  lastServerSync: Date.now(),
3298
3568
  });
3299
3569
 
3300
- // Notify any components that might be subscribed to the sync status
3301
3570
  const fullPath = [stateKey, ...path].join('.');
3302
3571
  notifyPathSubscribers(fullPath, {
3303
3572
  type: 'SYNC_STATUS_CHANGE',
@@ -3371,21 +3640,15 @@ function createProxyHandler<T>(
3371
3640
  .getShadowMetadata(stateKey, []);
3372
3641
  let revertState;
3373
3642
 
3374
- // Determine the correct state to revert to (same logic as before)
3375
3643
  if (shadowMeta?.stateSource === 'server' && shadowMeta.baseServerState) {
3376
3644
  revertState = shadowMeta.baseServerState;
3377
3645
  } else {
3378
3646
  revertState = getGlobalStore.getState().initialStateGlobal[stateKey];
3379
3647
  }
3380
3648
 
3381
- // Perform necessary cleanup
3382
3649
  clearSelectedIndexesForState(stateKey);
3383
-
3384
- // FIX 1: Use the IMMEDIATE, SYNCHRONOUS state reset function.
3385
- // This is what your tests expect for a clean slate.
3386
3650
  initializeShadowState(stateKey, revertState);
3387
3651
 
3388
- // Rebuild the proxy's internal shape after the reset
3389
3652
  rebuildStateShape({
3390
3653
  path: [],
3391
3654
  componentId: outerComponentId!,
@@ -3401,15 +3664,15 @@ function createProxyHandler<T>(
3401
3664
  localStorage.removeItem(storageKey);
3402
3665
  }
3403
3666
 
3404
- // FIX 2: Use the library's BATCHED notification system instead of a manual forceUpdate loop.
3405
- // This fixes the original infinite loop bug safely.
3406
3667
  notifyComponents(stateKey);
3407
3668
 
3408
3669
  return revertState;
3409
3670
  },
3671
+ $initializeAndMergeShadowState: (newState: T) => {
3672
+ initializeAndMergeShadowState(stateKey, newState);
3673
+ notifyComponents(stateKey);
3674
+ },
3410
3675
  $updateInitialState: (newState: T) => {
3411
- stateVersion++;
3412
-
3413
3676
  const newUpdaterState = createProxyHandler(
3414
3677
  stateKey,
3415
3678
  effectiveSetState,
@@ -3488,7 +3751,6 @@ function SignalRenderer({
3488
3751
 
3489
3752
  const value = getShadowValue(proxy._stateKey, proxy._path, viewIds);
3490
3753
 
3491
- // Setup effect - runs only once
3492
3754
  useEffect(() => {
3493
3755
  const element = elementRef.current;
3494
3756
  if (!element || isSetupRef.current) return;
@@ -3511,7 +3773,6 @@ function SignalRenderer({
3511
3773
 
3512
3774
  instanceIdRef.current = `instance-${crypto.randomUUID()}`;
3513
3775
 
3514
- // Store signal info in shadow metadata
3515
3776
  const currentMeta =
3516
3777
  getGlobalStore
3517
3778
  .getState()