@veams/status-quo 1.7.0 → 1.8.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.
Files changed (94) hide show
  1. package/.turbo/turbo-test.log +115 -15
  2. package/CHANGELOG.md +2 -0
  3. package/README.md +51 -7
  4. package/dist/config/status-quo-config.d.ts +22 -1
  5. package/dist/config/status-quo-config.js +46 -2
  6. package/dist/config/status-quo-config.js.map +1 -1
  7. package/dist/index.d.ts +12 -2
  8. package/dist/index.js +22 -2
  9. package/dist/index.js.map +1 -1
  10. package/dist/react/hooks/__tests__/state-provider.spec.js +2 -2
  11. package/dist/react/hooks/__tests__/state-provider.spec.js.map +1 -1
  12. package/dist/react/hooks/index.d.ts +6 -3
  13. package/dist/react/hooks/index.js +12 -3
  14. package/dist/react/hooks/index.js.map +1 -1
  15. package/dist/react/hooks/state-actions.d.ts +9 -1
  16. package/dist/react/hooks/state-actions.js +21 -2
  17. package/dist/react/hooks/state-actions.js.map +1 -1
  18. package/dist/react/hooks/state-factory.d.ts +7 -0
  19. package/dist/react/hooks/state-factory.js +23 -1
  20. package/dist/react/hooks/state-factory.js.map +1 -1
  21. package/dist/react/hooks/state-handler.d.ts +4 -0
  22. package/dist/react/hooks/state-handler.js +18 -1
  23. package/dist/react/hooks/state-handler.js.map +1 -1
  24. package/dist/react/hooks/state-provider.d.ts +18 -9
  25. package/dist/react/hooks/state-provider.js +25 -17
  26. package/dist/react/hooks/state-provider.js.map +1 -1
  27. package/dist/react/hooks/state-singleton.d.ts +8 -2
  28. package/dist/react/hooks/state-singleton.js +21 -3
  29. package/dist/react/hooks/state-singleton.js.map +1 -1
  30. package/dist/react/hooks/state-subscription-selector.d.ts +4 -0
  31. package/dist/react/hooks/state-subscription-selector.js +64 -8
  32. package/dist/react/hooks/state-subscription-selector.js.map +1 -1
  33. package/dist/react/hooks/state-subscription.d.ts +12 -0
  34. package/dist/react/hooks/state-subscription.js +49 -1
  35. package/dist/react/hooks/state-subscription.js.map +1 -1
  36. package/dist/react/index.d.ts +4 -1
  37. package/dist/react/index.js +5 -1
  38. package/dist/react/index.js.map +1 -1
  39. package/dist/store/__tests__/native-state-handler.spec.d.ts +1 -0
  40. package/dist/store/__tests__/native-state-handler.spec.js +210 -0
  41. package/dist/store/__tests__/native-state-handler.spec.js.map +1 -0
  42. package/dist/store/__tests__/observable-state-handler.spec.d.ts +7 -0
  43. package/dist/store/__tests__/observable-state-handler.spec.js.map +1 -1
  44. package/dist/store/base-state-handler.d.ts +42 -0
  45. package/dist/store/base-state-handler.js +73 -10
  46. package/dist/store/base-state-handler.js.map +1 -1
  47. package/dist/store/dev-tools.d.ts +42 -17
  48. package/dist/store/dev-tools.js +24 -8
  49. package/dist/store/dev-tools.js.map +1 -1
  50. package/dist/store/index.d.ts +7 -0
  51. package/dist/store/index.js +9 -0
  52. package/dist/store/index.js.map +1 -1
  53. package/dist/store/native-state-handler.d.ts +44 -0
  54. package/dist/store/native-state-handler.js +62 -0
  55. package/dist/store/native-state-handler.js.map +1 -0
  56. package/dist/store/observable-state-handler.d.ts +34 -0
  57. package/dist/store/observable-state-handler.js +45 -1
  58. package/dist/store/observable-state-handler.js.map +1 -1
  59. package/dist/store/signal-state-handler.d.ts +26 -0
  60. package/dist/store/signal-state-handler.js +35 -0
  61. package/dist/store/signal-state-handler.js.map +1 -1
  62. package/dist/store/state-singleton.d.ts +14 -0
  63. package/dist/store/state-singleton.js +20 -1
  64. package/dist/store/state-singleton.js.map +1 -1
  65. package/dist/types/types.d.ts +9 -0
  66. package/dist/types/types.js +3 -0
  67. package/dist/types/types.js.map +1 -1
  68. package/dist/utils/selector-cache.d.ts +17 -0
  69. package/dist/utils/selector-cache.js +28 -1
  70. package/dist/utils/selector-cache.js.map +1 -1
  71. package/package.json +12 -1
  72. package/src/config/status-quo-config.ts +64 -1
  73. package/src/index.ts +29 -0
  74. package/src/react/hooks/__tests__/state-provider.spec.tsx +2 -2
  75. package/src/react/hooks/index.ts +13 -8
  76. package/src/react/hooks/state-actions.tsx +23 -2
  77. package/src/react/hooks/state-factory.tsx +34 -0
  78. package/src/react/hooks/state-handler.tsx +15 -0
  79. package/src/react/hooks/state-provider.tsx +36 -40
  80. package/src/react/hooks/state-singleton.tsx +37 -7
  81. package/src/react/hooks/state-subscription-selector.tsx +85 -7
  82. package/src/react/hooks/state-subscription.tsx +75 -0
  83. package/src/react/index.ts +16 -1
  84. package/src/store/__tests__/native-state-handler.spec.ts +291 -0
  85. package/src/store/__tests__/observable-state-handler.spec.ts +8 -0
  86. package/src/store/base-state-handler.ts +89 -12
  87. package/src/store/dev-tools.ts +72 -27
  88. package/src/store/index.ts +16 -0
  89. package/src/store/native-state-handler.ts +98 -0
  90. package/src/store/observable-state-handler.ts +57 -0
  91. package/src/store/signal-state-handler.ts +47 -1
  92. package/src/store/state-singleton.ts +30 -0
  93. package/src/types/types.ts +16 -0
  94. package/src/utils/selector-cache.ts +37 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Import internal configuration and the abstract base state handler.
3
+ */
4
+ import { resolveDistinctOptions } from '../config/status-quo-config.js';
5
+ import { BaseStateHandler } from './base-state-handler.js';
6
+
7
+ /**
8
+ * Import necessary types for DevTools and distinct update options.
9
+ */
10
+ import type { DevToolsOptions, DistinctOptions } from '../config/status-quo-config.js';
11
+
12
+ /**
13
+ * Props for configuring the NativeStateHandler instance.
14
+ */
15
+ type NativeStateHandlerProps<S> = {
16
+ // The initial value for the state.
17
+ initialState: S;
18
+ // Optional configuration for DevTools and distinct update behaviors.
19
+ options?: {
20
+ devTools?: DevToolsOptions;
21
+ distinct?: DistinctOptions<S>;
22
+ useDistinctUntilChanged?: boolean;
23
+ };
24
+ };
25
+
26
+ /**
27
+ * NativeStateHandler: A lightweight state handler using plain JavaScript.
28
+ * Ideal for minimizing dependencies when neither RxJS nor Signals are required.
29
+ */
30
+ export abstract class NativeStateHandler<S, A> extends BaseStateHandler<S, A> {
31
+ // The actual state value stored in memory.
32
+ private state: S;
33
+ // A collection of active listeners to notify when the state changes.
34
+ private readonly listeners: Set<(value: S) => void> = new Set();
35
+ // Configuration to determine if and how state changes should trigger notifications.
36
+ private readonly distinctOptions: ReturnType<typeof resolveDistinctOptions<S>>;
37
+
38
+ /**
39
+ * Initializes the native state handler with given state and configuration.
40
+ */
41
+ protected constructor({ initialState, options }: NativeStateHandlerProps<S>) {
42
+ // Pass the initial state to the base handler.
43
+ super(initialState);
44
+ // Set the initial internal state.
45
+ this.state = initialState;
46
+ // Resolve the final distinct options based on provided configuration.
47
+ this.distinctOptions = resolveDistinctOptions(
48
+ options?.distinct,
49
+ options?.useDistinctUntilChanged
50
+ );
51
+ // Initialize Redux DevTools integration.
52
+ this.initDevTools(options?.devTools);
53
+ }
54
+
55
+ /**
56
+ * Internal implementation of getting the state value.
57
+ */
58
+ protected getStateValue() {
59
+ return this.state;
60
+ }
61
+
62
+ /**
63
+ * Internal implementation of updating the state value.
64
+ */
65
+ protected setStateValue(nextState: S) {
66
+ // Store the previous state to compare against the new state.
67
+ const previousState = this.state;
68
+ // Update the internal state storage.
69
+ this.state = nextState;
70
+
71
+ // If distinct mode is enabled and the state hasn't effectively changed, stop here.
72
+ if (this.distinctOptions.enabled && this.distinctOptions.comparator(previousState, nextState)) {
73
+ return;
74
+ }
75
+
76
+ // Notify all active listeners of the state update.
77
+ this.listeners.forEach((listener) => listener(nextState));
78
+ }
79
+
80
+ /**
81
+ * Subscribes a listener function to state changes.
82
+ * Overloaded to handle both void and state-receiving listeners.
83
+ */
84
+ subscribe(listener: () => void): () => void;
85
+ subscribe(listener: (value: S) => void): () => void;
86
+ subscribe(listener: (value: S) => void) {
87
+ // Register the listener to the active set.
88
+ this.listeners.add(listener);
89
+
90
+ // Call the listener immediately with the current state to ensure initial consistency.
91
+ listener(this.state);
92
+
93
+ // Return an unsubscribe function to remove the listener from the set.
94
+ return () => {
95
+ this.listeners.delete(listener);
96
+ };
97
+ }
98
+ }
@@ -1,13 +1,27 @@
1
+ /**
2
+ * Import RxJS components for reactive state handling.
3
+ */
1
4
  import { BehaviorSubject, distinctUntilChanged, distinctUntilKeyChanged, map } from 'rxjs';
2
5
 
6
+ /**
7
+ * Import internal configuration and the abstract base state handler.
8
+ */
3
9
  import { resolveDistinctOptions } from '../config/status-quo-config.js';
4
10
  import { BaseStateHandler } from './base-state-handler.js';
5
11
 
12
+ /**
13
+ * Import necessary types for DevTools, distinct update options, and RxJS Observable.
14
+ */
6
15
  import type { DevToolsOptions, DistinctOptions } from '../config/status-quo-config.js';
7
16
  import type { Observable } from 'rxjs';
8
17
 
18
+ /**
19
+ * Props for configuring the ObservableStateHandler instance.
20
+ */
9
21
  type ObservableStateHandlerProps<S> = {
22
+ // The initial value for the state.
10
23
  initialState: S;
24
+ // Optional configuration for DevTools and distinct update behaviors.
11
25
  options?: {
12
26
  devTools?: DevToolsOptions;
13
27
  distinct?: DistinctOptions<S>;
@@ -15,57 +29,100 @@ type ObservableStateHandlerProps<S> = {
15
29
  };
16
30
  };
17
31
 
32
+ /**
33
+ * Options for configuring state-related observables.
34
+ */
18
35
  type StateObservableOptions = { useDistinctUntilChanged?: boolean };
19
36
 
37
+ /**
38
+ * ObservableStateHandler: A state handler built on top of RxJS BehaviorSubject.
39
+ * Provides powerful reactive patterns and integration with RxJS pipelines.
40
+ */
20
41
  export abstract class ObservableStateHandler<S, A> extends BaseStateHandler<S, A> {
42
+ // Internal BehaviorSubject to manage the state and provide current values to new subscribers.
21
43
  private readonly state$: BehaviorSubject<S>;
44
+ // Configuration to determine if and how state changes should trigger notifications.
22
45
  private readonly distinctOptions: ReturnType<typeof resolveDistinctOptions<S>>;
23
46
 
47
+ /**
48
+ * Initializes the observable state handler with given state and configuration.
49
+ */
24
50
  protected constructor({ initialState, options }: ObservableStateHandlerProps<S>) {
51
+ // Pass the initial state to the base handler.
25
52
  super(initialState);
53
+ // Initialize the internal BehaviorSubject with the initial state.
26
54
  this.state$ = new BehaviorSubject<S>(initialState);
55
+ // Resolve the final distinct options based on provided configuration.
27
56
  this.distinctOptions = resolveDistinctOptions(
28
57
  options?.distinct,
29
58
  options?.useDistinctUntilChanged
30
59
  );
60
+ // Initialize Redux DevTools integration.
31
61
  this.initDevTools(options?.devTools);
32
62
  }
33
63
 
64
+ /**
65
+ * Internal implementation of getting the state value via BehaviorSubject.
66
+ */
34
67
  protected getStateValue() {
35
68
  return this.state$.getValue();
36
69
  }
37
70
 
71
+ /**
72
+ * Internal implementation of updating the state value via BehaviorSubject.
73
+ */
38
74
  protected setStateValue(nextState: S) {
75
+ // Notify the subject and all its subscribers of the new state.
39
76
  this.state$.next(nextState);
40
77
  }
41
78
 
79
+ /**
80
+ * Returns an observable tracking a specific key within the state object.
81
+ * Only emits when the value of the specified key actually changes.
82
+ */
42
83
  getObservableItem<K extends keyof S>(key: K): Observable<S[K]> {
43
84
  return this.state$.pipe(
85
+ // Ensure we only emit if the value associated with the key has changed.
44
86
  distinctUntilKeyChanged(key),
87
+ // Transform the state object into the specific key value.
45
88
  map((state) => state[key])
46
89
  );
47
90
  }
48
91
 
92
+ /**
93
+ * Returns an observable tracking the entire state object.
94
+ * Can be configured to emit only when the state object effectively changes.
95
+ */
49
96
  getObservable(options: StateObservableOptions = {}): Observable<S> {
97
+ // Determine whether distinct emissions are enabled.
50
98
  const useDistinctUntilChanged = options.useDistinctUntilChanged ?? this.distinctOptions.enabled;
51
99
 
100
+ // If distinct emissions are not enabled, return the subject as-is.
52
101
  if (!useDistinctUntilChanged) {
53
102
  return this.state$;
54
103
  }
55
104
 
105
+ // Apply the distinctUntilChanged operator to filter out redundant updates.
56
106
  return this.state$.pipe(
57
107
  distinctUntilChanged((previous, next) => {
108
+ // Use the resolved comparator to compare the previous and next states.
58
109
  return this.distinctOptions.comparator(previous, next);
59
110
  })
60
111
  );
61
112
  }
62
113
 
114
+ /**
115
+ * Subscribes a listener function to state changes via the internal observable.
116
+ * Overloaded to handle both void and state-receiving listeners.
117
+ */
63
118
  subscribe(listener: () => void): () => void;
64
119
  subscribe(listener: (value: S) => void): () => void;
65
120
  subscribe(listener: (value: S) => void) {
121
+ // Subscribe to the state observable and call the listener on each emission.
66
122
  const subscription = this.getObservable().subscribe((nextState) => {
67
123
  listener(nextState);
68
124
  });
125
+ // Return an unsubscribe function to properly clean up the RxJS subscription.
69
126
  return () => subscription.unsubscribe();
70
127
  }
71
128
  }
@@ -1,13 +1,27 @@
1
+ /**
2
+ * Import signal-based state primitives from @preact/signals-core.
3
+ */
1
4
  import { signal } from '@preact/signals-core';
2
5
 
6
+ /**
7
+ * Import internal configuration and the abstract base state handler.
8
+ */
3
9
  import { resolveDistinctOptions } from '../config/status-quo-config.js';
4
10
  import { BaseStateHandler } from './base-state-handler.js';
5
11
 
12
+ /**
13
+ * Import necessary types for DevTools, distinct update options, and Preact Signals.
14
+ */
6
15
  import type { DevToolsOptions, DistinctOptions } from '../config/status-quo-config.js';
7
16
  import type { Signal } from '@preact/signals-core';
8
17
 
18
+ /**
19
+ * Props for configuring the SignalStateHandler instance.
20
+ */
9
21
  type SignalStateHandlerProps<S> = {
22
+ // The initial value for the state.
10
23
  initialState: S;
24
+ // Optional configuration for DevTools and distinct update behaviors.
11
25
  options?: {
12
26
  devTools?: DevToolsOptions;
13
27
  distinct?: DistinctOptions<S>;
@@ -15,32 +29,55 @@ type SignalStateHandlerProps<S> = {
15
29
  };
16
30
  };
17
31
 
32
+ /**
33
+ * SignalStateHandler: A state handler built on top of Preact Signals.
34
+ * Provides fine-grained reactivity and efficient state updates.
35
+ */
18
36
  export abstract class SignalStateHandler<S, A> extends BaseStateHandler<S, A> {
37
+ // Internal Signal to manage the state with high performance.
19
38
  private readonly state: Signal<S>;
39
+ // Configuration to determine if and how state changes should trigger notifications.
20
40
  private readonly distinctOptions: ReturnType<typeof resolveDistinctOptions<S>>;
21
41
 
42
+ /**
43
+ * Initializes the signal state handler with given state and configuration.
44
+ */
22
45
  protected constructor({ initialState, options }: SignalStateHandlerProps<S>) {
46
+ // Pass the initial state to the base handler.
23
47
  super(initialState);
24
-
48
+ // Initialize the internal Signal with the initial state.
25
49
  this.state = signal<S>(initialState);
50
+ // Resolve the final distinct options based on provided configuration.
26
51
  this.distinctOptions = resolveDistinctOptions(
27
52
  options?.distinct,
28
53
  options?.useDistinctUntilChanged
29
54
  );
55
+ // Initialize Redux DevTools integration.
30
56
  this.initDevTools(options?.devTools);
31
57
  }
32
58
 
59
+ /**
60
+ * Returns the underlying signal for external use (e.g., in reactive templates).
61
+ */
33
62
  getSignal() {
34
63
  return this.state;
35
64
  }
36
65
 
66
+ /**
67
+ * Subscribes a listener function to state changes via the internal signal.
68
+ * Overloaded to handle both void and state-receiving listeners.
69
+ */
37
70
  subscribe(listener: () => void): () => void;
38
71
  subscribe(listener: (value: S) => void): () => void;
39
72
  subscribe(listener: (value: S) => void) {
73
+ // Track whether the listener has been initialized during the first emission.
40
74
  let initialized = false;
75
+ // Keep track of the previous state snapshot for comparison.
41
76
  let previousSnapshot = this.state.value;
42
77
 
78
+ // Use the Signal's subscription method.
43
79
  return this.state.subscribe((nextState) => {
80
+ // If this is the initial emission, notify the listener and mark as initialized.
44
81
  if (!initialized) {
45
82
  initialized = true;
46
83
  previousSnapshot = nextState;
@@ -48,6 +85,7 @@ export abstract class SignalStateHandler<S, A> extends BaseStateHandler<S, A> {
48
85
  return;
49
86
  }
50
87
 
88
+ // If distinct mode is enabled and the state hasn't effectively changed, stop here.
51
89
  if (
52
90
  this.distinctOptions.enabled &&
53
91
  this.distinctOptions.comparator(previousSnapshot, nextState)
@@ -56,16 +94,24 @@ export abstract class SignalStateHandler<S, A> extends BaseStateHandler<S, A> {
56
94
  return;
57
95
  }
58
96
 
97
+ // Update the previous snapshot and notify the listener.
59
98
  previousSnapshot = nextState;
60
99
  listener(nextState);
61
100
  });
62
101
  }
63
102
 
103
+ /**
104
+ * Internal implementation of getting the state value via the Signal's value property.
105
+ */
64
106
  protected getStateValue() {
65
107
  return this.state.value;
66
108
  }
67
109
 
110
+ /**
111
+ * Internal implementation of updating the state value via the Signal's value property.
112
+ */
68
113
  protected setStateValue(nextState: S) {
114
+ // Update the Signal's value, which automatically notifies its subscribers.
69
115
  this.state.value = nextState;
70
116
  }
71
117
  }
@@ -1,39 +1,69 @@
1
+ /**
2
+ * Import necessary type for state subscription management.
3
+ */
1
4
  import type { StateSubscriptionHandler } from '../types/types.js';
2
5
 
6
+ /**
7
+ * Interface representing a singleton state handler.
8
+ * Provides a method to get the single instance of a state handler.
9
+ */
3
10
  export interface StateSingleton<V, A> {
11
+ // Access the singleton instance of the state handler.
4
12
  getInstance: () => StateSubscriptionHandler<V, A>;
5
13
  }
6
14
 
15
+ /**
16
+ * Options for configuring the behavior of a state singleton.
17
+ */
7
18
  export interface StateSingletonOptions {
19
+ // Whether to automatically destroy the instance when no more consumers are active.
8
20
  destroyOnNoConsumers?: boolean;
9
21
  }
10
22
 
23
+ /**
24
+ * Factory function to create a singleton state handler.
25
+ * Ensures only one instance of the state handler exists throughout the application.
26
+ */
11
27
  export function makeStateSingleton<S, A>(
28
+ // Function responsible for creating the state handler instance when needed.
12
29
  stateHandlerFactory: () => StateSubscriptionHandler<S, A>,
30
+ // Optional configuration for the singleton.
13
31
  { destroyOnNoConsumers = false }: StateSingletonOptions = {}
14
32
  ): StateSingleton<S, A> {
33
+ // Internal variable to store the shared instance of the state handler.
15
34
  let instance: StateSubscriptionHandler<S, A> | null = null;
35
+
36
+ // The singleton object with an added internal destroy method.
16
37
  const singleton: StateSingleton<S, A> & {
17
38
  destroyInstance: () => void;
18
39
  destroyOnNoConsumers: boolean;
19
40
  } = {
41
+ // Expose whether this singleton should be destroyed when not in use.
20
42
  destroyOnNoConsumers,
43
+ // Method to get or create the singleton instance.
21
44
  getInstance() {
45
+ // If no instance exists, create it using the factory function.
22
46
  if (!instance) {
23
47
  instance = stateHandlerFactory();
24
48
  }
25
49
 
50
+ // Return the current instance.
26
51
  return instance;
27
52
  },
53
+ // Method to manually destroy the singleton instance and its resources.
28
54
  destroyInstance() {
55
+ // If no instance exists, there's nothing to destroy.
29
56
  if (!instance) {
30
57
  return;
31
58
  }
32
59
 
60
+ // Call the destroy method on the instance to clean up its resources.
33
61
  instance.destroy();
62
+ // Clear the internal reference to the instance.
34
63
  instance = null;
35
64
  },
36
65
  };
37
66
 
67
+ // Return the created singleton object.
38
68
  return singleton;
39
69
  }
@@ -1,8 +1,24 @@
1
+ /**
2
+ * Common types and interfaces for state management.
3
+ */
4
+
5
+ /**
6
+ * Interface representing a standard state handler with subscription support.
7
+ * Used by React hooks and other components to interact with the state store.
8
+ * V: The type of the state value.
9
+ * A: The type of the available actions.
10
+ */
1
11
  export interface StateSubscriptionHandler<V, A> {
12
+ // Method to subscribe a listener to state changes.
2
13
  subscribe(listener: () => void): () => void;
14
+ // Method to subscribe a listener that receives the updated state value.
3
15
  subscribe(listener: (value: V) => void): () => void;
16
+ // Method to retrieve the current state snapshot.
4
17
  getSnapshot: () => V;
18
+ // Method to clean up resources associated with the handler.
5
19
  destroy: () => void;
20
+ // Method to retrieve the initial state value.
6
21
  getInitialState: () => V;
22
+ // Method to retrieve the set of available actions for this handler.
7
23
  getActions: () => A;
8
24
  }
@@ -1,43 +1,80 @@
1
+ /**
2
+ * Utility functions for caching selector results to optimize state updates.
3
+ */
4
+
5
+ // Function signature for transforming state into a specific selected value.
1
6
  export type Selector<Value, Selected> = (value: Value) => Selected;
7
+
8
+ // Function signature for comparing two selected values for equality.
2
9
  export type EqualityFn<Selected> = (current: Selected, next: Selected) => boolean;
3
10
 
11
+ /**
12
+ * Interface for internal selector cache storage.
13
+ */
4
14
  type SelectorCache<Selected> = {
15
+ // Whether the cache already contains a computed value.
5
16
  hasValue: boolean;
17
+ // The cached value of the selection result.
6
18
  value: Selected | undefined;
7
19
  };
8
20
 
21
+ /**
22
+ * Interface for the result of a selection operation.
23
+ */
9
24
  type SelectionResult<Selected> = {
25
+ // The selected value (either newly computed or from cache).
10
26
  value: Selected;
27
+ // Whether the selected value has effectively changed since the last check.
11
28
  hasChanged: boolean;
12
29
  };
13
30
 
31
+ /**
32
+ * Creates a new, empty selector cache.
33
+ * Provides a simple container to track state and values across selections.
34
+ */
14
35
  export function createSelectorCache<Selected>(): SelectorCache<Selected> {
15
36
  return {
37
+ // Initially, no value is stored in the cache.
16
38
  hasValue: false,
17
39
  value: undefined,
18
40
  };
19
41
  }
20
42
 
43
+ /**
44
+ * Executes a selector and returns the cached value if the result is considered equal.
45
+ * This optimization avoids unnecessary re-renders or updates when the derived state is the same.
46
+ */
21
47
  export function selectWithCache<Value, Selected>(
48
+ // The cache object to store and compare values.
22
49
  selectorCache: SelectorCache<Selected>,
50
+ // The source value from which to select.
23
51
  value: Value,
52
+ // The selector function to transform the source value.
24
53
  selector: Selector<Value, Selected>,
54
+ // Optional equality function to compare selected values (defaults to Object.is).
25
55
  isEqual: EqualityFn<Selected> = Object.is
26
56
  ): SelectionResult<Selected> {
57
+ // Execute the selector to derive the current selection value.
27
58
  const nextSelection = selector(value);
28
59
 
60
+ // If the cache has a value and the new selection is equal to it, return the cached version.
29
61
  if (selectorCache.hasValue && isEqual(selectorCache.value as Selected, nextSelection)) {
30
62
  return {
63
+ // Return the previously cached value to maintain referential stability.
31
64
  value: selectorCache.value as Selected,
65
+ // Signal that no effective change has occurred.
32
66
  hasChanged: false,
33
67
  };
34
68
  }
35
69
 
70
+ // Update the cache with the newly computed selection.
36
71
  selectorCache.hasValue = true;
37
72
  selectorCache.value = nextSelection;
38
73
 
39
74
  return {
75
+ // Return the fresh selection result.
40
76
  value: nextSelection,
77
+ // Signal that the selected value has changed.
41
78
  hasChanged: true,
42
79
  };
43
80
  }