@oxog/state 1.0.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +261 -55
- package/dist/iife/index.global.js +11 -11
- package/dist/index.cjs +2366 -518
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1784 -64
- package/dist/index.d.ts +1784 -64
- package/dist/index.js +2321 -518
- package/dist/index.js.map +1 -1
- package/package.json +11 -4
package/dist/index.d.ts
CHANGED
|
@@ -478,6 +478,741 @@ declare function useCreateStore<TState>(initialState: TState): Store<TState>;
|
|
|
478
478
|
* ```
|
|
479
479
|
*/
|
|
480
480
|
declare function useAction<TState, TAction extends (...args: any[]) => any>(store: Store<TState>, actionName: string): TAction;
|
|
481
|
+
/**
|
|
482
|
+
* Create a shallow equality wrapper for selectors.
|
|
483
|
+
*
|
|
484
|
+
* Use this hook to wrap selectors that return objects or arrays
|
|
485
|
+
* and you want to avoid re-renders when the content hasn't changed.
|
|
486
|
+
*
|
|
487
|
+
* @param selector - The selector function to wrap
|
|
488
|
+
* @returns A wrapped selector with shallow equality
|
|
489
|
+
*
|
|
490
|
+
* @example
|
|
491
|
+
* ```typescript
|
|
492
|
+
* import { useStore, useShallow } from '@oxog/state';
|
|
493
|
+
*
|
|
494
|
+
* function UserInfo() {
|
|
495
|
+
* // Without useShallow - re-renders on every state change
|
|
496
|
+
* const { name, email } = useStore(store, s => ({ name: s.name, email: s.email }));
|
|
497
|
+
*
|
|
498
|
+
* // With useShallow - only re-renders when name or email actually changes
|
|
499
|
+
* const { name, email } = useStore(
|
|
500
|
+
* store,
|
|
501
|
+
* useShallow(s => ({ name: s.name, email: s.email }))
|
|
502
|
+
* );
|
|
503
|
+
* }
|
|
504
|
+
* ```
|
|
505
|
+
*/
|
|
506
|
+
declare function useShallow<TState, TSelected extends object>(selector: Selector<TState, TSelected>): Selector<TState, TSelected>;
|
|
507
|
+
/**
|
|
508
|
+
* Hook for selecting multiple actions from a store.
|
|
509
|
+
*
|
|
510
|
+
* Returns stable references to actions that don't change between renders.
|
|
511
|
+
*
|
|
512
|
+
* @param store - The store instance
|
|
513
|
+
* @param actionNames - Names of actions to select
|
|
514
|
+
* @returns An object with the selected actions
|
|
515
|
+
*
|
|
516
|
+
* @example
|
|
517
|
+
* ```typescript
|
|
518
|
+
* import { useStoreActions } from '@oxog/state';
|
|
519
|
+
*
|
|
520
|
+
* function Counter() {
|
|
521
|
+
* const { increment, decrement, reset } = useStoreActions(
|
|
522
|
+
* store,
|
|
523
|
+
* 'increment',
|
|
524
|
+
* 'decrement',
|
|
525
|
+
* 'reset'
|
|
526
|
+
* );
|
|
527
|
+
*
|
|
528
|
+
* return (
|
|
529
|
+
* <>
|
|
530
|
+
* <button onClick={decrement}>-</button>
|
|
531
|
+
* <button onClick={increment}>+</button>
|
|
532
|
+
* <button onClick={reset}>Reset</button>
|
|
533
|
+
* </>
|
|
534
|
+
* );
|
|
535
|
+
* }
|
|
536
|
+
* ```
|
|
537
|
+
*/
|
|
538
|
+
declare function useStoreActions<TState, TKeys extends string[]>(store: Store<TState>, ...actionNames: TKeys): {
|
|
539
|
+
[K in TKeys[number]]: (...args: any[]) => any;
|
|
540
|
+
};
|
|
541
|
+
/**
|
|
542
|
+
* Hook for selecting multiple values with independent selectors.
|
|
543
|
+
*
|
|
544
|
+
* Each selector is tracked independently, so the component only
|
|
545
|
+
* re-renders when a selector's output actually changes.
|
|
546
|
+
*
|
|
547
|
+
* @param store - The store instance
|
|
548
|
+
* @param selectors - Object of named selectors
|
|
549
|
+
* @returns An object with selected values
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* ```typescript
|
|
553
|
+
* import { useStoreSelector } from '@oxog/state';
|
|
554
|
+
*
|
|
555
|
+
* function Dashboard() {
|
|
556
|
+
* const { userCount, activeUsers, totalRevenue } = useStoreSelector(store, {
|
|
557
|
+
* userCount: s => s.users.length,
|
|
558
|
+
* activeUsers: s => s.users.filter(u => u.active).length,
|
|
559
|
+
* totalRevenue: s => s.orders.reduce((sum, o) => sum + o.total, 0),
|
|
560
|
+
* });
|
|
561
|
+
*
|
|
562
|
+
* return (
|
|
563
|
+
* <div>
|
|
564
|
+
* <p>Users: {userCount} ({activeUsers} active)</p>
|
|
565
|
+
* <p>Revenue: ${totalRevenue}</p>
|
|
566
|
+
* </div>
|
|
567
|
+
* );
|
|
568
|
+
* }
|
|
569
|
+
* ```
|
|
570
|
+
*/
|
|
571
|
+
declare function useStoreSelector<TState, TSelectors extends Record<string, Selector<TState, any>>>(store: Store<TState>, selectors: TSelectors): {
|
|
572
|
+
[K in keyof TSelectors]: ReturnType<TSelectors[K]>;
|
|
573
|
+
};
|
|
574
|
+
/**
|
|
575
|
+
* Hook for transient updates - subscribe without causing re-renders.
|
|
576
|
+
*
|
|
577
|
+
* Use this for side effects that don't need to update the UI.
|
|
578
|
+
*
|
|
579
|
+
* @param store - The store instance
|
|
580
|
+
* @param selector - Selector function
|
|
581
|
+
* @param callback - Callback to run on changes
|
|
582
|
+
*
|
|
583
|
+
* @example
|
|
584
|
+
* ```typescript
|
|
585
|
+
* import { useTransientSubscribe } from '@oxog/state';
|
|
586
|
+
*
|
|
587
|
+
* function Analytics() {
|
|
588
|
+
* // Track page views without re-rendering
|
|
589
|
+
* useTransientSubscribe(
|
|
590
|
+
* store,
|
|
591
|
+
* s => s.currentPage,
|
|
592
|
+
* (page) => analytics.track('pageview', { page })
|
|
593
|
+
* );
|
|
594
|
+
*
|
|
595
|
+
* return <div>...</div>;
|
|
596
|
+
* }
|
|
597
|
+
* ```
|
|
598
|
+
*/
|
|
599
|
+
declare function useTransientSubscribe<TState, TSelected>(store: Store<TState>, selector: Selector<TState, TSelected>, callback: (value: TSelected, prevValue: TSelected) => void, equalityFn?: EqualityFn<TSelected>): void;
|
|
600
|
+
/**
|
|
601
|
+
* Hook that returns the store's setState function with stable reference.
|
|
602
|
+
*
|
|
603
|
+
* @param store - The store instance
|
|
604
|
+
* @returns The setState function
|
|
605
|
+
*
|
|
606
|
+
* @example
|
|
607
|
+
* ```typescript
|
|
608
|
+
* function Form() {
|
|
609
|
+
* const setState = useSetState(store);
|
|
610
|
+
*
|
|
611
|
+
* const handleChange = (e) => {
|
|
612
|
+
* setState({ [e.target.name]: e.target.value });
|
|
613
|
+
* };
|
|
614
|
+
*
|
|
615
|
+
* return <input onChange={handleChange} />;
|
|
616
|
+
* }
|
|
617
|
+
* ```
|
|
618
|
+
*/
|
|
619
|
+
declare function useSetState<TState>(store: Store<TState>): (partial: Partial<TState> | ((state: TState) => Partial<TState>)) => void;
|
|
620
|
+
|
|
621
|
+
/**
|
|
622
|
+
* Enhanced subscription system with advanced options.
|
|
623
|
+
*
|
|
624
|
+
* @module subscribe
|
|
625
|
+
*/
|
|
626
|
+
|
|
627
|
+
/**
|
|
628
|
+
* Options for enhanced subscriptions.
|
|
629
|
+
*
|
|
630
|
+
* @typeParam T - The store state type
|
|
631
|
+
* @typeParam U - The selected value type
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```typescript
|
|
635
|
+
* const options: SubscribeOptions<State, number> = {
|
|
636
|
+
* debounce: 100,
|
|
637
|
+
* fireImmediately: true,
|
|
638
|
+
* when: (state) => state.isReady
|
|
639
|
+
* };
|
|
640
|
+
* ```
|
|
641
|
+
*/
|
|
642
|
+
interface SubscribeOptions<T, U = T> {
|
|
643
|
+
/** Custom equality function for comparing selected values */
|
|
644
|
+
equalityFn?: EqualityFn<U>;
|
|
645
|
+
/** Fire callback immediately with current state */
|
|
646
|
+
fireImmediately?: boolean;
|
|
647
|
+
/** Debounce notifications (ms) */
|
|
648
|
+
debounce?: number;
|
|
649
|
+
/** Throttle notifications (ms) */
|
|
650
|
+
throttle?: number;
|
|
651
|
+
/** Only fire when condition is met */
|
|
652
|
+
when?: (state: T, prevState: T) => boolean;
|
|
653
|
+
}
|
|
654
|
+
/**
|
|
655
|
+
* Enhanced subscribe function with advanced options.
|
|
656
|
+
*
|
|
657
|
+
* @param store - The store to subscribe to
|
|
658
|
+
* @param selectorOrListener - Selector function or direct listener
|
|
659
|
+
* @param listenerOrOptions - Listener function or options (if no selector)
|
|
660
|
+
* @param options - Subscription options
|
|
661
|
+
* @returns Unsubscribe function
|
|
662
|
+
*
|
|
663
|
+
* @example
|
|
664
|
+
* ```typescript
|
|
665
|
+
* // With debounce
|
|
666
|
+
* const unsubscribe = subscribeWithOptions(
|
|
667
|
+
* store,
|
|
668
|
+
* (state) => state.searchQuery,
|
|
669
|
+
* (query) => performSearch(query),
|
|
670
|
+
* { debounce: 300 }
|
|
671
|
+
* );
|
|
672
|
+
*
|
|
673
|
+
* // With throttle and condition
|
|
674
|
+
* const unsubscribe = subscribeWithOptions(
|
|
675
|
+
* store,
|
|
676
|
+
* (state) => state.scrollPosition,
|
|
677
|
+
* (pos) => updateUI(pos),
|
|
678
|
+
* { throttle: 16, when: (state) => state.isVisible }
|
|
679
|
+
* );
|
|
680
|
+
*
|
|
681
|
+
* // Fire immediately
|
|
682
|
+
* const unsubscribe = subscribeWithOptions(
|
|
683
|
+
* store,
|
|
684
|
+
* (state) => state.user,
|
|
685
|
+
* (user) => console.log('User:', user),
|
|
686
|
+
* { fireImmediately: true }
|
|
687
|
+
* );
|
|
688
|
+
* ```
|
|
689
|
+
*/
|
|
690
|
+
declare function subscribeWithOptions<TState, TSelected>(store: Store<TState>, selector: Selector<TState, TSelected>, listener: Listener<TSelected>, options?: SubscribeOptions<TState, TSelected>): () => void;
|
|
691
|
+
/**
|
|
692
|
+
* Create a subscription factory with preset options.
|
|
693
|
+
*
|
|
694
|
+
* @param defaultOptions - Default options for all subscriptions
|
|
695
|
+
* @returns A subscribe function with preset options
|
|
696
|
+
*
|
|
697
|
+
* @example
|
|
698
|
+
* ```typescript
|
|
699
|
+
* const debouncedSubscribe = createSubscriber({ debounce: 100 });
|
|
700
|
+
*
|
|
701
|
+
* const unsubscribe = debouncedSubscribe(
|
|
702
|
+
* store,
|
|
703
|
+
* (state) => state.input,
|
|
704
|
+
* (input) => validate(input)
|
|
705
|
+
* );
|
|
706
|
+
* ```
|
|
707
|
+
*/
|
|
708
|
+
declare function createSubscriber<TState>(defaultOptions?: SubscribeOptions<TState, any>): <TSelected>(store: Store<TState>, selector: Selector<TState, TSelected>, listener: Listener<TSelected>, options?: SubscribeOptions<TState, TSelected>) => () => void;
|
|
709
|
+
/**
|
|
710
|
+
* Subscribe to multiple selectors with a single listener.
|
|
711
|
+
*
|
|
712
|
+
* @param store - The store to subscribe to
|
|
713
|
+
* @param selectors - Object of named selectors
|
|
714
|
+
* @param listener - Listener called when any selector value changes
|
|
715
|
+
* @param options - Subscription options
|
|
716
|
+
* @returns Unsubscribe function
|
|
717
|
+
*
|
|
718
|
+
* @example
|
|
719
|
+
* ```typescript
|
|
720
|
+
* const unsubscribe = subscribeToMany(
|
|
721
|
+
* store,
|
|
722
|
+
* {
|
|
723
|
+
* count: (s) => s.count,
|
|
724
|
+
* name: (s) => s.name,
|
|
725
|
+
* },
|
|
726
|
+
* ({ count, name }) => console.log(`${name}: ${count}`)
|
|
727
|
+
* );
|
|
728
|
+
* ```
|
|
729
|
+
*/
|
|
730
|
+
declare function subscribeToMany<TState, TSelectors extends Record<string, Selector<TState, any>>>(store: Store<TState>, selectors: TSelectors, listener: (values: {
|
|
731
|
+
[K in keyof TSelectors]: ReturnType<TSelectors[K]>;
|
|
732
|
+
}) => void, options?: Omit<SubscribeOptions<TState, any>, 'equalityFn'>): () => void;
|
|
733
|
+
/**
|
|
734
|
+
* Subscribe only once - automatically unsubscribes after first call.
|
|
735
|
+
*
|
|
736
|
+
* @param store - The store to subscribe to
|
|
737
|
+
* @param selector - Selector function
|
|
738
|
+
* @param listener - Listener called once when value changes
|
|
739
|
+
* @param options - Subscription options
|
|
740
|
+
* @returns Unsubscribe function (can be called to cancel before trigger)
|
|
741
|
+
*
|
|
742
|
+
* @example
|
|
743
|
+
* ```typescript
|
|
744
|
+
* // Wait for user to be loaded
|
|
745
|
+
* subscribeOnce(
|
|
746
|
+
* store,
|
|
747
|
+
* (state) => state.user,
|
|
748
|
+
* (user) => console.log('User loaded:', user),
|
|
749
|
+
* { when: (state) => state.user !== null }
|
|
750
|
+
* );
|
|
751
|
+
* ```
|
|
752
|
+
*/
|
|
753
|
+
declare function subscribeOnce<TState, TSelected>(store: Store<TState>, selector: Selector<TState, TSelected>, listener: Listener<TSelected>, options?: SubscribeOptions<TState, TSelected>): () => void;
|
|
754
|
+
|
|
755
|
+
/**
|
|
756
|
+
* Computed values (derived state) with memoization.
|
|
757
|
+
*
|
|
758
|
+
* @module computed
|
|
759
|
+
*/
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Options for computed values.
|
|
763
|
+
*
|
|
764
|
+
* @typeParam T - The store state type
|
|
765
|
+
* @typeParam U - The computed value type
|
|
766
|
+
*/
|
|
767
|
+
interface ComputedOptions<T, U> {
|
|
768
|
+
/** Custom equality for caching */
|
|
769
|
+
equals?: EqualityFn<U>;
|
|
770
|
+
/** Lazy evaluation (only compute when accessed) */
|
|
771
|
+
lazy?: boolean;
|
|
772
|
+
/** Keep last N values in cache for time-travel debugging */
|
|
773
|
+
cacheSize?: number;
|
|
774
|
+
}
|
|
775
|
+
/**
|
|
776
|
+
* Computed value interface with getter and utilities.
|
|
777
|
+
*/
|
|
778
|
+
interface Computed<U> {
|
|
779
|
+
/** Get the computed value */
|
|
780
|
+
(): U;
|
|
781
|
+
/** Get value (alias) */
|
|
782
|
+
get(): U;
|
|
783
|
+
/** Force recomputation */
|
|
784
|
+
invalidate(): void;
|
|
785
|
+
/** Subscribe to value changes */
|
|
786
|
+
subscribe(listener: (value: U, prevValue: U) => void): () => void;
|
|
787
|
+
/** Get cache history (if cacheSize > 1) */
|
|
788
|
+
getHistory(): U[];
|
|
789
|
+
/** Destroy and cleanup */
|
|
790
|
+
destroy(): void;
|
|
791
|
+
}
|
|
792
|
+
/**
|
|
793
|
+
* Create a computed (derived) value from store state.
|
|
794
|
+
*
|
|
795
|
+
* Computed values are memoized and only recompute when dependencies change.
|
|
796
|
+
*
|
|
797
|
+
* @param store - The store to derive from
|
|
798
|
+
* @param selector - Function to compute the derived value
|
|
799
|
+
* @param options - Computed options
|
|
800
|
+
* @returns A computed value accessor
|
|
801
|
+
*
|
|
802
|
+
* @example
|
|
803
|
+
* ```typescript
|
|
804
|
+
* const store = createStore({ items: [], filter: 'all' });
|
|
805
|
+
*
|
|
806
|
+
* // Simple computed
|
|
807
|
+
* const filteredItems = computed(store, (state) => {
|
|
808
|
+
* if (state.filter === 'all') return state.items;
|
|
809
|
+
* return state.items.filter(item => item.status === state.filter);
|
|
810
|
+
* });
|
|
811
|
+
*
|
|
812
|
+
* console.log(filteredItems()); // Get computed value
|
|
813
|
+
*
|
|
814
|
+
* // With caching for debugging
|
|
815
|
+
* const total = computed(
|
|
816
|
+
* store,
|
|
817
|
+
* (state) => state.items.reduce((sum, item) => sum + item.price, 0),
|
|
818
|
+
* { cacheSize: 10 }
|
|
819
|
+
* );
|
|
820
|
+
*
|
|
821
|
+
* console.log(total.getHistory()); // See past values
|
|
822
|
+
* ```
|
|
823
|
+
*/
|
|
824
|
+
declare function computed<TState, TComputed>(store: Store<TState>, selector: Selector<TState, TComputed>, options?: ComputedOptions<TState, TComputed>): Computed<TComputed>;
|
|
825
|
+
/**
|
|
826
|
+
* Create a computed value that depends on other computed values.
|
|
827
|
+
*
|
|
828
|
+
* @param computeds - Array of computed values to depend on
|
|
829
|
+
* @param combiner - Function to combine the computed values
|
|
830
|
+
* @param options - Computed options
|
|
831
|
+
* @returns A combined computed value
|
|
832
|
+
*
|
|
833
|
+
* @example
|
|
834
|
+
* ```typescript
|
|
835
|
+
* const subtotal = computed(store, (s) => s.items.reduce((sum, i) => sum + i.price, 0));
|
|
836
|
+
* const tax = computed(store, (s) => s.taxRate);
|
|
837
|
+
*
|
|
838
|
+
* const total = combineComputed(
|
|
839
|
+
* [subtotal, tax],
|
|
840
|
+
* ([subtotalVal, taxVal]) => subtotalVal * (1 + taxVal)
|
|
841
|
+
* );
|
|
842
|
+
*
|
|
843
|
+
* console.log(total()); // Subtotal + tax
|
|
844
|
+
* ```
|
|
845
|
+
*/
|
|
846
|
+
declare function combineComputed<TInputs extends Computed<any>[], TOutput>(computeds: [...TInputs], combiner: (values: {
|
|
847
|
+
[K in keyof TInputs]: TInputs[K] extends Computed<infer U> ? U : never;
|
|
848
|
+
}) => TOutput, options?: Omit<ComputedOptions<any, TOutput>, 'lazy'>): Computed<TOutput>;
|
|
849
|
+
/**
|
|
850
|
+
* Create a memoized selector for use with store.subscribe.
|
|
851
|
+
*
|
|
852
|
+
* @param selector - The selector function to memoize
|
|
853
|
+
* @param equals - Custom equality function
|
|
854
|
+
* @returns A memoized selector
|
|
855
|
+
*
|
|
856
|
+
* @example
|
|
857
|
+
* ```typescript
|
|
858
|
+
* const selectExpensiveComputation = memoizeSelector(
|
|
859
|
+
* (state: State) => expensiveOperation(state.data)
|
|
860
|
+
* );
|
|
861
|
+
*
|
|
862
|
+
* store.subscribe(selectExpensiveComputation, (result) => {
|
|
863
|
+
* console.log('Result:', result);
|
|
864
|
+
* });
|
|
865
|
+
* ```
|
|
866
|
+
*/
|
|
867
|
+
declare function memoizeSelector<TState, TSelected>(selector: Selector<TState, TSelected>, equals?: EqualityFn<TSelected>): Selector<TState, TSelected>;
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Slices pattern for modular store composition.
|
|
871
|
+
*
|
|
872
|
+
* @module slices
|
|
873
|
+
*/
|
|
874
|
+
|
|
875
|
+
/**
|
|
876
|
+
* SetSlice function type for updating slice state.
|
|
877
|
+
*/
|
|
878
|
+
type SetSlice<TState> = {
|
|
879
|
+
(partial: Partial<TState>): void;
|
|
880
|
+
(updater: (state: TState) => Partial<TState>): void;
|
|
881
|
+
};
|
|
882
|
+
/**
|
|
883
|
+
* GetSlice function type for getting current state.
|
|
884
|
+
*/
|
|
885
|
+
type GetSlice<TState> = () => TState;
|
|
886
|
+
/**
|
|
887
|
+
* Slice creator function signature.
|
|
888
|
+
*
|
|
889
|
+
* @typeParam TState - The full store state type
|
|
890
|
+
* @typeParam TSlice - The slice state type
|
|
891
|
+
*/
|
|
892
|
+
type SliceCreator<TState, TSlice extends Partial<TState>> = (set: SetSlice<TState>, get: GetSlice<TState>, store: Store<TState>) => TSlice;
|
|
893
|
+
/**
|
|
894
|
+
* Slice definition with state and actions.
|
|
895
|
+
*/
|
|
896
|
+
interface SliceDefinition<TState, TSlice extends Partial<TState>> {
|
|
897
|
+
/** Slice name for debugging */
|
|
898
|
+
name: string;
|
|
899
|
+
/** Slice creator function */
|
|
900
|
+
creator: SliceCreator<TState, TSlice>;
|
|
901
|
+
}
|
|
902
|
+
/**
|
|
903
|
+
* Create a slice definition for use with combineSlices.
|
|
904
|
+
*
|
|
905
|
+
* Slices are modular pieces of state and actions that can be combined
|
|
906
|
+
* to create a complete store.
|
|
907
|
+
*
|
|
908
|
+
* @param name - Slice name for debugging
|
|
909
|
+
* @param creator - Slice creator function
|
|
910
|
+
* @returns A slice definition
|
|
911
|
+
*
|
|
912
|
+
* @example
|
|
913
|
+
* ```typescript
|
|
914
|
+
* // Define a counter slice
|
|
915
|
+
* const counterSlice = createSlice('counter', (set, get) => ({
|
|
916
|
+
* count: 0,
|
|
917
|
+
* increment: () => set((s) => ({ count: s.count + 1 })),
|
|
918
|
+
* decrement: () => set((s) => ({ count: s.count - 1 })),
|
|
919
|
+
* reset: () => set({ count: 0 }),
|
|
920
|
+
* }));
|
|
921
|
+
*
|
|
922
|
+
* // Define a user slice
|
|
923
|
+
* const userSlice = createSlice('user', (set, get) => ({
|
|
924
|
+
* user: null as User | null,
|
|
925
|
+
* setUser: (user: User) => set({ user }),
|
|
926
|
+
* logout: () => set({ user: null }),
|
|
927
|
+
* }));
|
|
928
|
+
*
|
|
929
|
+
* // Combine slices into a store
|
|
930
|
+
* const store = combineSlices(counterSlice, userSlice);
|
|
931
|
+
*
|
|
932
|
+
* // Use the store
|
|
933
|
+
* store.increment();
|
|
934
|
+
* store.setUser({ name: 'John' });
|
|
935
|
+
* ```
|
|
936
|
+
*/
|
|
937
|
+
declare function createSlice<TState, TSlice extends Partial<TState>>(name: string, creator: SliceCreator<TState, TSlice>): SliceDefinition<TState, TSlice>;
|
|
938
|
+
/**
|
|
939
|
+
* Internal slice creator type for type inference.
|
|
940
|
+
*/
|
|
941
|
+
type InferSlice<T> = T extends SliceDefinition<any, infer S> ? S : never;
|
|
942
|
+
/**
|
|
943
|
+
* Combined state type from multiple slices.
|
|
944
|
+
*/
|
|
945
|
+
type CombinedState<TSlices extends SliceDefinition<any, any>[]> = {
|
|
946
|
+
[K in keyof TSlices]: InferSlice<TSlices[K]>;
|
|
947
|
+
}[number] extends infer U ? U extends object ? {
|
|
948
|
+
[K in keyof U as U extends Record<K, any> ? K : never]: U[K];
|
|
949
|
+
} : never : never;
|
|
950
|
+
/**
|
|
951
|
+
* Combine multiple slices into a single store.
|
|
952
|
+
*
|
|
953
|
+
* @param slices - Slice definitions to combine
|
|
954
|
+
* @returns A store with combined state and actions
|
|
955
|
+
*
|
|
956
|
+
* @example
|
|
957
|
+
* ```typescript
|
|
958
|
+
* const counterSlice = createSlice('counter', (set) => ({
|
|
959
|
+
* count: 0,
|
|
960
|
+
* increment: () => set((s) => ({ count: s.count + 1 })),
|
|
961
|
+
* }));
|
|
962
|
+
*
|
|
963
|
+
* const userSlice = createSlice('user', (set) => ({
|
|
964
|
+
* user: null,
|
|
965
|
+
* setUser: (user) => set({ user }),
|
|
966
|
+
* }));
|
|
967
|
+
*
|
|
968
|
+
* const store = combineSlices(counterSlice, userSlice);
|
|
969
|
+
*
|
|
970
|
+
* // Type-safe access to all slice methods
|
|
971
|
+
* store.increment();
|
|
972
|
+
* store.setUser({ name: 'John' });
|
|
973
|
+
* ```
|
|
974
|
+
*/
|
|
975
|
+
declare function combineSlices<TSlices extends SliceDefinition<any, any>[]>(...slices: TSlices): Store<CombinedState<TSlices>> & CombinedState<TSlices>;
|
|
976
|
+
/**
|
|
977
|
+
* Create a namespaced slice that prefixes all state keys.
|
|
978
|
+
*
|
|
979
|
+
* @param namespace - The namespace prefix
|
|
980
|
+
* @param creator - Slice creator function
|
|
981
|
+
* @returns A namespaced slice definition
|
|
982
|
+
*
|
|
983
|
+
* @example
|
|
984
|
+
* ```typescript
|
|
985
|
+
* const settingsSlice = createNamespacedSlice('settings', (set) => ({
|
|
986
|
+
* theme: 'dark',
|
|
987
|
+
* language: 'en',
|
|
988
|
+
* setTheme: (theme) => set({ theme }),
|
|
989
|
+
* }));
|
|
990
|
+
*
|
|
991
|
+
* // State will be: { settings: { theme: 'dark', language: 'en' } }
|
|
992
|
+
* // Actions will be: setTheme
|
|
993
|
+
* ```
|
|
994
|
+
*/
|
|
995
|
+
declare function createNamespacedSlice<TSlice extends Record<string, any>>(namespace: string, creator: (set: (partial: Partial<TSlice>) => void, get: () => TSlice) => TSlice): SliceDefinition<Record<string, any>, Record<string, any>>;
|
|
996
|
+
/**
|
|
997
|
+
* Extend an existing store with additional slices.
|
|
998
|
+
*
|
|
999
|
+
* @param store - The existing store
|
|
1000
|
+
* @param slices - Additional slices to add
|
|
1001
|
+
* @returns The extended store
|
|
1002
|
+
*
|
|
1003
|
+
* @example
|
|
1004
|
+
* ```typescript
|
|
1005
|
+
* const baseStore = createStore({ count: 0 });
|
|
1006
|
+
*
|
|
1007
|
+
* const extendedStore = extendStore(
|
|
1008
|
+
* baseStore,
|
|
1009
|
+
* createSlice('user', (set) => ({
|
|
1010
|
+
* user: null,
|
|
1011
|
+
* setUser: (user) => set({ user }),
|
|
1012
|
+
* }))
|
|
1013
|
+
* );
|
|
1014
|
+
* ```
|
|
1015
|
+
*/
|
|
1016
|
+
declare function extendStore<TState, TSlices extends SliceDefinition<TState & any, any>[]>(store: Store<TState>, ...slices: TSlices): Store<TState & CombinedState<TSlices>> & CombinedState<TSlices>;
|
|
1017
|
+
/**
|
|
1018
|
+
* Reset specific slices to their initial state.
|
|
1019
|
+
*
|
|
1020
|
+
* @param store - The store
|
|
1021
|
+
* @param sliceNames - Names of slices to reset
|
|
1022
|
+
*
|
|
1023
|
+
* @example
|
|
1024
|
+
* ```typescript
|
|
1025
|
+
* // Reset only the user slice
|
|
1026
|
+
* resetSlices(store, 'user');
|
|
1027
|
+
*
|
|
1028
|
+
* // Reset multiple slices
|
|
1029
|
+
* resetSlices(store, 'user', 'cart');
|
|
1030
|
+
* ```
|
|
1031
|
+
*/
|
|
1032
|
+
declare function resetSlices<TState>(store: Store<TState>, ...sliceNames: string[]): void;
|
|
1033
|
+
|
|
1034
|
+
/**
|
|
1035
|
+
* Store Federation - Multi-store coordination and communication.
|
|
1036
|
+
*
|
|
1037
|
+
* Enables coordinated updates across multiple stores with
|
|
1038
|
+
* atomic transactions and unified state access.
|
|
1039
|
+
*
|
|
1040
|
+
* @module federation
|
|
1041
|
+
*/
|
|
1042
|
+
|
|
1043
|
+
/**
|
|
1044
|
+
* Extract state type from a Store.
|
|
1045
|
+
*/
|
|
1046
|
+
type StoreState<S> = S extends Store<infer T> ? T : never;
|
|
1047
|
+
/**
|
|
1048
|
+
* Map of store names to their state types.
|
|
1049
|
+
*/
|
|
1050
|
+
type FederatedState<Stores extends Record<string, Store<any>>> = {
|
|
1051
|
+
[K in keyof Stores]: StoreState<Stores[K]>;
|
|
1052
|
+
};
|
|
1053
|
+
/**
|
|
1054
|
+
* Federation listener callback.
|
|
1055
|
+
*/
|
|
1056
|
+
type FederationListener<Stores extends Record<string, Store<any>>> = (state: FederatedState<Stores>, prevState: FederatedState<Stores>, changedStore: keyof Stores) => void;
|
|
1057
|
+
/**
|
|
1058
|
+
* Federation transaction function.
|
|
1059
|
+
*/
|
|
1060
|
+
type TransactionFn<Stores extends Record<string, Store<any>>> = (stores: Stores) => void | Promise<void>;
|
|
1061
|
+
/**
|
|
1062
|
+
* Federation options.
|
|
1063
|
+
*/
|
|
1064
|
+
interface FederationOptions {
|
|
1065
|
+
/** Name for debugging */
|
|
1066
|
+
name?: string;
|
|
1067
|
+
/** Enable transaction logging */
|
|
1068
|
+
debug?: boolean;
|
|
1069
|
+
}
|
|
1070
|
+
/**
|
|
1071
|
+
* Federation interface for coordinating multiple stores.
|
|
1072
|
+
*/
|
|
1073
|
+
interface Federation<Stores extends Record<string, Store<any>>> {
|
|
1074
|
+
/** Access to individual stores */
|
|
1075
|
+
readonly stores: Stores;
|
|
1076
|
+
/** Get combined state from all stores */
|
|
1077
|
+
getState(): FederatedState<Stores>;
|
|
1078
|
+
/** Subscribe to any store change */
|
|
1079
|
+
subscribe(listener: FederationListener<Stores>): () => void;
|
|
1080
|
+
/** Atomic multi-store update (synchronous) */
|
|
1081
|
+
transaction(fn: TransactionFn<Stores>): void;
|
|
1082
|
+
/** Async transaction with rollback on error */
|
|
1083
|
+
transactionAsync(fn: TransactionFn<Stores>): Promise<void>;
|
|
1084
|
+
/** Get a specific store */
|
|
1085
|
+
getStore<K extends keyof Stores>(name: K): Stores[K];
|
|
1086
|
+
/** Destroy federation and unsubscribe from all stores */
|
|
1087
|
+
destroy(): void;
|
|
1088
|
+
}
|
|
1089
|
+
/**
|
|
1090
|
+
* Create a federation of multiple stores.
|
|
1091
|
+
*
|
|
1092
|
+
* @param stores - Object containing named stores
|
|
1093
|
+
* @param options - Federation options
|
|
1094
|
+
* @returns A federation instance
|
|
1095
|
+
*
|
|
1096
|
+
* @example
|
|
1097
|
+
* ```typescript
|
|
1098
|
+
* import { createStore, createFederation } from '@oxog/state';
|
|
1099
|
+
*
|
|
1100
|
+
* // Create individual stores
|
|
1101
|
+
* const userStore = createStore({
|
|
1102
|
+
* user: null,
|
|
1103
|
+
* setUser: (state, user) => ({ user }),
|
|
1104
|
+
* });
|
|
1105
|
+
*
|
|
1106
|
+
* const cartStore = createStore({
|
|
1107
|
+
* items: [],
|
|
1108
|
+
* addItem: (state, item) => ({ items: [...state.items, item] }),
|
|
1109
|
+
* clear: () => ({ items: [] }),
|
|
1110
|
+
* });
|
|
1111
|
+
*
|
|
1112
|
+
* const settingsStore = createStore({
|
|
1113
|
+
* theme: 'light',
|
|
1114
|
+
* setTheme: (state, theme) => ({ theme }),
|
|
1115
|
+
* });
|
|
1116
|
+
*
|
|
1117
|
+
* // Create federation
|
|
1118
|
+
* const federation = createFederation({
|
|
1119
|
+
* user: userStore,
|
|
1120
|
+
* cart: cartStore,
|
|
1121
|
+
* settings: settingsStore,
|
|
1122
|
+
* });
|
|
1123
|
+
*
|
|
1124
|
+
* // Get combined state
|
|
1125
|
+
* const state = federation.getState();
|
|
1126
|
+
* // { user: { user: null, ... }, cart: { items: [], ... }, settings: { theme: 'light', ... } }
|
|
1127
|
+
*
|
|
1128
|
+
* // Subscribe to any store change
|
|
1129
|
+
* federation.subscribe((state, prevState, changedStore) => {
|
|
1130
|
+
* console.log(`${changedStore} changed:`, state[changedStore]);
|
|
1131
|
+
* });
|
|
1132
|
+
*
|
|
1133
|
+
* // Atomic cross-store transaction
|
|
1134
|
+
* federation.transaction(({ user, cart }) => {
|
|
1135
|
+
* cart.setState({ items: [] });
|
|
1136
|
+
* user.setState({ user: null });
|
|
1137
|
+
* });
|
|
1138
|
+
* ```
|
|
1139
|
+
*/
|
|
1140
|
+
declare function createFederation<Stores extends Record<string, Store<any>>>(stores: Stores, options?: FederationOptions): Federation<Stores>;
|
|
1141
|
+
/**
|
|
1142
|
+
* Create a selector that combines state from multiple stores.
|
|
1143
|
+
*
|
|
1144
|
+
* @param federation - The federation instance
|
|
1145
|
+
* @param selector - Selector function
|
|
1146
|
+
* @returns A function that returns the selected value
|
|
1147
|
+
*
|
|
1148
|
+
* @example
|
|
1149
|
+
* ```typescript
|
|
1150
|
+
* const getCartTotal = createFederatedSelector(
|
|
1151
|
+
* federation,
|
|
1152
|
+
* (state) => {
|
|
1153
|
+
* const items = state.cart.items;
|
|
1154
|
+
* const discount = state.user.user?.discount || 0;
|
|
1155
|
+
* const total = items.reduce((sum, item) => sum + item.price, 0);
|
|
1156
|
+
* return total * (1 - discount);
|
|
1157
|
+
* }
|
|
1158
|
+
* );
|
|
1159
|
+
*
|
|
1160
|
+
* const total = getCartTotal();
|
|
1161
|
+
* ```
|
|
1162
|
+
*/
|
|
1163
|
+
declare function createFederatedSelector<Stores extends Record<string, Store<any>>, R>(federation: Federation<Stores>, selector: (state: FederatedState<Stores>) => R): () => R;
|
|
1164
|
+
/**
|
|
1165
|
+
* Create a computed value from federated state.
|
|
1166
|
+
*
|
|
1167
|
+
* @param federation - The federation instance
|
|
1168
|
+
* @param selector - Selector function
|
|
1169
|
+
* @param equalityFn - Optional equality function for caching
|
|
1170
|
+
* @returns Object with get() and subscribe() methods
|
|
1171
|
+
*
|
|
1172
|
+
* @example
|
|
1173
|
+
* ```typescript
|
|
1174
|
+
* const isLoggedIn = createFederatedComputed(
|
|
1175
|
+
* federation,
|
|
1176
|
+
* (state) => state.user.user !== null
|
|
1177
|
+
* );
|
|
1178
|
+
*
|
|
1179
|
+
* // Get current value
|
|
1180
|
+
* if (isLoggedIn.get()) {
|
|
1181
|
+
* showDashboard();
|
|
1182
|
+
* }
|
|
1183
|
+
*
|
|
1184
|
+
* // Subscribe to changes
|
|
1185
|
+
* isLoggedIn.subscribe((value) => {
|
|
1186
|
+
* console.log('Login status:', value);
|
|
1187
|
+
* });
|
|
1188
|
+
* ```
|
|
1189
|
+
*/
|
|
1190
|
+
declare function createFederatedComputed<Stores extends Record<string, Store<any>>, R>(federation: Federation<Stores>, selector: (state: FederatedState<Stores>) => R, equalityFn?: (a: R, b: R) => boolean): {
|
|
1191
|
+
get(): R;
|
|
1192
|
+
subscribe(listener: (value: R, prevValue: R) => void): () => void;
|
|
1193
|
+
};
|
|
1194
|
+
/**
|
|
1195
|
+
* Wait for a condition across federated stores.
|
|
1196
|
+
*
|
|
1197
|
+
* @param federation - The federation instance
|
|
1198
|
+
* @param predicate - Condition to wait for
|
|
1199
|
+
* @param timeout - Optional timeout in ms
|
|
1200
|
+
* @returns Promise that resolves when condition is met
|
|
1201
|
+
*
|
|
1202
|
+
* @example
|
|
1203
|
+
* ```typescript
|
|
1204
|
+
* // Wait for user to be logged in and cart to have items
|
|
1205
|
+
* await waitForFederated(
|
|
1206
|
+
* federation,
|
|
1207
|
+
* (state) => state.user.user !== null && state.cart.items.length > 0,
|
|
1208
|
+
* 5000 // 5 second timeout
|
|
1209
|
+
* );
|
|
1210
|
+
*
|
|
1211
|
+
* // Now proceed with checkout
|
|
1212
|
+
* checkout();
|
|
1213
|
+
* ```
|
|
1214
|
+
*/
|
|
1215
|
+
declare function waitForFederated<Stores extends Record<string, Store<any>>>(federation: Federation<Stores>, predicate: (state: FederatedState<Stores>) => boolean, timeout?: number): Promise<FederatedState<Stores>>;
|
|
481
1216
|
|
|
482
1217
|
/**
|
|
483
1218
|
* Plugin-specific types and interfaces.
|
|
@@ -495,6 +1230,30 @@ interface PersistOptions<TState> {
|
|
|
495
1230
|
whitelist?: Array<keyof TState>;
|
|
496
1231
|
/** List of state keys to exclude (blacklist) */
|
|
497
1232
|
blacklist?: Array<keyof TState>;
|
|
1233
|
+
/** Custom function to select which parts of state to persist */
|
|
1234
|
+
partialize?: (state: TState) => Partial<TState>;
|
|
1235
|
+
/** Custom merge strategy for hydration */
|
|
1236
|
+
merge?: (persistedState: Partial<TState>, currentState: TState) => TState;
|
|
1237
|
+
/** Version for migration support */
|
|
1238
|
+
version?: number;
|
|
1239
|
+
/** Migration function between versions */
|
|
1240
|
+
migrate?: (persistedState: unknown, version: number) => TState;
|
|
1241
|
+
/** Custom serialization */
|
|
1242
|
+
serialize?: (state: Partial<TState>) => string;
|
|
1243
|
+
/** Custom deserialization */
|
|
1244
|
+
deserialize?: (str: string) => Partial<TState>;
|
|
1245
|
+
/** Encryption function */
|
|
1246
|
+
encrypt?: (data: string) => string;
|
|
1247
|
+
/** Decryption function */
|
|
1248
|
+
decrypt?: (data: string) => string;
|
|
1249
|
+
/** Debounce writes to storage (ms) */
|
|
1250
|
+
writeDebounce?: number;
|
|
1251
|
+
/** Called when hydration starts */
|
|
1252
|
+
onRehydrateStorage?: (state: TState | undefined) => void;
|
|
1253
|
+
/** Called when hydration completes */
|
|
1254
|
+
onHydrationComplete?: (state: TState) => void;
|
|
1255
|
+
/** Called on persistence error */
|
|
1256
|
+
onPersistError?: (error: Error) => void;
|
|
498
1257
|
}
|
|
499
1258
|
/**
|
|
500
1259
|
* Storage interface for persistence.
|
|
@@ -596,9 +1355,25 @@ declare const sessionStorage: StorageLike;
|
|
|
596
1355
|
* const store = createStore({ count: 0, temp: '' })
|
|
597
1356
|
* .use(persist({ key: 'app', whitelist: ['count'] }));
|
|
598
1357
|
*
|
|
599
|
-
* // With
|
|
600
|
-
* const store = createStore({
|
|
601
|
-
* .use(persist({
|
|
1358
|
+
* // With encryption
|
|
1359
|
+
* const store = createStore({ secret: 'data' })
|
|
1360
|
+
* .use(persist({
|
|
1361
|
+
* key: 'secure',
|
|
1362
|
+
* encrypt: (data) => btoa(data),
|
|
1363
|
+
* decrypt: (data) => atob(data),
|
|
1364
|
+
* }));
|
|
1365
|
+
*
|
|
1366
|
+
* // With debounce and migration
|
|
1367
|
+
* const store = createStore({ count: 0 })
|
|
1368
|
+
* .use(persist({
|
|
1369
|
+
* key: 'app',
|
|
1370
|
+
* writeDebounce: 1000,
|
|
1371
|
+
* version: 2,
|
|
1372
|
+
* migrate: (state, version) => {
|
|
1373
|
+
* if (version < 2) return { ...state, newField: 'default' };
|
|
1374
|
+
* return state;
|
|
1375
|
+
* },
|
|
1376
|
+
* }));
|
|
602
1377
|
* ```
|
|
603
1378
|
*/
|
|
604
1379
|
declare function persist<TState>(options: PersistOptions<TState>): Plugin<TState>;
|
|
@@ -807,92 +1582,611 @@ declare function triggerSync<TState>(store: Store<TState>, channel?: string): vo
|
|
|
807
1582
|
* ```typescript
|
|
808
1583
|
* import { produce } from '@oxog/state';
|
|
809
1584
|
*
|
|
810
|
-
* const state = { users: [{ name: 'John', age: 30 }] };
|
|
811
|
-
* const newState = produce(state, (draft) => {
|
|
812
|
-
* draft.users[0].age = 31;
|
|
813
|
-
* });
|
|
1585
|
+
* const state = { users: [{ name: 'John', age: 30 }] };
|
|
1586
|
+
* const newState = produce(state, (draft) => {
|
|
1587
|
+
* draft.users[0].age = 31;
|
|
1588
|
+
* });
|
|
1589
|
+
* ```
|
|
1590
|
+
*/
|
|
1591
|
+
declare function produce<T>(base: T, recipe: (draft: T) => void): T;
|
|
1592
|
+
/**
|
|
1593
|
+
* Create an immer plugin.
|
|
1594
|
+
*
|
|
1595
|
+
* @returns An immer plugin
|
|
1596
|
+
*
|
|
1597
|
+
* @example
|
|
1598
|
+
* ```typescript
|
|
1599
|
+
* import { createStore, immer } from '@oxog/state';
|
|
1600
|
+
*
|
|
1601
|
+
* const store = createStore({
|
|
1602
|
+
* items: [{ id: 1, name: 'Item 1' }]
|
|
1603
|
+
* }).use(immer());
|
|
1604
|
+
*
|
|
1605
|
+
* // Now setState can accept a mutable recipe function
|
|
1606
|
+
* store.setState((draft) => {
|
|
1607
|
+
* draft.items[0].name = 'Updated Item';
|
|
1608
|
+
* draft.items.push({ id: 2, name: 'Item 2' });
|
|
1609
|
+
* });
|
|
1610
|
+
* ```
|
|
1611
|
+
*/
|
|
1612
|
+
declare function immer<TState>(): Plugin<TState>;
|
|
1613
|
+
|
|
1614
|
+
/**
|
|
1615
|
+
* Selector plugin - Computed/derived values.
|
|
1616
|
+
*
|
|
1617
|
+
* Define computed values that automatically update when dependencies change.
|
|
1618
|
+
*
|
|
1619
|
+
* @example
|
|
1620
|
+
* ```typescript
|
|
1621
|
+
* import { createStore, selector } from '@oxog/state';
|
|
1622
|
+
*
|
|
1623
|
+
* const store = createStore({
|
|
1624
|
+
* items: [],
|
|
1625
|
+
* filter: 'all',
|
|
1626
|
+
* })
|
|
1627
|
+
* .use(selector({
|
|
1628
|
+
* // Computed: filtered items
|
|
1629
|
+
* filteredItems: (state) => {
|
|
1630
|
+
* if (state.filter === 'all') return state.items;
|
|
1631
|
+
* return state.items.filter((item: any) => item.status === state.filter);
|
|
1632
|
+
* },
|
|
1633
|
+
* // Computed: item count
|
|
1634
|
+
* itemCount: (state) => state.items.length,
|
|
1635
|
+
* }));
|
|
1636
|
+
*
|
|
1637
|
+
* // Access computed values
|
|
1638
|
+
* const state = store.getState();
|
|
1639
|
+
* console.log(state.filteredItems); // Computed value
|
|
1640
|
+
* ```
|
|
1641
|
+
*/
|
|
1642
|
+
|
|
1643
|
+
/**
|
|
1644
|
+
* Create a selector plugin for computed values.
|
|
1645
|
+
*
|
|
1646
|
+
* @param options - Plugin options with selectors
|
|
1647
|
+
* @returns A selector plugin
|
|
1648
|
+
*
|
|
1649
|
+
* @example
|
|
1650
|
+
* ```typescript
|
|
1651
|
+
* import { selector } from '@oxog/state';
|
|
1652
|
+
*
|
|
1653
|
+
* const store = createStore({
|
|
1654
|
+
* items: [{ price: 10 }, { price: 20 }],
|
|
1655
|
+
* taxRate: 0.1,
|
|
1656
|
+
* })
|
|
1657
|
+
* .use(selector({
|
|
1658
|
+
* // Computed total
|
|
1659
|
+
* total: (state) =>
|
|
1660
|
+
* state.items.reduce((sum: number, item: any) => sum + item.price, 0),
|
|
1661
|
+
*
|
|
1662
|
+
* // Computed total with tax
|
|
1663
|
+
* totalWithTax: (state) => {
|
|
1664
|
+
* const total = state.items.reduce((sum: number, item: any) => sum + item.price, 0);
|
|
1665
|
+
* return total * (1 + state.taxRate);
|
|
1666
|
+
* },
|
|
1667
|
+
* }));
|
|
1668
|
+
* ```
|
|
1669
|
+
*/
|
|
1670
|
+
declare function selector<TState>(options: SelectorOptions<TState>): Plugin<TState>;
|
|
1671
|
+
|
|
1672
|
+
/**
|
|
1673
|
+
* Logger plugin for console logging state changes.
|
|
1674
|
+
*
|
|
1675
|
+
* @module plugins/logger
|
|
1676
|
+
*/
|
|
1677
|
+
|
|
1678
|
+
/**
|
|
1679
|
+
* Log level for filtering messages.
|
|
1680
|
+
*/
|
|
1681
|
+
type LogLevel = 'debug' | 'info' | 'warn' | 'error';
|
|
1682
|
+
/**
|
|
1683
|
+
* Logger options.
|
|
1684
|
+
*/
|
|
1685
|
+
interface LoggerOptions<TState = unknown> {
|
|
1686
|
+
/** Log level filter (default: 'debug') */
|
|
1687
|
+
level?: LogLevel;
|
|
1688
|
+
/** Custom logger (default: console) */
|
|
1689
|
+
logger?: Console;
|
|
1690
|
+
/** Use collapsed console groups (default: true) */
|
|
1691
|
+
collapsed?: boolean;
|
|
1692
|
+
/** Show state diff (default: true) */
|
|
1693
|
+
diff?: boolean;
|
|
1694
|
+
/** Colorize output (default: true in browsers) */
|
|
1695
|
+
colors?: boolean;
|
|
1696
|
+
/** Show timestamp (default: true) */
|
|
1697
|
+
timestamp?: boolean;
|
|
1698
|
+
/** Filter function to skip certain changes */
|
|
1699
|
+
filter?: (state: TState, prevState: TState) => boolean;
|
|
1700
|
+
/** Transform state before logging */
|
|
1701
|
+
stateTransformer?: (state: TState) => unknown;
|
|
1702
|
+
/** Custom action name (default: 'STATE_CHANGE') */
|
|
1703
|
+
actionName?: string | ((state: TState, prevState: TState) => string);
|
|
1704
|
+
/** Name for this logger instance */
|
|
1705
|
+
name?: string;
|
|
1706
|
+
/** Enabled flag (default: true) */
|
|
1707
|
+
enabled?: boolean;
|
|
1708
|
+
}
|
|
1709
|
+
/**
|
|
1710
|
+
* Create a logger plugin.
|
|
1711
|
+
*
|
|
1712
|
+
* @param options - Logger options
|
|
1713
|
+
* @returns A logger plugin
|
|
1714
|
+
*
|
|
1715
|
+
* @example
|
|
1716
|
+
* ```typescript
|
|
1717
|
+
* import { createStore, logger } from '@oxog/state';
|
|
1718
|
+
*
|
|
1719
|
+
* // Basic usage
|
|
1720
|
+
* const store = createStore({ count: 0 })
|
|
1721
|
+
* .use(logger());
|
|
1722
|
+
*
|
|
1723
|
+
* // With options
|
|
1724
|
+
* const store = createStore({ count: 0 })
|
|
1725
|
+
* .use(logger({
|
|
1726
|
+
* level: 'info',
|
|
1727
|
+
* collapsed: false,
|
|
1728
|
+
* diff: true,
|
|
1729
|
+
* name: 'CounterStore',
|
|
1730
|
+
* }));
|
|
1731
|
+
*
|
|
1732
|
+
* // Disabled in production
|
|
1733
|
+
* const store = createStore({ count: 0 })
|
|
1734
|
+
* .use(logger({
|
|
1735
|
+
* enabled: process.env.NODE_ENV !== 'production',
|
|
1736
|
+
* }));
|
|
1737
|
+
* ```
|
|
1738
|
+
*/
|
|
1739
|
+
declare function logger<TState = unknown>(options?: LoggerOptions<TState>): Plugin<TState>;
|
|
1740
|
+
/**
|
|
1741
|
+
* Create a logger with preset options.
|
|
1742
|
+
*
|
|
1743
|
+
* @param defaultOptions - Default options
|
|
1744
|
+
* @returns A logger factory function
|
|
1745
|
+
*
|
|
1746
|
+
* @example
|
|
1747
|
+
* ```typescript
|
|
1748
|
+
* const devLogger = createLogger({
|
|
1749
|
+
* enabled: process.env.NODE_ENV === 'development',
|
|
1750
|
+
* collapsed: true,
|
|
1751
|
+
* });
|
|
1752
|
+
*
|
|
1753
|
+
* const store = createStore({ count: 0 })
|
|
1754
|
+
* .use(devLogger({ name: 'Counter' }));
|
|
1755
|
+
* ```
|
|
1756
|
+
*/
|
|
1757
|
+
declare function createLogger<TState = unknown>(defaultOptions?: LoggerOptions<TState>): (options?: LoggerOptions<TState>) => Plugin<TState>;
|
|
1758
|
+
|
|
1759
|
+
/**
|
|
1760
|
+
* Effects plugin for managing side effects.
|
|
1761
|
+
*
|
|
1762
|
+
* Provides a declarative way to handle async operations,
|
|
1763
|
+
* API calls, and other side effects in response to state changes.
|
|
1764
|
+
*
|
|
1765
|
+
* @module plugins/effects
|
|
1766
|
+
*/
|
|
1767
|
+
|
|
1768
|
+
/**
|
|
1769
|
+
* Effect cleanup function.
|
|
1770
|
+
*/
|
|
1771
|
+
type EffectCleanup = () => void;
|
|
1772
|
+
/**
|
|
1773
|
+
* Effect function that runs when dependencies change.
|
|
1774
|
+
*/
|
|
1775
|
+
type EffectFn<T> = (value: T, prevValue: T, utils: EffectUtils) => void | EffectCleanup | Promise<void | EffectCleanup>;
|
|
1776
|
+
/**
|
|
1777
|
+
* Utilities available to effects.
|
|
1778
|
+
*/
|
|
1779
|
+
interface EffectUtils {
|
|
1780
|
+
/** Get current store state */
|
|
1781
|
+
getState: () => unknown;
|
|
1782
|
+
/** Set store state */
|
|
1783
|
+
setState: (partial: any) => void;
|
|
1784
|
+
/** Check if effect is still active (not cancelled) */
|
|
1785
|
+
isActive: () => boolean;
|
|
1786
|
+
/** Cancel this effect */
|
|
1787
|
+
cancel: () => void;
|
|
1788
|
+
/** Run a callback only if effect is active */
|
|
1789
|
+
onlyIfActive: <T>(fn: () => T) => T | undefined;
|
|
1790
|
+
}
|
|
1791
|
+
/**
|
|
1792
|
+
* Effect definition for the effects plugin.
|
|
1793
|
+
*/
|
|
1794
|
+
interface EffectDefinition<TState, TSelected = TState> {
|
|
1795
|
+
/** Selector to watch */
|
|
1796
|
+
selector: Selector<TState, TSelected>;
|
|
1797
|
+
/** Effect function */
|
|
1798
|
+
effect: EffectFn<TSelected>;
|
|
1799
|
+
/** Custom equality function */
|
|
1800
|
+
equalityFn?: EqualityFn<TSelected>;
|
|
1801
|
+
/** Fire effect immediately with current value */
|
|
1802
|
+
fireImmediately?: boolean;
|
|
1803
|
+
/** Debounce effect (ms) */
|
|
1804
|
+
debounce?: number;
|
|
1805
|
+
/** Effect name for debugging */
|
|
1806
|
+
name?: string;
|
|
1807
|
+
}
|
|
1808
|
+
/**
|
|
1809
|
+
* Options for the effects plugin.
|
|
1810
|
+
*/
|
|
1811
|
+
interface EffectsOptions<TState> {
|
|
1812
|
+
/** Array of effect definitions */
|
|
1813
|
+
effects: EffectDefinition<TState, any>[];
|
|
1814
|
+
/** Error handler for effect errors */
|
|
1815
|
+
onError?: (error: Error, effectName?: string) => void;
|
|
1816
|
+
}
|
|
1817
|
+
/**
|
|
1818
|
+
* Create an effects plugin.
|
|
1819
|
+
*
|
|
1820
|
+
* @param options - Effects configuration
|
|
1821
|
+
* @returns An effects plugin
|
|
1822
|
+
*
|
|
1823
|
+
* @example
|
|
1824
|
+
* ```typescript
|
|
1825
|
+
* import { createStore, effects } from '@oxog/state';
|
|
1826
|
+
*
|
|
1827
|
+
* const store = createStore({
|
|
1828
|
+
* userId: null,
|
|
1829
|
+
* user: null,
|
|
1830
|
+
* loading: false,
|
|
1831
|
+
* }).use(effects({
|
|
1832
|
+
* effects: [
|
|
1833
|
+
* {
|
|
1834
|
+
* name: 'fetchUser',
|
|
1835
|
+
* selector: (s) => s.userId,
|
|
1836
|
+
* effect: async (userId, _prev, { setState, isActive }) => {
|
|
1837
|
+
* if (!userId) return;
|
|
1838
|
+
*
|
|
1839
|
+
* setState({ loading: true });
|
|
1840
|
+
* try {
|
|
1841
|
+
* const user = await fetchUser(userId);
|
|
1842
|
+
* if (isActive()) {
|
|
1843
|
+
* setState({ user, loading: false });
|
|
1844
|
+
* }
|
|
1845
|
+
* } catch (error) {
|
|
1846
|
+
* if (isActive()) {
|
|
1847
|
+
* setState({ user: null, loading: false });
|
|
1848
|
+
* }
|
|
1849
|
+
* }
|
|
1850
|
+
* },
|
|
1851
|
+
* },
|
|
1852
|
+
* ],
|
|
1853
|
+
* }));
|
|
1854
|
+
* ```
|
|
1855
|
+
*/
|
|
1856
|
+
declare function effects<TState>(options: EffectsOptions<TState>): Plugin<TState>;
|
|
1857
|
+
/**
|
|
1858
|
+
* Create an effect definition helper.
|
|
1859
|
+
*
|
|
1860
|
+
* @param name - Effect name
|
|
1861
|
+
* @param selector - Selector to watch
|
|
1862
|
+
* @param effect - Effect function
|
|
1863
|
+
* @param options - Additional options
|
|
1864
|
+
* @returns Effect definition
|
|
1865
|
+
*
|
|
1866
|
+
* @example
|
|
1867
|
+
* ```typescript
|
|
1868
|
+
* const fetchUserEffect = createEffect(
|
|
1869
|
+
* 'fetchUser',
|
|
1870
|
+
* (s) => s.userId,
|
|
1871
|
+
* async (userId, _prev, { setState }) => {
|
|
1872
|
+
* const user = await api.getUser(userId);
|
|
1873
|
+
* setState({ user });
|
|
1874
|
+
* },
|
|
1875
|
+
* { fireImmediately: true }
|
|
1876
|
+
* );
|
|
1877
|
+
*
|
|
1878
|
+
* const store = createStore({ userId: null, user: null })
|
|
1879
|
+
* .use(effects({ effects: [fetchUserEffect] }));
|
|
1880
|
+
* ```
|
|
1881
|
+
*/
|
|
1882
|
+
declare function createEffect<TState, TSelected>(name: string, selector: Selector<TState, TSelected>, effect: EffectFn<TSelected>, options?: Omit<EffectDefinition<TState, TSelected>, 'name' | 'selector' | 'effect'>): EffectDefinition<TState, TSelected>;
|
|
1883
|
+
/**
|
|
1884
|
+
* Create a simple effect that runs on any state change.
|
|
1885
|
+
*
|
|
1886
|
+
* @param effect - Effect function
|
|
1887
|
+
* @param options - Effect options
|
|
1888
|
+
* @returns Effect definition
|
|
1889
|
+
*
|
|
1890
|
+
* @example
|
|
1891
|
+
* ```typescript
|
|
1892
|
+
* const logEffect = createSimpleEffect(
|
|
1893
|
+
* (state, prevState) => {
|
|
1894
|
+
* console.log('State changed:', state);
|
|
1895
|
+
* }
|
|
1896
|
+
* );
|
|
1897
|
+
* ```
|
|
1898
|
+
*/
|
|
1899
|
+
declare function createSimpleEffect<TState>(effect: (state: TState, prevState: TState, utils: EffectUtils) => void | EffectCleanup, options?: Omit<EffectDefinition<TState, TState>, 'selector' | 'effect'>): EffectDefinition<TState, TState>;
|
|
1900
|
+
/**
|
|
1901
|
+
* Combine multiple effect definitions.
|
|
1902
|
+
*
|
|
1903
|
+
* @param effectDefs - Effect definitions to combine
|
|
1904
|
+
* @returns Combined effects options
|
|
1905
|
+
*
|
|
1906
|
+
* @example
|
|
1907
|
+
* ```typescript
|
|
1908
|
+
* const store = createStore({ ... })
|
|
1909
|
+
* .use(effects(combineEffects(
|
|
1910
|
+
* fetchUserEffect,
|
|
1911
|
+
* saveToStorageEffect,
|
|
1912
|
+
* analyticsEffect,
|
|
1913
|
+
* )));
|
|
1914
|
+
* ```
|
|
1915
|
+
*/
|
|
1916
|
+
declare function combineEffects<TState>(...effectDefs: EffectDefinition<TState, any>[]): EffectsOptions<TState>;
|
|
1917
|
+
/**
|
|
1918
|
+
* Create a debounced effect that only fires after a delay.
|
|
1919
|
+
*
|
|
1920
|
+
* @param delay - Debounce delay in ms
|
|
1921
|
+
* @param selector - Selector to watch
|
|
1922
|
+
* @param effect - Effect function
|
|
1923
|
+
* @returns Effect definition
|
|
1924
|
+
*
|
|
1925
|
+
* @example
|
|
1926
|
+
* ```typescript
|
|
1927
|
+
* const searchEffect = createDebouncedEffect(
|
|
1928
|
+
* 300,
|
|
1929
|
+
* (s) => s.searchQuery,
|
|
1930
|
+
* async (query, _prev, { setState }) => {
|
|
1931
|
+
* const results = await api.search(query);
|
|
1932
|
+
* setState({ searchResults: results });
|
|
1933
|
+
* }
|
|
1934
|
+
* );
|
|
1935
|
+
* ```
|
|
1936
|
+
*/
|
|
1937
|
+
declare function createDebouncedEffect<TState, TSelected>(delay: number, selector: Selector<TState, TSelected>, effect: EffectFn<TSelected>, options?: Omit<EffectDefinition<TState, TSelected>, 'selector' | 'effect' | 'debounce'>): EffectDefinition<TState, TSelected>;
|
|
1938
|
+
|
|
1939
|
+
/**
|
|
1940
|
+
* Validation plugin for state schema validation.
|
|
1941
|
+
*
|
|
1942
|
+
* Provides schema-agnostic validation with support for Zod, Yup,
|
|
1943
|
+
* or custom validation functions.
|
|
1944
|
+
*
|
|
1945
|
+
* @module plugins/validate
|
|
1946
|
+
*/
|
|
1947
|
+
|
|
1948
|
+
/**
|
|
1949
|
+
* Validation error type.
|
|
1950
|
+
*/
|
|
1951
|
+
interface ValidationError {
|
|
1952
|
+
/** Path to the invalid field */
|
|
1953
|
+
path: string[];
|
|
1954
|
+
/** Error message */
|
|
1955
|
+
message: string;
|
|
1956
|
+
/** Validation rule that failed */
|
|
1957
|
+
rule?: string;
|
|
1958
|
+
/** The invalid value */
|
|
1959
|
+
value?: unknown;
|
|
1960
|
+
}
|
|
1961
|
+
/**
|
|
1962
|
+
* Validation result.
|
|
1963
|
+
*/
|
|
1964
|
+
interface ValidationResult {
|
|
1965
|
+
/** Whether validation passed */
|
|
1966
|
+
valid: boolean;
|
|
1967
|
+
/** List of validation errors */
|
|
1968
|
+
errors: ValidationError[];
|
|
1969
|
+
}
|
|
1970
|
+
/**
|
|
1971
|
+
* Custom validator function type.
|
|
1972
|
+
*/
|
|
1973
|
+
type ValidatorFn<T> = (state: T) => boolean | ValidationResult | ValidationError[];
|
|
1974
|
+
/**
|
|
1975
|
+
* Zod-like schema interface.
|
|
1976
|
+
*/
|
|
1977
|
+
interface ZodLikeSchema<T> {
|
|
1978
|
+
safeParse(data: unknown): {
|
|
1979
|
+
success: boolean;
|
|
1980
|
+
error?: {
|
|
1981
|
+
issues: Array<{
|
|
1982
|
+
path: (string | number)[];
|
|
1983
|
+
message: string;
|
|
1984
|
+
code?: string;
|
|
1985
|
+
}>;
|
|
1986
|
+
};
|
|
1987
|
+
data?: T;
|
|
1988
|
+
};
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* Yup-like schema interface.
|
|
1992
|
+
*/
|
|
1993
|
+
interface YupLikeSchema<T> {
|
|
1994
|
+
validateSync(data: unknown, options?: {
|
|
1995
|
+
abortEarly?: boolean;
|
|
1996
|
+
}): T;
|
|
1997
|
+
validate(data: unknown, options?: {
|
|
1998
|
+
abortEarly?: boolean;
|
|
1999
|
+
}): Promise<T>;
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Schema type - supports Zod, Yup, or custom validators.
|
|
2003
|
+
*/
|
|
2004
|
+
type Schema<T> = ZodLikeSchema<T> | YupLikeSchema<T> | ValidatorFn<T>;
|
|
2005
|
+
/**
|
|
2006
|
+
* Validation timing options.
|
|
2007
|
+
*/
|
|
2008
|
+
type ValidationTiming = 'change' | 'commit' | 'manual';
|
|
2009
|
+
/**
|
|
2010
|
+
* Validation plugin options.
|
|
2011
|
+
*/
|
|
2012
|
+
interface ValidateOptions<T> {
|
|
2013
|
+
/** Schema to validate against (Zod, Yup, or custom function) */
|
|
2014
|
+
schema: Schema<T>;
|
|
2015
|
+
/** When to validate: 'change' (every setState), 'commit' (on batch commit), 'manual' (only when validate() called) */
|
|
2016
|
+
on?: ValidationTiming;
|
|
2017
|
+
/** Callback when validation fails */
|
|
2018
|
+
onError?: (errors: ValidationError[]) => void;
|
|
2019
|
+
/** Throw error on validation failure */
|
|
2020
|
+
throwOnError?: boolean;
|
|
2021
|
+
/** Only validate these paths (keys of state) */
|
|
2022
|
+
paths?: Array<keyof T>;
|
|
2023
|
+
/** Name for debugging */
|
|
2024
|
+
name?: string;
|
|
2025
|
+
}
|
|
2026
|
+
/**
|
|
2027
|
+
* Validation API added to store.
|
|
2028
|
+
*/
|
|
2029
|
+
interface ValidationAPI {
|
|
2030
|
+
/** Manually trigger validation */
|
|
2031
|
+
validate(): ValidationResult;
|
|
2032
|
+
/** Check if current state is valid */
|
|
2033
|
+
isValid(): boolean;
|
|
2034
|
+
/** Get current validation errors */
|
|
2035
|
+
getErrors(): ValidationError[];
|
|
2036
|
+
/** Clear validation errors */
|
|
2037
|
+
clearErrors(): void;
|
|
2038
|
+
}
|
|
2039
|
+
/**
|
|
2040
|
+
* Create a validation plugin.
|
|
2041
|
+
*
|
|
2042
|
+
* @param options - Validation options
|
|
2043
|
+
* @returns A validation plugin
|
|
2044
|
+
*
|
|
2045
|
+
* @example
|
|
2046
|
+
* ```typescript
|
|
2047
|
+
* import { createStore, validate } from '@oxog/state';
|
|
2048
|
+
* import { z } from 'zod';
|
|
2049
|
+
*
|
|
2050
|
+
* // With Zod schema
|
|
2051
|
+
* const userSchema = z.object({
|
|
2052
|
+
* name: z.string().min(1),
|
|
2053
|
+
* age: z.number().min(0),
|
|
2054
|
+
* email: z.string().email(),
|
|
2055
|
+
* });
|
|
2056
|
+
*
|
|
2057
|
+
* const store = createStore({ name: '', age: 0, email: '' })
|
|
2058
|
+
* .use(validate({
|
|
2059
|
+
* schema: userSchema,
|
|
2060
|
+
* on: 'change',
|
|
2061
|
+
* onError: (errors) => console.log('Validation errors:', errors),
|
|
2062
|
+
* }));
|
|
2063
|
+
*
|
|
2064
|
+
* // Manual validation
|
|
2065
|
+
* const result = store.validate();
|
|
2066
|
+
* if (!result.valid) {
|
|
2067
|
+
* console.log(result.errors);
|
|
2068
|
+
* }
|
|
2069
|
+
*
|
|
2070
|
+
* // Check if valid
|
|
2071
|
+
* if (store.isValid()) {
|
|
2072
|
+
* submitForm();
|
|
2073
|
+
* }
|
|
2074
|
+
* ```
|
|
2075
|
+
*
|
|
2076
|
+
* @example
|
|
2077
|
+
* ```typescript
|
|
2078
|
+
* // With custom validator function
|
|
2079
|
+
* const store = createStore({ password: '', confirmPassword: '' })
|
|
2080
|
+
* .use(validate({
|
|
2081
|
+
* schema: (state) => {
|
|
2082
|
+
* const errors = [];
|
|
2083
|
+
* if (state.password.length < 8) {
|
|
2084
|
+
* errors.push({
|
|
2085
|
+
* path: ['password'],
|
|
2086
|
+
* message: 'Password must be at least 8 characters',
|
|
2087
|
+
* });
|
|
2088
|
+
* }
|
|
2089
|
+
* if (state.password !== state.confirmPassword) {
|
|
2090
|
+
* errors.push({
|
|
2091
|
+
* path: ['confirmPassword'],
|
|
2092
|
+
* message: 'Passwords must match',
|
|
2093
|
+
* });
|
|
2094
|
+
* }
|
|
2095
|
+
* return errors;
|
|
2096
|
+
* },
|
|
2097
|
+
* on: 'change',
|
|
2098
|
+
* }));
|
|
2099
|
+
* ```
|
|
2100
|
+
*
|
|
2101
|
+
* @example
|
|
2102
|
+
* ```typescript
|
|
2103
|
+
* // With partial validation
|
|
2104
|
+
* const store = createStore({ name: '', age: 0, temp: '' })
|
|
2105
|
+
* .use(validate({
|
|
2106
|
+
* schema: userSchema,
|
|
2107
|
+
* paths: ['name', 'age'], // Only validate these fields
|
|
2108
|
+
* }));
|
|
2109
|
+
* ```
|
|
2110
|
+
*/
|
|
2111
|
+
declare function validate<TState>(options: ValidateOptions<TState>): Plugin<TState>;
|
|
2112
|
+
/**
|
|
2113
|
+
* Create a simple boolean validator.
|
|
2114
|
+
*
|
|
2115
|
+
* @param predicate - Validation predicate function
|
|
2116
|
+
* @param message - Error message when validation fails
|
|
2117
|
+
* @returns A validator function
|
|
2118
|
+
*
|
|
2119
|
+
* @example
|
|
2120
|
+
* ```typescript
|
|
2121
|
+
* const isPositive = createValidator(
|
|
2122
|
+
* (state) => state.count >= 0,
|
|
2123
|
+
* 'Count must be positive'
|
|
2124
|
+
* );
|
|
2125
|
+
*
|
|
2126
|
+
* const store = createStore({ count: 0 })
|
|
2127
|
+
* .use(validate({ schema: isPositive }));
|
|
814
2128
|
* ```
|
|
815
2129
|
*/
|
|
816
|
-
declare function
|
|
2130
|
+
declare function createValidator<T>(predicate: (state: T) => boolean, message: string): ValidatorFn<T>;
|
|
817
2131
|
/**
|
|
818
|
-
*
|
|
2132
|
+
* Combine multiple validators into one.
|
|
819
2133
|
*
|
|
820
|
-
* @
|
|
2134
|
+
* @param validators - Array of validator functions
|
|
2135
|
+
* @returns Combined validator function
|
|
821
2136
|
*
|
|
822
2137
|
* @example
|
|
823
2138
|
* ```typescript
|
|
824
|
-
*
|
|
825
|
-
*
|
|
826
|
-
*
|
|
827
|
-
*
|
|
828
|
-
* }).use(immer());
|
|
2139
|
+
* const combinedValidator = combineValidators(
|
|
2140
|
+
* createValidator((s) => s.name.length > 0, 'Name required'),
|
|
2141
|
+
* createValidator((s) => s.age >= 18, 'Must be 18+'),
|
|
2142
|
+
* );
|
|
829
2143
|
*
|
|
830
|
-
*
|
|
831
|
-
*
|
|
832
|
-
* draft.items[0].name = 'Updated Item';
|
|
833
|
-
* draft.items.push({ id: 2, name: 'Item 2' });
|
|
834
|
-
* });
|
|
2144
|
+
* const store = createStore({ name: '', age: 0 })
|
|
2145
|
+
* .use(validate({ schema: combinedValidator }));
|
|
835
2146
|
* ```
|
|
836
2147
|
*/
|
|
837
|
-
declare function
|
|
838
|
-
|
|
2148
|
+
declare function combineValidators<T>(...validators: ValidatorFn<T>[]): ValidatorFn<T>;
|
|
839
2149
|
/**
|
|
840
|
-
*
|
|
2150
|
+
* Create a field-level validator.
|
|
841
2151
|
*
|
|
842
|
-
*
|
|
2152
|
+
* @param field - Field name to validate
|
|
2153
|
+
* @param predicate - Validation predicate for field value
|
|
2154
|
+
* @param message - Error message when validation fails
|
|
2155
|
+
* @returns A validator function
|
|
843
2156
|
*
|
|
844
2157
|
* @example
|
|
845
2158
|
* ```typescript
|
|
846
|
-
*
|
|
847
|
-
*
|
|
848
|
-
*
|
|
849
|
-
*
|
|
850
|
-
*
|
|
851
|
-
* })
|
|
852
|
-
* .use(selector({
|
|
853
|
-
* // Computed: filtered items
|
|
854
|
-
* filteredItems: (state) => {
|
|
855
|
-
* if (state.filter === 'all') return state.items;
|
|
856
|
-
* return state.items.filter((item: any) => item.status === state.filter);
|
|
857
|
-
* },
|
|
858
|
-
* // Computed: item count
|
|
859
|
-
* itemCount: (state) => state.items.length,
|
|
860
|
-
* }));
|
|
2159
|
+
* const emailValidator = createFieldValidator(
|
|
2160
|
+
* 'email',
|
|
2161
|
+
* (email) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email),
|
|
2162
|
+
* 'Invalid email format'
|
|
2163
|
+
* );
|
|
861
2164
|
*
|
|
862
|
-
*
|
|
863
|
-
*
|
|
864
|
-
* console.log(state.filteredItems); // Computed value
|
|
2165
|
+
* const store = createStore({ email: '' })
|
|
2166
|
+
* .use(validate({ schema: emailValidator }));
|
|
865
2167
|
* ```
|
|
866
2168
|
*/
|
|
867
|
-
|
|
2169
|
+
declare function createFieldValidator<T, K extends keyof T>(field: K, predicate: (value: T[K], state: T) => boolean, message: string): ValidatorFn<T>;
|
|
868
2170
|
/**
|
|
869
|
-
* Create
|
|
2171
|
+
* Create an async validator (for server-side validation).
|
|
870
2172
|
*
|
|
871
|
-
* @param
|
|
872
|
-
* @returns A
|
|
2173
|
+
* @param validator - Async validation function
|
|
2174
|
+
* @returns A promise that resolves to validation result
|
|
873
2175
|
*
|
|
874
2176
|
* @example
|
|
875
2177
|
* ```typescript
|
|
876
|
-
*
|
|
877
|
-
*
|
|
878
|
-
*
|
|
879
|
-
*
|
|
880
|
-
*
|
|
881
|
-
* })
|
|
882
|
-
* .use(selector({
|
|
883
|
-
* // Computed total
|
|
884
|
-
* total: (state) =>
|
|
885
|
-
* state.items.reduce((sum: number, item: any) => sum + item.price, 0),
|
|
2178
|
+
* const checkUsername = createAsyncValidator(async (state) => {
|
|
2179
|
+
* const exists = await api.checkUsername(state.username);
|
|
2180
|
+
* return exists
|
|
2181
|
+
* ? [{ path: ['username'], message: 'Username already taken' }]
|
|
2182
|
+
* : [];
|
|
2183
|
+
* });
|
|
886
2184
|
*
|
|
887
|
-
*
|
|
888
|
-
*
|
|
889
|
-
* const total = state.items.reduce((sum: number, item: any) => sum + item.price, 0);
|
|
890
|
-
* return total * (1 + state.taxRate);
|
|
891
|
-
* },
|
|
892
|
-
* }));
|
|
2185
|
+
* // Manual validation
|
|
2186
|
+
* const result = await checkUsername(store.getState());
|
|
893
2187
|
* ```
|
|
894
2188
|
*/
|
|
895
|
-
declare function
|
|
2189
|
+
declare function createAsyncValidator<T>(validator: (state: T) => Promise<ValidationError[]>): (state: T) => Promise<ValidationResult>;
|
|
896
2190
|
|
|
897
2191
|
/**
|
|
898
2192
|
* Deep clone a value, creating copies of nested objects and arrays.
|
|
@@ -1058,4 +2352,430 @@ declare function omit<T extends object, K extends keyof T>(obj: T, keys: readonl
|
|
|
1058
2352
|
*/
|
|
1059
2353
|
declare function identity<T>(value: T): T;
|
|
1060
2354
|
|
|
1061
|
-
|
|
2355
|
+
/**
|
|
2356
|
+
* Testing utilities for @oxog/state.
|
|
2357
|
+
*
|
|
2358
|
+
* Provides helpers for testing stores, state changes,
|
|
2359
|
+
* and async effects in unit tests.
|
|
2360
|
+
*
|
|
2361
|
+
* @module testing
|
|
2362
|
+
*/
|
|
2363
|
+
|
|
2364
|
+
/**
|
|
2365
|
+
* Mock storage for testing persist plugin.
|
|
2366
|
+
*/
|
|
2367
|
+
interface MockStorage {
|
|
2368
|
+
getItem(key: string): string | null;
|
|
2369
|
+
setItem(key: string, value: string): void;
|
|
2370
|
+
removeItem(key: string): void;
|
|
2371
|
+
clear(): void;
|
|
2372
|
+
getAll(): Record<string, string>;
|
|
2373
|
+
}
|
|
2374
|
+
/**
|
|
2375
|
+
* Create a mock storage for testing.
|
|
2376
|
+
*
|
|
2377
|
+
* @returns A mock storage implementation
|
|
2378
|
+
*
|
|
2379
|
+
* @example
|
|
2380
|
+
* ```typescript
|
|
2381
|
+
* import { createMockStorage, persist } from '@oxog/state';
|
|
2382
|
+
*
|
|
2383
|
+
* const mockStorage = createMockStorage();
|
|
2384
|
+
*
|
|
2385
|
+
* const store = createStore({ count: 0 })
|
|
2386
|
+
* .use(persist({ key: 'test', storage: mockStorage }));
|
|
2387
|
+
*
|
|
2388
|
+
* store.setState({ count: 5 });
|
|
2389
|
+
* expect(mockStorage.getItem('test')).toBe('{"count":5}');
|
|
2390
|
+
* ```
|
|
2391
|
+
*/
|
|
2392
|
+
declare function createMockStorage(): MockStorage;
|
|
2393
|
+
/**
|
|
2394
|
+
* State change record for tracking.
|
|
2395
|
+
*/
|
|
2396
|
+
interface StateChange<TState> {
|
|
2397
|
+
state: TState;
|
|
2398
|
+
prevState: TState;
|
|
2399
|
+
timestamp: number;
|
|
2400
|
+
}
|
|
2401
|
+
/**
|
|
2402
|
+
* Store spy for testing state changes.
|
|
2403
|
+
*/
|
|
2404
|
+
interface StoreSpy<TState> {
|
|
2405
|
+
/** All recorded state changes */
|
|
2406
|
+
changes: StateChange<TState>[];
|
|
2407
|
+
/** Get the last state change */
|
|
2408
|
+
lastChange: () => StateChange<TState> | undefined;
|
|
2409
|
+
/** Get the last state */
|
|
2410
|
+
lastState: () => TState | undefined;
|
|
2411
|
+
/** Reset recorded changes */
|
|
2412
|
+
reset: () => void;
|
|
2413
|
+
/** Unsubscribe from store */
|
|
2414
|
+
unsubscribe: () => void;
|
|
2415
|
+
/** Wait for a specific state condition */
|
|
2416
|
+
waitFor: (predicate: (state: TState) => boolean, timeout?: number) => Promise<TState>;
|
|
2417
|
+
/** Wait for a specific number of changes */
|
|
2418
|
+
waitForChanges: (count: number, timeout?: number) => Promise<StateChange<TState>[]>;
|
|
2419
|
+
}
|
|
2420
|
+
/**
|
|
2421
|
+
* Create a spy to track store state changes.
|
|
2422
|
+
*
|
|
2423
|
+
* @param store - The store to spy on
|
|
2424
|
+
* @returns A store spy object
|
|
2425
|
+
*
|
|
2426
|
+
* @example
|
|
2427
|
+
* ```typescript
|
|
2428
|
+
* import { createStore, spyOnStore } from '@oxog/state';
|
|
2429
|
+
*
|
|
2430
|
+
* const store = createStore({ count: 0 });
|
|
2431
|
+
* const spy = spyOnStore(store);
|
|
2432
|
+
*
|
|
2433
|
+
* store.setState({ count: 1 });
|
|
2434
|
+
* store.setState({ count: 2 });
|
|
2435
|
+
*
|
|
2436
|
+
* expect(spy.changes).toHaveLength(2);
|
|
2437
|
+
* expect(spy.lastState()?.count).toBe(2);
|
|
2438
|
+
* ```
|
|
2439
|
+
*/
|
|
2440
|
+
declare function spyOnStore<TState>(store: Store<TState>): StoreSpy<TState>;
|
|
2441
|
+
/**
|
|
2442
|
+
* Test helper for asserting state values.
|
|
2443
|
+
*/
|
|
2444
|
+
interface StateAssertion<TState> {
|
|
2445
|
+
/** Assert the current state equals expected */
|
|
2446
|
+
toEqual: (expected: Partial<TState>) => void;
|
|
2447
|
+
/** Assert a selector value equals expected */
|
|
2448
|
+
toHaveSelected: <T>(selector: Selector<TState, T>, expected: T) => void;
|
|
2449
|
+
/** Assert state matches a predicate */
|
|
2450
|
+
toMatch: (predicate: (state: TState) => boolean) => void;
|
|
2451
|
+
}
|
|
2452
|
+
/**
|
|
2453
|
+
* Create assertions for a store's state.
|
|
2454
|
+
*
|
|
2455
|
+
* @param store - The store to assert on
|
|
2456
|
+
* @returns State assertion helpers
|
|
2457
|
+
*
|
|
2458
|
+
* @example
|
|
2459
|
+
* ```typescript
|
|
2460
|
+
* import { createStore, assertState } from '@oxog/state';
|
|
2461
|
+
*
|
|
2462
|
+
* const store = createStore({ count: 0, name: 'test' });
|
|
2463
|
+
* const assert = assertState(store);
|
|
2464
|
+
*
|
|
2465
|
+
* assert.toEqual({ count: 0 });
|
|
2466
|
+
* assert.toHaveSelected(s => s.name, 'test');
|
|
2467
|
+
* assert.toMatch(s => s.count >= 0);
|
|
2468
|
+
* ```
|
|
2469
|
+
*/
|
|
2470
|
+
declare function assertState<TState>(store: Store<TState>): StateAssertion<TState>;
|
|
2471
|
+
/**
|
|
2472
|
+
* Options for test store creation.
|
|
2473
|
+
*/
|
|
2474
|
+
interface TestStoreOptions<TState> {
|
|
2475
|
+
/** Initial state */
|
|
2476
|
+
initialState: TState;
|
|
2477
|
+
/** Whether to enable spy automatically */
|
|
2478
|
+
spy?: boolean;
|
|
2479
|
+
}
|
|
2480
|
+
/**
|
|
2481
|
+
* Test store wrapper with built-in utilities.
|
|
2482
|
+
*/
|
|
2483
|
+
interface TestStore<TState> {
|
|
2484
|
+
/** The underlying store */
|
|
2485
|
+
store: Store<TState>;
|
|
2486
|
+
/** State spy (if enabled) */
|
|
2487
|
+
spy?: StoreSpy<TState>;
|
|
2488
|
+
/** State assertions */
|
|
2489
|
+
assert: StateAssertion<TState>;
|
|
2490
|
+
/** Reset store to initial state */
|
|
2491
|
+
reset: () => void;
|
|
2492
|
+
/** Destroy store and cleanup */
|
|
2493
|
+
destroy: () => void;
|
|
2494
|
+
}
|
|
2495
|
+
/**
|
|
2496
|
+
* Create a test store with built-in utilities.
|
|
2497
|
+
*
|
|
2498
|
+
* @param options - Test store options
|
|
2499
|
+
* @returns A test store wrapper
|
|
2500
|
+
*
|
|
2501
|
+
* @example
|
|
2502
|
+
* ```typescript
|
|
2503
|
+
* import { createTestStore } from '@oxog/state';
|
|
2504
|
+
*
|
|
2505
|
+
* describe('Counter', () => {
|
|
2506
|
+
* let testStore: TestStore<{ count: number }>;
|
|
2507
|
+
*
|
|
2508
|
+
* beforeEach(() => {
|
|
2509
|
+
* testStore = createTestStore({ initialState: { count: 0 }, spy: true });
|
|
2510
|
+
* });
|
|
2511
|
+
*
|
|
2512
|
+
* afterEach(() => {
|
|
2513
|
+
* testStore.destroy();
|
|
2514
|
+
* });
|
|
2515
|
+
*
|
|
2516
|
+
* it('should increment count', () => {
|
|
2517
|
+
* testStore.store.setState({ count: 1 });
|
|
2518
|
+
* testStore.assert.toEqual({ count: 1 });
|
|
2519
|
+
* expect(testStore.spy?.changes).toHaveLength(1);
|
|
2520
|
+
* });
|
|
2521
|
+
* });
|
|
2522
|
+
* ```
|
|
2523
|
+
*/
|
|
2524
|
+
declare function createTestStore<TState>(options: TestStoreOptions<TState>): TestStore<TState>;
|
|
2525
|
+
/**
|
|
2526
|
+
* Wait for a specified amount of time.
|
|
2527
|
+
*
|
|
2528
|
+
* @param ms - Milliseconds to wait
|
|
2529
|
+
* @returns A promise that resolves after the delay
|
|
2530
|
+
*
|
|
2531
|
+
* @example
|
|
2532
|
+
* ```typescript
|
|
2533
|
+
* await delay(100);
|
|
2534
|
+
* ```
|
|
2535
|
+
*/
|
|
2536
|
+
declare function delay(ms: number): Promise<void>;
|
|
2537
|
+
/**
|
|
2538
|
+
* Run a function and wait for all pending microtasks.
|
|
2539
|
+
*
|
|
2540
|
+
* @param fn - Function to run
|
|
2541
|
+
* @returns A promise that resolves after microtasks complete
|
|
2542
|
+
*
|
|
2543
|
+
* @example
|
|
2544
|
+
* ```typescript
|
|
2545
|
+
* await flushMicrotasks(() => {
|
|
2546
|
+
* store.setState({ count: 1 });
|
|
2547
|
+
* });
|
|
2548
|
+
* ```
|
|
2549
|
+
*/
|
|
2550
|
+
declare function flushMicrotasks(fn?: () => void): Promise<void>;
|
|
2551
|
+
/**
|
|
2552
|
+
* Wrap a store action for testing with call tracking.
|
|
2553
|
+
*/
|
|
2554
|
+
interface MockedAction<TArgs extends any[], TReturn> {
|
|
2555
|
+
/** Call the action */
|
|
2556
|
+
(...args: TArgs): TReturn;
|
|
2557
|
+
/** Number of times called */
|
|
2558
|
+
callCount: number;
|
|
2559
|
+
/** Arguments from each call */
|
|
2560
|
+
calls: TArgs[];
|
|
2561
|
+
/** Reset call tracking */
|
|
2562
|
+
reset: () => void;
|
|
2563
|
+
}
|
|
2564
|
+
/**
|
|
2565
|
+
* Create a mocked action for testing.
|
|
2566
|
+
*
|
|
2567
|
+
* @param fn - The original function
|
|
2568
|
+
* @returns A mocked action with call tracking
|
|
2569
|
+
*
|
|
2570
|
+
* @example
|
|
2571
|
+
* ```typescript
|
|
2572
|
+
* const mockIncrement = mockAction((amount: number) => {
|
|
2573
|
+
* store.setState(s => ({ count: s.count + amount }));
|
|
2574
|
+
* });
|
|
2575
|
+
*
|
|
2576
|
+
* mockIncrement(5);
|
|
2577
|
+
* mockIncrement(3);
|
|
2578
|
+
*
|
|
2579
|
+
* expect(mockIncrement.callCount).toBe(2);
|
|
2580
|
+
* expect(mockIncrement.calls).toEqual([[5], [3]]);
|
|
2581
|
+
* ```
|
|
2582
|
+
*/
|
|
2583
|
+
declare function mockAction<TArgs extends any[], TReturn>(fn: (...args: TArgs) => TReturn): MockedAction<TArgs, TReturn>;
|
|
2584
|
+
/**
|
|
2585
|
+
* Create a snapshot of the current store state.
|
|
2586
|
+
*
|
|
2587
|
+
* @param store - The store to snapshot
|
|
2588
|
+
* @returns A deep clone of the current state
|
|
2589
|
+
*
|
|
2590
|
+
* @example
|
|
2591
|
+
* ```typescript
|
|
2592
|
+
* const before = snapshot(store);
|
|
2593
|
+
* store.setState({ count: 5 });
|
|
2594
|
+
* const after = snapshot(store);
|
|
2595
|
+
*
|
|
2596
|
+
* expect(before.count).toBe(0);
|
|
2597
|
+
* expect(after.count).toBe(5);
|
|
2598
|
+
* ```
|
|
2599
|
+
*/
|
|
2600
|
+
declare function snapshot<TState>(store: Store<TState>): TState;
|
|
2601
|
+
/**
|
|
2602
|
+
* Compare two state snapshots and return differences.
|
|
2603
|
+
*
|
|
2604
|
+
* @param before - State before changes
|
|
2605
|
+
* @param after - State after changes
|
|
2606
|
+
* @returns Object containing changed keys and their values
|
|
2607
|
+
*
|
|
2608
|
+
* @example
|
|
2609
|
+
* ```typescript
|
|
2610
|
+
* const before = { count: 0, name: 'test' };
|
|
2611
|
+
* const after = { count: 5, name: 'test' };
|
|
2612
|
+
*
|
|
2613
|
+
* const diff = stateDiff(before, after);
|
|
2614
|
+
* // { count: { before: 0, after: 5 } }
|
|
2615
|
+
* ```
|
|
2616
|
+
*/
|
|
2617
|
+
declare function stateDiff<TState extends Record<string, any>>(before: TState, after: TState): Record<string, {
|
|
2618
|
+
before: any;
|
|
2619
|
+
after: any;
|
|
2620
|
+
}>;
|
|
2621
|
+
|
|
2622
|
+
/**
|
|
2623
|
+
* Middleware Compatibility Layer
|
|
2624
|
+
*
|
|
2625
|
+
* Provides a generic middleware pattern for state management,
|
|
2626
|
+
* enabling compatibility with various middleware ecosystems.
|
|
2627
|
+
*
|
|
2628
|
+
* @module compat/middleware
|
|
2629
|
+
*/
|
|
2630
|
+
|
|
2631
|
+
/**
|
|
2632
|
+
* Middleware-style SetState function.
|
|
2633
|
+
*/
|
|
2634
|
+
type MiddlewareSetState<T> = {
|
|
2635
|
+
(partial: Partial<T> | ((state: T) => Partial<T>), replace?: boolean): void;
|
|
2636
|
+
};
|
|
2637
|
+
/**
|
|
2638
|
+
* Middleware-style GetState function.
|
|
2639
|
+
*/
|
|
2640
|
+
type MiddlewareGetState<T> = () => T;
|
|
2641
|
+
/**
|
|
2642
|
+
* Middleware-style Subscribe function.
|
|
2643
|
+
*/
|
|
2644
|
+
type MiddlewareSubscribe<T> = (listener: (state: T, prevState: T) => void) => () => void;
|
|
2645
|
+
/**
|
|
2646
|
+
* Generic StoreApi for middleware compatibility.
|
|
2647
|
+
*/
|
|
2648
|
+
interface MiddlewareStoreApi<T> {
|
|
2649
|
+
setState: MiddlewareSetState<T>;
|
|
2650
|
+
getState: MiddlewareGetState<T>;
|
|
2651
|
+
subscribe: MiddlewareSubscribe<T>;
|
|
2652
|
+
destroy?: () => void;
|
|
2653
|
+
}
|
|
2654
|
+
/**
|
|
2655
|
+
* State creator function for middleware pattern.
|
|
2656
|
+
*/
|
|
2657
|
+
type StateCreatorFn<T, Mps extends unknown[] = [], Mcs extends unknown[] = []> = (set: MiddlewareSetState<T>, get: MiddlewareGetState<T>, api: MiddlewareStoreApi<T>) => T;
|
|
2658
|
+
/**
|
|
2659
|
+
* Generic Middleware type.
|
|
2660
|
+
*/
|
|
2661
|
+
type MiddlewareFn<T, Mps extends unknown[] = [], Mcs extends unknown[] = []> = (creator: StateCreatorFn<T, Mps, Mcs>) => StateCreatorFn<T, Mps, Mcs>;
|
|
2662
|
+
/**
|
|
2663
|
+
* Convert an @oxog/state store to middleware-compatible API.
|
|
2664
|
+
*
|
|
2665
|
+
* @param store - An @oxog/state store
|
|
2666
|
+
* @returns A middleware-compatible store API
|
|
2667
|
+
*
|
|
2668
|
+
* @example
|
|
2669
|
+
* ```typescript
|
|
2670
|
+
* import { createStore, toMiddlewareApi } from '@oxog/state';
|
|
2671
|
+
*
|
|
2672
|
+
* const store = createStore({ count: 0 });
|
|
2673
|
+
* const api = toMiddlewareApi(store);
|
|
2674
|
+
*
|
|
2675
|
+
* // Use middleware-style API
|
|
2676
|
+
* api.setState({ count: 1 });
|
|
2677
|
+
* api.setState((state) => ({ count: state.count + 1 }));
|
|
2678
|
+
* console.log(api.getState()); // { count: 2 }
|
|
2679
|
+
* ```
|
|
2680
|
+
*/
|
|
2681
|
+
declare function toMiddlewareApi<T>(store: Store<T>): MiddlewareStoreApi<T>;
|
|
2682
|
+
/**
|
|
2683
|
+
* Wrap a middleware to work as an @oxog/state plugin.
|
|
2684
|
+
*
|
|
2685
|
+
* @param middleware - A middleware function
|
|
2686
|
+
* @param options - Optional middleware options
|
|
2687
|
+
* @returns An @oxog/state plugin
|
|
2688
|
+
*
|
|
2689
|
+
* @example
|
|
2690
|
+
* ```typescript
|
|
2691
|
+
* import { createStore, middlewareCompat } from '@oxog/state';
|
|
2692
|
+
*
|
|
2693
|
+
* // Use external middleware with @oxog/state
|
|
2694
|
+
* const store = createStore({ count: 0 })
|
|
2695
|
+
* .use(middlewareCompat(someMiddleware, { name: 'MyStore' }));
|
|
2696
|
+
* ```
|
|
2697
|
+
*/
|
|
2698
|
+
declare function middlewareCompat<T>(middleware: MiddlewareFn<T>, options?: Record<string, unknown>): Plugin<T>;
|
|
2699
|
+
/**
|
|
2700
|
+
* Create a store using middleware-style state creator.
|
|
2701
|
+
*
|
|
2702
|
+
* @param creator - Middleware-style state creator function
|
|
2703
|
+
* @returns A store API
|
|
2704
|
+
*
|
|
2705
|
+
* @example
|
|
2706
|
+
* ```typescript
|
|
2707
|
+
* import { createWithMiddleware } from '@oxog/state';
|
|
2708
|
+
*
|
|
2709
|
+
* const store = createWithMiddleware((set, get) => ({
|
|
2710
|
+
* count: 0,
|
|
2711
|
+
* increment: () => set((state) => ({ count: state.count + 1 })),
|
|
2712
|
+
* decrement: () => set((state) => ({ count: state.count - 1 })),
|
|
2713
|
+
* reset: () => set({ count: 0 }),
|
|
2714
|
+
* getDoubled: () => get().count * 2,
|
|
2715
|
+
* }));
|
|
2716
|
+
*
|
|
2717
|
+
* store.getState().increment();
|
|
2718
|
+
* console.log(store.getState().count); // 1
|
|
2719
|
+
* ```
|
|
2720
|
+
*/
|
|
2721
|
+
declare function createWithMiddleware<T extends object>(creator: StateCreatorFn<T>): MiddlewareStoreApi<T> & {
|
|
2722
|
+
use: <U>(plugin: Plugin<T>) => MiddlewareStoreApi<T>;
|
|
2723
|
+
};
|
|
2724
|
+
/**
|
|
2725
|
+
* Create a simple middleware.
|
|
2726
|
+
*
|
|
2727
|
+
* @param name - Middleware name
|
|
2728
|
+
* @param enhancer - Function that enhances the state creator
|
|
2729
|
+
* @returns A middleware function
|
|
2730
|
+
*
|
|
2731
|
+
* @example
|
|
2732
|
+
* ```typescript
|
|
2733
|
+
* const loggerMiddleware = createSimpleMiddleware('logger', (creator) => (set, get, api) => {
|
|
2734
|
+
* const loggedSet: typeof set = (partial, replace) => {
|
|
2735
|
+
* console.log('Before:', get());
|
|
2736
|
+
* set(partial, replace);
|
|
2737
|
+
* console.log('After:', get());
|
|
2738
|
+
* };
|
|
2739
|
+
* return creator(loggedSet, get, api);
|
|
2740
|
+
* });
|
|
2741
|
+
*
|
|
2742
|
+
* const store = createStore({ count: 0 })
|
|
2743
|
+
* .use(middlewareCompat(loggerMiddleware));
|
|
2744
|
+
* ```
|
|
2745
|
+
*/
|
|
2746
|
+
declare function createSimpleMiddleware<T>(name: string, enhancer: (creator: StateCreatorFn<T>) => StateCreatorFn<T>): MiddlewareFn<T>;
|
|
2747
|
+
/**
|
|
2748
|
+
* Compose multiple middlewares.
|
|
2749
|
+
*
|
|
2750
|
+
* @param middlewares - Array of middlewares
|
|
2751
|
+
* @returns A single composed middleware
|
|
2752
|
+
*
|
|
2753
|
+
* @example
|
|
2754
|
+
* ```typescript
|
|
2755
|
+
* import { compose, middlewareCompat } from '@oxog/state';
|
|
2756
|
+
*
|
|
2757
|
+
* const composedMiddleware = compose(
|
|
2758
|
+
* loggerMiddleware,
|
|
2759
|
+
* devtoolsMiddleware,
|
|
2760
|
+
* persistMiddleware,
|
|
2761
|
+
* );
|
|
2762
|
+
*
|
|
2763
|
+
* const store = createStore({ count: 0 })
|
|
2764
|
+
* .use(middlewareCompat(composedMiddleware));
|
|
2765
|
+
* ```
|
|
2766
|
+
*/
|
|
2767
|
+
declare function compose<T>(...middlewares: MiddlewareFn<T>[]): MiddlewareFn<T>;
|
|
2768
|
+
/**
|
|
2769
|
+
* Type helper for extracting state type from a store API.
|
|
2770
|
+
*/
|
|
2771
|
+
type ExtractStateType<S> = S extends MiddlewareStoreApi<infer T> ? T : never;
|
|
2772
|
+
/**
|
|
2773
|
+
* Type helper for selector function.
|
|
2774
|
+
*/
|
|
2775
|
+
type SelectorFn<T, U> = (state: T) => U;
|
|
2776
|
+
/**
|
|
2777
|
+
* Type helper for equality function.
|
|
2778
|
+
*/
|
|
2779
|
+
type EqualityCheck<T> = (a: T, b: T) => boolean;
|
|
2780
|
+
|
|
2781
|
+
export { type Action, type Actions, type Computed, type ComputedOptions, type DeepPartial, type DevtoolsOptions, type EffectCleanup, type EffectDefinition, type EffectFn, type EffectUtils, type EffectsOptions, type EqualityCheck, type EqualityFn, type ExtractStateType, type FederatedState, type Federation, type FederationListener, type FederationOptions, type GetSlice, type HistoryOptions, type HistoryStore, type Listener, type LogLevel, type LoggerOptions, type MiddlewareFn, type MiddlewareGetState, type MiddlewareSetState, type MiddlewareStoreApi, type MiddlewareSubscribe, type MockStorage, type MockedAction, type PersistOptions, type Plugin, type Selector, type SelectorFn, type SelectorOptions, type SetSlice, type SliceCreator, type SliceDefinition, type StateAssertion, type StateChange, type StateCreatorFn, type StorageLike$1 as StorageLike, type Store, type StoreBuilder, StoreError, StoreError as StoreErrorClass, StoreErrorCode, type StoreOptions, type StoreSpy, type StoreState, type SubscribeOptions, type SyncOptions, type TestStore, type TestStoreOptions, type TransactionFn, type ValidateOptions, type ValidationAPI, type ValidationError, type ValidationResult, type ValidationTiming, type ValidatorFn, assertState, batch, combineComputed, combineEffects, combineSlices, combineValidators, compose, computed, createAsyncValidator, createDebouncedEffect, createEffect, createFederatedComputed, createFederatedSelector, createFederation, createFieldValidator, createLogger, createMockStorage, createNamespacedSlice, createSimpleEffect, createSimpleMiddleware, createSlice, createStorage, createStore, createSubscriber, createTestStore, createValidator, createWithMiddleware, deepClone, deepEqual, deepMerge, delay, devtools, effects, extendStore, flushMicrotasks, hasHistory, history, identity, immer, isFunction, logger, memoizeSelector, middlewareCompat, mockAction, omit, persist, pick, produce, resetSlices, selector, sessionStorage, shallowEqual, snapshot, spyOnStore, stateDiff, subscribeOnce, subscribeToMany, subscribeWithOptions, sync, toMiddlewareApi, triggerSync, useAction, useCreateStore, useSetState, useShallow, useStore, useStoreActions, useStoreSelector, useTransientSubscribe, validate, waitForFederated };
|