react-hooks-global-states 16.0.1 → 16.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/GlobalStore.cjs +1 -0
  2. package/GlobalStore.d.ts +160 -0
  3. package/GlobalStore.debugProps.cjs +1 -0
  4. package/GlobalStore.debugProps.d.ts +5 -0
  5. package/GlobalStore.debugProps.js +1 -0
  6. package/GlobalStore.debugProps.mjs +1 -0
  7. package/GlobalStore.js +1 -0
  8. package/GlobalStore.mjs +1 -0
  9. package/actions.cjs +1 -0
  10. package/actions.d.ts +32 -0
  11. package/actions.js +1 -0
  12. package/actions.mjs +1 -0
  13. package/bundle.cjs +1 -0
  14. package/bundle.js +1 -0
  15. package/bundle.mjs +1 -0
  16. package/createContext.cjs +1 -0
  17. package/createContext.d.ts +15 -0
  18. package/createContext.js +1 -0
  19. package/createContext.mjs +1 -0
  20. package/createGlobalState.cjs +1 -0
  21. package/createGlobalState.d.ts +7 -0
  22. package/createGlobalState.js +1 -0
  23. package/createGlobalState.mjs +1 -0
  24. package/index.d.ts +9 -0
  25. package/isRecord.cjs +1 -0
  26. package/isRecord.d.ts +2 -0
  27. package/isRecord.js +1 -0
  28. package/isRecord.mjs +1 -0
  29. package/package.json +1 -51
  30. package/shallowCompare.cjs +1 -0
  31. package/shallowCompare.d.ts +52 -0
  32. package/shallowCompare.js +1 -0
  33. package/shallowCompare.mjs +1 -0
  34. package/throwWrongKeyOnActionCollectionConfig.cjs +12 -0
  35. package/throwWrongKeyOnActionCollectionConfig.d.ts +2 -0
  36. package/throwWrongKeyOnActionCollectionConfig.js +12 -0
  37. package/throwWrongKeyOnActionCollectionConfig.mjs +12 -0
  38. package/types.cjs +1 -0
  39. package/types.d.ts +1268 -0
  40. package/types.js +1 -0
  41. package/types.mjs +0 -0
  42. package/uniqueId.cjs +1 -0
  43. package/uniqueId.d.ts +7 -0
  44. package/uniqueId.js +1 -0
  45. package/uniqueId.mjs +1 -0
  46. package/jest.config.js +0 -50
package/types.d.ts ADDED
@@ -0,0 +1,1268 @@
1
+ import type GlobalStore from './GlobalStore';
2
+ import type { Context as ReactContext, PropsWithChildren } from 'react';
3
+ export type Any = any;
4
+ export type AnyFunction = (...args: Any[]) => Any;
5
+ /**
6
+ * @description Represents a hook that returns a readonly state.
7
+ */
8
+ export interface ReadonlyHook<State, StateMutator, Metadata extends BaseMetadata> extends ReadonlyStateApi<State, StateMutator, Metadata> {
9
+ /**
10
+ * @description Returns the current state value.
11
+ */
12
+ (): State;
13
+ /**
14
+ * @description Returns a derived state based on the provided selector function.
15
+ */
16
+ <Derivate>(selector: (state: State) => Derivate, dependencies?: unknown[]): Derivate;
17
+ /**
18
+ * @description Returns a derived state based on the provided selector function and configuration.
19
+ */
20
+ <Derivate>(selector: (state: State) => Derivate, config?: UseHookOptions<Derivate, State>): Derivate;
21
+ }
22
+ /**
23
+ * @description Hook to select a fragment of the state
24
+ */
25
+ export interface SelectHook<State> {
26
+ /**
27
+ * @description Selects a fragment of the state using the provided selector function.
28
+ * @param selector - The function to select a fragment of the state.
29
+ * @param dependencies - Optional dependencies array to control re-evaluation.
30
+ * @returns The selected fragment of the state.
31
+ */
32
+ <Selection>(selector: (state: State) => Selection, dependencies?: unknown[]): Selection;
33
+ /**
34
+ * @description Selects a fragment of the state using the provided selector function and configuration.
35
+ * @param selector - The function to select a fragment of the state.
36
+ * @param args - Configuration options for the selection.
37
+ * @returns The selected fragment of the state.
38
+ */
39
+ <Selection>(selector: (state: State) => Selection, args?: UseHookOptions<Selection, State>): Selection;
40
+ }
41
+ /**
42
+ * @description Represents the complete non-reactive API of a global state instance.
43
+ * This API provides full control over the state, including reading, writing, subscribing,
44
+ * and creating derived hooks or observable fragments.
45
+ */
46
+ export type StateApi<State, StateMutator, Metadata extends BaseMetadata> = {
47
+ /**
48
+ * @description The metadata associated with the global state.
49
+ * Metadata is additional non reactive information associated with the global state, reading it will not trigger re-renders.
50
+ * To change the metadata use `setMetadata`.
51
+ */
52
+ readonly metadata: Metadata;
53
+ /**
54
+ * @deprecated Use the `metadata` property instead, e.g. `const { metadata } = api;`. Metadata is stable, so it is exposed directly.
55
+ * Returns the metadata
56
+ * Metadata is additional non reactive information associated with the global state
57
+ */
58
+ getMetadata: MetadataGetter<Metadata>;
59
+ /**
60
+ * Sets the metadata value
61
+ * The metadata value is not reactive and wont trigger re-renders
62
+ */
63
+ setMetadata: MetadataSetter<Metadata>;
64
+ /**
65
+ * @description Contains the generated action functions if custom actions are defined.
66
+ * If no actions are provided, this property is `null`.
67
+ */
68
+ actions: StateMutator extends AnyFunction ? null : StateMutator;
69
+ /**
70
+ * @description Provides direct access to the state updater.
71
+ * Always available for testing purposes, even when actions are defined.
72
+ * In production, prefer using actions when they are defined.
73
+ */
74
+ setState: React.Dispatch<React.SetStateAction<State>>;
75
+ /**
76
+ * @description Get the current state value
77
+ */
78
+ getState: () => State;
79
+ /**
80
+ * @description Subscribe to the state changes
81
+ * You can subscribe to the whole state or to a fragment of the state by passing a selector as first parameter,
82
+ * this can be used in non react environments to listen to the state changes
83
+ */
84
+ subscribe: SubscribeToState<State>;
85
+ /***
86
+ * @description Creates a new hooks that returns the result of the selector passed as a parameter
87
+ * Your can create selector hooks of other selectors hooks and extract as many derived states as or fragments of the state as you want
88
+ * The selector hook will be evaluated only if the result of the selector changes and the equality function returns false
89
+ * you can customize the equality function by passing the isEqualRoot and isEqual parameters
90
+ */
91
+ createSelectorHook: <Selection>(selector: (state: State) => Selection, args?: Omit<UseHookOptions<Selection, State>, 'dependencies'> & {
92
+ name?: string;
93
+ }) => ReadonlyHook<Selection, StateMutator, Metadata>;
94
+ /**
95
+ * @description Creates a function that allows you to subscribe to a fragment of the state
96
+ * The observable selection will notify the subscribers only if the fragment changes and the equality function returns false
97
+ * you can customize the equality function by passing the isEqualRoot and isEqual parameters
98
+ */
99
+ createObservable: <Selection>(this: ReadonlyStateApi<State, StateMutator, Metadata>, selector: (state: State) => Selection, args?: {
100
+ isEqual?: (current: Selection, next: Selection) => boolean;
101
+ isEqualRoot?: (current: State, next: State) => boolean;
102
+ /**
103
+ * @description Name of the observable fragment for debugging purposes
104
+ */
105
+ name?: string;
106
+ }) => ObservableFragment<Selection, StateMutator, Metadata>;
107
+ /**
108
+ * @description Selects a fragment of the state using the provided selector function.
109
+ */
110
+ select: SelectHook<State>;
111
+ /**
112
+ * @description Sugared hook to use the global state in React components
113
+ * Allows you to tread the global hook as an store, with better semantics
114
+ *
115
+ * @example
116
+ * ```tsx
117
+ * const contacts = createContext<ContactType[]>([]);
118
+ *
119
+ * function ContactsList() {
120
+ * const [contacts, setContacts] = contacts.use();
121
+ * ```
122
+ *
123
+ * This is more practical since the StateApi is slightly more complex than a simple hook
124
+ *
125
+ * @example
126
+ * Using a global state:
127
+ *
128
+ * ```tsx
129
+ * const [state, setState, metadata] = state.use();
130
+ *
131
+ * const selection = state.select((state) => state.someFragment);
132
+ *
133
+ * const unsubscribe = state.subscribe((state) => { ... });
134
+ *
135
+ * const useFragment = state.createSelectorHook((state) => state.someFragment);
136
+ *
137
+ * const observable = state.createObservable((state) => state.someFragment);
138
+ * ```
139
+ */
140
+ use: StateHook<State, StateMutator, Metadata>;
141
+ /**
142
+ * @description Disposes the global state instance, cleaning up all resources and subscriptions.
143
+ */
144
+ dispose: () => void;
145
+ /**
146
+ * PLEASE USE CALLBACK-BASED INITIALIZERS FOR STATE AND METADATA IF YOU PLAN TO USE RESET OFTEN.
147
+ *
148
+ * @description Resets the store to its initial state and metadata as provided during creation.
149
+ * This method is reserved for advanced use cases and testing scenarios, use with caution.
150
+ * If the initial state and metadata are static values this may not work as you expect.
151
+ */
152
+ reset(): void;
153
+ /**
154
+ * @description Resets the store to a new initial state and re-runs initialization (including onInit callbacks).
155
+ * Existing subscribers are maintained and notified of the new state.
156
+ * Useful for testing scenarios where you need to reinitialize the store.
157
+ * @param newState - The new initial state to reset to
158
+ * @param newMetadata - The new metadata to set after reset
159
+ */
160
+ reset(newState: State, newMetadata: Metadata): void;
161
+ /**
162
+ * @deprecated
163
+ * @description Useful for debugging purposes, exposes the current subscribers of the store
164
+ * You'll probably not need to use this in your application
165
+ */
166
+ subscribers: Set<SubscriberParameters>;
167
+ };
168
+ /**
169
+ * @description Readonly version of the StateApi, excluding mutative methods.
170
+ */
171
+ export type ReadonlyStateApi<State, StateMutator, Metadata extends BaseMetadata> = Pick<StateApi<State, StateMutator, Metadata>, 'dispose' | 'getState' | 'subscribe' | 'createSelectorHook' | 'createObservable' | 'subscribers'>;
172
+ /**
173
+ * @description Function that allows you to subscribe to a fragment of the state
174
+ */
175
+ export type ObservableFragment<State, StateMutator, Metadata extends BaseMetadata> = SubscribeToState<State> & Pick<StateApi<State, StateMutator, Metadata>, 'getState' | 'subscribe' | 'createSelectorHook' | 'createObservable' | 'dispose' | 'subscribers'>;
176
+ export interface StateHook<State, StateMutator, Metadata extends BaseMetadata> extends StateApi<State, StateMutator, Metadata> {
177
+ /**
178
+ * @description React hook that provides access to the state, state mutator, and metadata.
179
+ */
180
+ (): Readonly<[state: State, stateMutator: StateMutator, metadata: Metadata]>;
181
+ /**
182
+ * @description React hook that provides a derived state based on the provided selector function.
183
+ */
184
+ <Derivate>(selector: (state: State) => Derivate, dependencies?: unknown[]): Readonly<[state: Derivate, stateMutator: StateMutator, metadata: Metadata]>;
185
+ /**
186
+ * @description React hook that provides a derived state based on the provided selector function and configuration.
187
+ */
188
+ <Derivate>(selector: (state: State) => Derivate, config?: UseHookOptions<Derivate, State>): Readonly<[state: Derivate, stateMutator: StateMutator, metadata: Metadata]>;
189
+ }
190
+ /**
191
+ * @description Function to set the metadata value
192
+ * The metadata value is not reactive and wont trigger re-renders
193
+ */
194
+ export type MetadataSetter<Metadata extends BaseMetadata> = (setter: Metadata | ((metadata: Metadata) => Metadata)) => void;
195
+ /**
196
+ * @description Represents the changes in the state
197
+ */
198
+ export type StateChanges<State> = {
199
+ state: State;
200
+ previousState: State | undefined;
201
+ identifier: string | undefined;
202
+ };
203
+ /**
204
+ * API for the actions of the global states
205
+ **/
206
+ export type StoreTools<State, StateMutator = React.Dispatch<React.SetStateAction<State>>, Metadata extends BaseMetadata = BaseMetadata> = {
207
+ /**
208
+ * The actions available for the global state if provided
209
+ */
210
+ actions: StateMutator extends AnyFunction ? null : StateMutator;
211
+ /**
212
+ * @description Metadata associated with the global state.
213
+ * Metadata is non-reactive; reading it will not trigger re-renders. To change it use `setMetadata`.
214
+ */
215
+ readonly metadata: Metadata;
216
+ /**
217
+ * @deprecated Use the `metadata` property instead, e.g. `const { metadata } = storeTools;`. Metadata is stable, so it is exposed directly.
218
+ * @description Metadata associated with the global state
219
+ */
220
+ getMetadata: () => Metadata;
221
+ /**
222
+ * @description Current state value
223
+ */
224
+ getState: () => State;
225
+ /**
226
+ * @description Sets the metadata value
227
+ */
228
+ setMetadata: MetadataSetter<Metadata>;
229
+ /**
230
+ * @description Function to set the state value
231
+ */
232
+ setState: (setter: React.SetStateAction<State>, args?: {
233
+ /**
234
+ * @description Force update even if the state value did not change, this is for advanced use cases only
235
+ */
236
+ forceUpdate?: boolean;
237
+ /**
238
+ * @description Optional identifier visible on the devtools
239
+ */
240
+ identifier?: string;
241
+ }) => void;
242
+ /**
243
+ * @description Subscribe to the state changes
244
+ * You can subscribe to the whole state or to a fragment of the state by passing a selector as first parameter,
245
+ * this can be used in non react environments to listen to the state changes
246
+ *
247
+ * @example
248
+ * ```ts
249
+ * const unsubscribe = storeTools.subscribe((state) => {
250
+ * console.log('State changed:', state);
251
+ * });
252
+ *
253
+ * // To unsubscribe later
254
+ * unsubscribe();
255
+ * ```
256
+ */
257
+ subscribe: SubscribeToState<State>;
258
+ };
259
+ /**
260
+ * contract for the storeActionsConfig configuration
261
+ */
262
+ export interface ActionCollectionConfig<State, Metadata extends BaseMetadata, ThisAPI = Record<string, (...parameters: Any[]) => unknown>> {
263
+ readonly [key: string]: {
264
+ (this: ThisAPI, ...parameters: Any[]): (this: ThisAPI, storeTools: StoreTools<State, Record<string, (...parameters: Any[]) => unknown | void>, Metadata>) => unknown | void;
265
+ };
266
+ }
267
+ /**
268
+ * @description Resulting type of the action collection configuration
269
+ */
270
+ export type ActionCollectionResult<State, Metadata extends BaseMetadata, ActionsConfig extends ActionCollectionConfig<State, Metadata>> = {
271
+ [key in keyof ActionsConfig]: {
272
+ (...params: Parameters<ActionsConfig[key]>): ReturnType<ReturnType<ActionsConfig[key]>>;
273
+ };
274
+ };
275
+ export type CleanupFunction = () => void;
276
+ /**
277
+ * Callbacks for the global store lifecycle events
278
+ */
279
+ export type GlobalStoreCallbacks<State, StateMutator, Metadata extends BaseMetadata> = {
280
+ /**
281
+ * @description Called when the store is initialized
282
+ */
283
+ onInit?: (args: StoreTools<State, StateMutator, Metadata>) => void | Promise<void> | CleanupFunction;
284
+ /**
285
+ * @description Called when the state has changed
286
+ */
287
+ onStateChanged?: (args: StoreTools<State, StateMutator, Metadata> & StateChanges<State>) => void;
288
+ /**
289
+ * @description Called when a new subscription is created
290
+ */
291
+ onSubscribed?: (args: StoreTools<State, StateMutator, Metadata>, subscription: SubscriberParameters) => void;
292
+ /**
293
+ * @description Called to determine whether to prevent a state change
294
+ */
295
+ computePreventStateChange?: (args: StoreTools<State, StateMutator, Metadata> & StateChanges<State>) => boolean;
296
+ /**
297
+ * @description Called when the store is unmounted, only applicable in context stores
298
+ */
299
+ onUnMount?: (store: StoreTools<State, StateMutator, Metadata>) => void;
300
+ };
301
+ /**
302
+ * @description Configuration options for the use hook
303
+ */
304
+ export type UseHookOptions<State, TRoot = Any> = {
305
+ isEqual?: (current: State, next: State) => boolean;
306
+ isEqualRoot?: (current: TRoot, next: TRoot) => boolean;
307
+ dependencies?: unknown[];
308
+ };
309
+ /**
310
+ * @description Callback function to unsubscribe from the store changes
311
+ */
312
+ export type UnsubscribeCallback = () => void;
313
+ /**
314
+ * @description Configuration for the subscribe callback
315
+ */
316
+ export type SubscribeCallbackConfig<State> = UseHookOptions<State> & {
317
+ /**
318
+ * By default the callback is executed immediately after the subscription
319
+ */
320
+ skipFirst?: boolean;
321
+ };
322
+ /**
323
+ * Callback function to subscribe to the store changes
324
+ */
325
+ export type SubscribeCallback<State> = (state: State) => void;
326
+ /**
327
+ * @description Subscribe to the state changes
328
+ * You can subscribe to the whole state or to a fragment of the state by passing a selector as first parameter
329
+ * This can be used in non react environments to listen to the state changes
330
+ */
331
+ export type SubscribeToState<State> = {
332
+ /**
333
+ * @description Subscribe to the whole state changes
334
+ *
335
+ * @example
336
+ * ```ts
337
+ * const unsubscribe = store.subscribe((state) => {
338
+ * console.log('State changed:', state);
339
+ * });
340
+ *
341
+ * // To unsubscribe later
342
+ * unsubscribe();
343
+ * ```
344
+ */
345
+ (subscription: SubscribeCallback<State>, config?: SubscribeCallbackConfig<State>): UnsubscribeCallback;
346
+ /**
347
+ * @description Subscribe to a fragment of the state changes
348
+ *
349
+ * @example
350
+ * ```ts
351
+ * const unsubscribe = store.subscribe(
352
+ * (fragment) => {
353
+ * console.log('Fragment changed:', fragment);
354
+ * },
355
+ * (state) => {
356
+ * console.log('Selected fragment changed:', state.someFragment);
357
+ * }
358
+ * );
359
+ *
360
+ * // To unsubscribe later
361
+ * unsubscribe();
362
+ * ```
363
+ */
364
+ <TDerivate>(selector: SelectorCallback<State, TDerivate>, subscription: SubscribeCallback<TDerivate>, config?: SubscribeCallbackConfig<TDerivate>): UnsubscribeCallback;
365
+ };
366
+ /**
367
+ * @description Metadata, non reactive additional information associated with the global state
368
+ */
369
+ export type BaseMetadata = Record<string, unknown>;
370
+ /**
371
+ * @description Function to get the metadata
372
+ */
373
+ export type MetadataGetter<Metadata extends BaseMetadata> = () => Metadata;
374
+ /**
375
+ * @description Selector function to derive a fragment of the state
376
+ */
377
+ export type SelectorCallback<State, TDerivate> = (state: State) => TDerivate;
378
+ /**
379
+ * @description Parameters for the store subscription
380
+ */
381
+ export type SubscriberParameters = {
382
+ selector: SelectorCallback<Any, Any> | undefined;
383
+ currentState: unknown;
384
+ /**
385
+ * @description notification callback
386
+ */
387
+ onStoreChange: SubscriptionCallback | (() => void);
388
+ } & UseHookOptions<Any> & SubscribeCallbackConfig<Any>;
389
+ /**
390
+ * @description
391
+ * This is the final listener of the store changes, it can be a subscription or a setState
392
+ * @param {unknown} params - The parameters of the subscription
393
+ * @param {unknown} params.state - The new state
394
+ * @param {string} params.identifier - Optional identifier for the setState call
395
+ */
396
+ export type SubscriptionCallback<State = unknown> = (params: {
397
+ state: State;
398
+ }, args: {
399
+ identifier?: string;
400
+ }) => void;
401
+ export type GlobalStoreContextCallbacks<State, StateMutator, Metadata extends BaseMetadata> = {
402
+ /**
403
+ * @description Optional callback invoked after the context is created,
404
+ */
405
+ onCreated?: (
406
+ /**
407
+ * @description Full context instance
408
+ */
409
+ storeTools: ContextStoreTools<State, StateMutator extends AnyFunction ? null : StateMutator, Metadata>,
410
+ /**
411
+ * @description Underlying store instance
412
+ */
413
+ store: GlobalStore<State, Metadata, unknown, Any>) => void;
414
+ /**
415
+ * @description Called when the context provider is mounted
416
+ */
417
+ onMounted?: (
418
+ /**
419
+ * @description Full context instance
420
+ */
421
+ storeTools: ContextStoreTools<State, StateMutator extends AnyFunction ? null : StateMutator, Metadata>,
422
+ /**
423
+ * @description Underlying store instance
424
+ */
425
+ store: GlobalStore<State, Metadata, unknown, Any>) => void | UnsubscribeCallback;
426
+ /**
427
+ * @description Called synchronously during every Provider render.
428
+ *
429
+ * Must be idempotent and must not produce external side effects
430
+ * or retain references to the provided store.
431
+ *
432
+ * React may invoke renders that are later discarded.
433
+ */
434
+ onRender?: (
435
+ /**
436
+ * @description Full context instance
437
+ */
438
+ storeTools: ContextStoreTools<State, StateMutator extends AnyFunction ? null : StateMutator, Metadata>,
439
+ /**
440
+ * @description Underlying store instance
441
+ */
442
+ store: GlobalStore<State, Metadata, unknown, Any>) => void;
443
+ };
444
+ export type ContextStoreCallbacks<State, StateMutator, Metadata extends BaseMetadata> = GlobalStoreCallbacks<State, StateMutator, Metadata> & GlobalStoreContextCallbacks<State, StateMutator, Metadata>;
445
+ /**
446
+ * @description Resulting type of the action collection configuration
447
+ */
448
+ export type ContextActionCollectionResult<State, Metadata extends BaseMetadata, ActionsConfig extends ContextActionCollectionConfig<State, Metadata>> = {
449
+ [key in keyof ActionsConfig]: {
450
+ (...params: Parameters<ActionsConfig[key]>): ReturnType<ReturnType<ActionsConfig[key]>>;
451
+ };
452
+ };
453
+ /**
454
+ * contract for the storeActionsConfig configuration
455
+ */
456
+ export interface ContextActionCollectionConfig<State, Metadata extends BaseMetadata, ThisAPI = Record<string, (...parameters: Any[]) => unknown>> {
457
+ readonly [key: string]: {
458
+ (this: ThisAPI, ...parameters: Any[]): (this: ThisAPI, storeTools: ContextStoreTools<State, Record<string, (...parameters: Any[]) => unknown | void>, Metadata>) => unknown | void;
459
+ };
460
+ }
461
+ /**
462
+ * @description Extensions methods for the context store tools
463
+ */
464
+ export type ContextStoreToolsExtensions<State, StateMutator, Metadata extends BaseMetadata> = {
465
+ /**
466
+ * @description Main hook of the context
467
+ *
468
+ * @example
469
+ * ```tsx
470
+ * type CounterContext = import('../../stores/counter').CounterContext;
471
+ *
472
+ * const useLogCount = () => {
473
+ * return ({ use }: CounterContext) => {
474
+ * const count = use.select(s => s.count);
475
+ *
476
+ * console.log('Count changed:', count);
477
+ * };
478
+ * }
479
+ *
480
+ * // Usage in store
481
+ * import useLogCount from './actions/useLogCount';
482
+ *
483
+ * const counter = createContext({ count: 0 }, {
484
+ * actions: {
485
+ * useLogCount,
486
+ * }
487
+ * });
488
+ *
489
+ * // Usage in component
490
+ * import counter from '../stores/counter';
491
+ *
492
+ * const CounterLogger = () => {
493
+ * // access to the actions is NOT-REACTIVE
494
+ * const { useLogCount } = counter.use.actions();
495
+ *
496
+ * useLogCount();
497
+ * }
498
+ * ```
499
+ */
500
+ use: ContextHook<State, StateMutator, Metadata>;
501
+ };
502
+ /**
503
+ * @description Store tools specialized for context usage
504
+ */
505
+ export type ContextStoreTools<State, StateMutator, Metadata extends BaseMetadata> = StoreTools<State, StateMutator, Metadata> & ContextStoreToolsExtensions<State, StateMutator, Metadata>;
506
+ /**
507
+ * @description Extensions methods for the ContextProvider component
508
+ */
509
+ export type ContextProviderExtensions<State, StateMutator, Metadata extends BaseMetadata> = {
510
+ /**
511
+ * Creates a provider wrapper which allows to capture the context value,
512
+ * useful for testing purposes.
513
+ * @param options configuration options for the provider wrapper
514
+ * @param options.value optional initial state or initializer function
515
+ * @param options.onCreated optional callback invoked after the context is created
516
+ * @returns an object containing the wrapper component and a reference to the context value
517
+ */
518
+ makeProviderWrapper: (options?: {
519
+ /**
520
+ * @description Optional initial state or initializer function, useful for testing, storybooks, etc.
521
+ */
522
+ value?: State | ((initialValue: State) => State);
523
+ } & GlobalStoreContextCallbacks<State, StateMutator extends AnyFunction ? null : StateMutator, Metadata>) => {
524
+ /**
525
+ * Provider for the context
526
+ */
527
+ wrapper: React.FC<PropsWithChildren<{
528
+ /**
529
+ * @description Optional initial state or initializer function, useful for testing, storybooks, etc.
530
+ */
531
+ value?: State | ((initialValue: State) => State);
532
+ }>>;
533
+ /**
534
+ * Reference to the current context value
535
+ */
536
+ context: {
537
+ /**
538
+ * @description Current context value
539
+ */
540
+ current: ContextStoreTools<State, StateMutator extends AnyFunction ? null : StateMutator, Metadata>;
541
+ /**
542
+ * @description Underlying store instance
543
+ */
544
+ instance: GlobalStore<State, Metadata, unknown, Any>;
545
+ };
546
+ };
547
+ };
548
+ /**
549
+ * @description Creates a React context provider component for the given global state.
550
+ * @param value Optional initial state or initializer function, useful for testing.
551
+ * @param onCreated Optional callback invoked after the context is created, receiving the full context instance.
552
+ */
553
+ export type ContextProvider<State, StateMutator, Metadata extends BaseMetadata> = React.FC<PropsWithChildren<{
554
+ /**
555
+ * @description Optional initial state or initializer function, useful for testing, storybooks, etc.
556
+ */
557
+ value?: State | ((initialValue: State) => State);
558
+ } & GlobalStoreContextCallbacks<State, StateMutator extends AnyFunction ? null : StateMutator, Metadata>>> & ContextProviderExtensions<State, StateMutator, Metadata>;
559
+ /**
560
+ * @description Represents a hook that returns a readonly state.
561
+ */
562
+ export interface ReadonlyContextHook<State, StateMutator, Metadata extends BaseMetadata> extends ReadonlyContextPublicApi<State, StateMutator, Metadata> {
563
+ /**
564
+ * @description Returns the full state.
565
+ */
566
+ (): State;
567
+ /**
568
+ * @description Returns a derived value from the state using the provided selector function.
569
+ * @param selector A function that selects a part of the state.
570
+ * @param dependencies Optional array of dependencies to control when the selector is re-evaluated.
571
+ * @returns The derived value from the state.
572
+ */
573
+ <Derivate>(selector: (state: State) => Derivate, dependencies?: unknown[]): Derivate;
574
+ /**
575
+ * @description Returns a derived value from the state using the provided selector function.
576
+ * @param selector A function that selects a part of the state.
577
+ * @param options Optional configuration for the selector hook.
578
+ * @param options.isEqual Optional equality function to compare the selected fragment.
579
+ * @param options.isEqualRoot Optional equality function to compare the root state.
580
+ * @param options.dependencies Optional array of dependencies to control when the selector is re-evaluated.
581
+ * @returns The derived value from the state.
582
+ */
583
+ <Derivate>(selector: (state: State) => Derivate, options?: UseHookOptions<Derivate, State>): Derivate;
584
+ }
585
+ /**
586
+ * @description Hook for accessing a context's state, mutator (setState or actions), and metadata.
587
+ * @returns A read-only tuple containing:
588
+ * - state: the current state, or the derived value when a selector is used
589
+ * - stateMutator: a function or actions collection to update the state
590
+ * - metadata: the current context metadata
591
+ *
592
+ * @example
593
+ * ```tsx
594
+ * // Simple usage (full state)
595
+ * const [state, setState] = useTodosContext();
596
+ *
597
+ * // With a selector (preferred for render isolation)
598
+ * const [todos, actions] = useTodosContext(s => s.todos);
599
+ *
600
+ * actions.setTodos(next);
601
+ * ```
602
+ */
603
+ export interface ContextHook<State, StateMutator, Metadata extends BaseMetadata> extends ContextPublicApi<State, StateMutator, Metadata> {
604
+ /**
605
+ * @description Retrieves the full state, state mutator (setState or actions), and metadata.
606
+ */
607
+ (): Readonly<[state: State, stateMutator: StateMutator, metadata: Metadata]>;
608
+ /**
609
+ * @description Retrieves a derived value from the state using the provided selector function.
610
+ * @param selector A function that selects a part of the state.
611
+ * @param dependencies Optional array of dependencies to control when the selector is re-evaluated.
612
+ * @returns A read-only tuple containing the derived state, state mutator (setState or actions), and metadata.
613
+ */
614
+ <Derivate>(selector: (state: State) => Derivate, dependencies?: unknown[]): Readonly<[state: Derivate, stateMutator: StateMutator, metadata: Metadata]>;
615
+ /**
616
+ * @description Retrieves a derived value from the state using the provided selector function.
617
+ * @param selector A function that selects a part of the state.
618
+ * @param dependencies Optional array of dependencies to control when the selector is re-evaluated.
619
+ * @returns A read-only tuple containing the derived state, state mutator (setState or actions), and metadata.
620
+ */
621
+ <Derivate>(selector: (state: State) => Derivate, config?: UseHookOptions<Derivate, State>): Readonly<[state: Derivate, stateMutator: StateMutator, metadata: Metadata]>;
622
+ }
623
+ export type ContextPublicApi<State, StateMutator, Metadata extends BaseMetadata> = {
624
+ /**
625
+ * @description [NOT A HOOK, NON-REACTIVE]
626
+ * Creates a derived hook that subscribes to a selected fragment of the context state.
627
+ * The selector determines which portion of the state the new hook exposes.
628
+ * This hook must be used within the corresponding context provider.
629
+ *
630
+ * @param selector A function that selects a part of the state.
631
+ * @param args Optional configuration for the derived hook, including:
632
+ * - isEqual: A function to compare the current and next selected fragment for equality.
633
+ * - isEqualRoot: A function to compare the entire state for equality.
634
+ * - name: An optional name for debugging purposes.
635
+ * @returns A new context hook that provides access to the selected fragment of the state,
636
+ * along with the state mutator and metadata.
637
+ *
638
+ * @example
639
+ * ```tsx
640
+ * const useTodos = createContext({
641
+ * todos: [],
642
+ * filter: '',
643
+ * }, {
644
+ * actions: {
645
+ * setFilter(filter: string) {
646
+ * ...
647
+ * });
648
+ *
649
+ * const useFilter = useTodos.createSelectorHook((state) => {
650
+ * return state.filter;
651
+ * });
652
+ *
653
+ * function FilterComponent() {
654
+ * // The selector only listen to the selected fragment (filter)
655
+ * // But has access to the full actions collection
656
+ * const [filter, { setFilter }] = useFilter();
657
+ *
658
+ * return (
659
+ * <input
660
+ * value={filter}
661
+ * onChange={(e) => setFilter(e.target.value)}
662
+ * />
663
+ * );
664
+ * }
665
+ * ```
666
+ */
667
+ createSelectorHook: <Derivate>(this: ReadonlyContextPublicApi<State, StateMutator, Metadata>, selector: (state: State) => Derivate, args?: Omit<UseHookOptions<Derivate, State>, 'dependencies'> & {
668
+ name?: string;
669
+ }) => ReadonlyContextHook<Derivate, StateMutator, Metadata>;
670
+ /**
671
+ * @description [NON-REACTIVE]
672
+ * Hook that provides non-reactive access to the context API.
673
+ * This allows direct interaction with the context’s state, metadata, and actions
674
+ * without triggering component re-renders.
675
+ * @returns An object containing the context API methods and properties.
676
+ */
677
+ api: () => StateApi<State, StateMutator, Metadata>;
678
+ /**
679
+ * @description [NON-REACTIVE]
680
+ * Provides direct access to the context's actions, if available.
681
+ */
682
+ actions: () => StateMutator extends AnyFunction ? null : StateMutator;
683
+ /**
684
+ * @description Selects a fragment of the state using the provided selector function.
685
+ */
686
+ select: SelectHook<State>;
687
+ /**
688
+ * @description
689
+ * Creates a hook that allows you to subscribe to a fragment of the state
690
+ * The observable selection will notify the subscribers only if the fragment changes and the equality function returns false
691
+ * you can customize the equality function by passing the isEqualRoot and isEqual parameters
692
+ *
693
+ * @example
694
+ * ```tsx
695
+ * const observable = store.use.observable(state => state.count);
696
+ *
697
+ * useEffect(() => {
698
+ * const unsubscribe = observable.subscribe(() => {
699
+ * // do something when the selected fragment changes
700
+ * });
701
+ *
702
+ * return () => {
703
+ * unsubscribe();
704
+ * };
705
+ * }, [observable]);
706
+ * ```
707
+ */
708
+ observable: <Selection>(selector: (state: State) => Selection, args?: {
709
+ isEqual?: (current: Selection, next: Selection) => boolean;
710
+ isEqualRoot?: (current: State, next: State) => boolean;
711
+ /**
712
+ * @description Name of the observable fragment for debugging purposes
713
+ */
714
+ name?: string;
715
+ }) => ObservableFragment<Selection, StateMutator, Metadata>;
716
+ /**
717
+ * @description display name for debugging purposes
718
+ */
719
+ readonly displayName: string;
720
+ };
721
+ /**
722
+ * @description Readonly version of the ContextPublicApi, expose by selectors and observables
723
+ */
724
+ export type ReadonlyContextPublicApi<State, StateMutator, Metadata extends BaseMetadata> = Pick<ContextPublicApi<State, StateMutator, Metadata>, 'createSelectorHook' | 'displayName'> & {
725
+ /**
726
+ * @description Hook that provides non-reactive access to the context API.
727
+ * This allows direct interaction with the context’s state, metadata, and actions
728
+ * without triggering component re-renders.
729
+ * @returns An object containing the context API methods and properties.
730
+ */
731
+ api: () => ReadonlyStateApi<State, StateMutator, Metadata>;
732
+ };
733
+ export interface CreateContext {
734
+ /**
735
+ * @description Creates a highly granular React context with its associated provider and state hook.
736
+ * @param value Initial state value or initializer function.
737
+ * @returns An object containing:
738
+ * - **`use`** — A custom hook to read and mutate the context state.
739
+ * Supports selectors for granular subscriptions and returns `[state, stateMutator, metadata]`.
740
+ * - **`Provider`** — A React component that provides the context to its descendants.
741
+ * It accepts an optional initial value and `onCreated` callback.
742
+ * - **`Context`** — The raw React `Context` object for advanced usage, such as integration with
743
+ * external tools or non-React consumers.
744
+ */
745
+ <State, StateMutator = React.Dispatch<React.SetStateAction<State>>>(value: State | (() => State)): {
746
+ /**
747
+ * @description Hook and API for interacting with the context.
748
+ * This hook provides access to the context state, actions, and metadata.
749
+ *
750
+ * There are two ways to use the hook
751
+ * @example
752
+ * The more simple and familiar way is to use it as a regular hook
753
+ *
754
+ * ```tsx
755
+ * const { Context, Provider, user: useUser} = createContext({ name: 'John', age: 30 });
756
+ *
757
+ * function UserProfile() {
758
+ * const [state, setState, metadata] = useUser();
759
+ *
760
+ * ....
761
+ * }
762
+ * ```
763
+ *
764
+ * @example
765
+ * The recommended, more sematic and easier to read way:
766
+ *
767
+ * ```tsx
768
+ * const user = createContext({ name: 'John', age: 30 });
769
+ *
770
+ * <user.Provider>
771
+ * <UserProfile />
772
+ * </user.Provider>
773
+ *
774
+ * function UserProfile() {
775
+ * const [state, setState, metadata] = user.use();
776
+ * const userContext = user.use.api();
777
+ * const userName = user.use.select(s => s.name);
778
+ * // ...
779
+ * }
780
+ */
781
+ use: ContextHook<State, StateMutator, BaseMetadata>;
782
+ /**
783
+ * @description Provider for the context
784
+ */
785
+ Provider: ContextProvider<State, StateMutator, BaseMetadata>;
786
+ /**
787
+ * @description The raw React Context object
788
+ */
789
+ Context: ReactContext<ContextHook<State, StateMutator, BaseMetadata> | null>;
790
+ };
791
+ /**
792
+ * @description Creates a highly granular React context with its associated provider and state hook.
793
+ * @param value Initial state value or initializer function.
794
+ * @param args Additional configuration for the context.
795
+ * @param args.name Optional name for debugging purposes.
796
+ * @param args.metadata Optional non-reactive metadata associated with the state.
797
+ * @param args.callbacks Optional lifecycle callbacks for the context.
798
+ * @param args.actions Optional actions to restrict state mutations [if provided `setState` will be nullified].
799
+ * @returns An object containing:
800
+ * - **`use`** — A custom hook to read and mutate the context state.
801
+ * Supports selectors for granular subscriptions and returns `[state, stateMutator, metadata]`.
802
+ * - **`Provider`** — A React component that provides the context to its descendants.
803
+ * It accepts an optional initial value and `onCreated` callback.
804
+ * - **`Context`** — The raw React `Context` object for advanced usage, such as integration with
805
+ * external tools or non-React consumers.
806
+ */
807
+ <State, Metadata extends BaseMetadata, ActionsConfig extends ContextActionCollectionConfig<State, Metadata> | null | {}, StateMutator = keyof ActionsConfig extends never | undefined ? React.Dispatch<React.SetStateAction<State>> : ContextActionCollectionResult<State, Metadata, NonNullable<ActionsConfig>>>(
808
+ /**
809
+ * @description Initial state value or initializer function.
810
+ */
811
+ value: State | (() => State),
812
+ /**
813
+ * @description Additional configuration for the context.
814
+ * @param args.name Optional name for debugging purposes.
815
+ * @param args.metadata Optional non-reactive metadata associated with the state.
816
+ * @param args.callbacks Optional lifecycle callbacks for the context.
817
+ * @param args.actions Optional actions to restrict state mutations [if provided `setState` will be nullified].
818
+ */
819
+ args: {
820
+ name?: string;
821
+ metadata?: Metadata | (() => Metadata);
822
+ callbacks?: ContextStoreCallbacks<Any, AnyActions, Any>;
823
+ actions?: ActionsConfig;
824
+ }): {
825
+ /**
826
+ * @description Hook and API for interacting with the context.
827
+ * This hook provides access to the context state, actions, and metadata.
828
+ *
829
+ * There are two ways to use the hook
830
+ * @example
831
+ * The more simple and familiar way is to use it as a regular hook
832
+ *
833
+ * ```tsx
834
+ * const { Context, Provider, user: useUser} = createContext({ name: 'John', age: 30 });
835
+ *
836
+ * function UserProfile() {
837
+ * const [state, setState, metadata] = useUser();
838
+ *
839
+ * ....
840
+ * }
841
+ * ```
842
+ *
843
+ * @example
844
+ * The recommended, more sematic and easier to read way:
845
+ *
846
+ * ```tsx
847
+ * const user = createContext({ name: 'John', age: 30 });
848
+ *
849
+ * <user.Provider>
850
+ * <UserProfile />
851
+ * </user.Provider>
852
+ *
853
+ * function UserProfile() {
854
+ * const [state, setState, metadata] = user.use();
855
+ * const userContext = user.use.api();
856
+ * const userName = user.use.select(s => s.name);
857
+ * // ...
858
+ * }
859
+ */
860
+ use: ContextHook<State, StateMutator, Metadata>;
861
+ /**
862
+ * @description Provider for the context
863
+ */
864
+ Provider: ContextProvider<State, StateMutator, Metadata>;
865
+ /**
866
+ * @description Raw React Context object
867
+ */
868
+ Context: ReactContext<ContextHook<State, StateMutator, Metadata> | null>;
869
+ };
870
+ /**
871
+ * @description Creates a highly granular React context with its associated provider and state hook.
872
+ * @param value Initial state value or initializer function.
873
+ * @param args Additional configuration for the context.
874
+ * @param args.name Optional name for debugging purposes.
875
+ * @param args.metadata Optional non-reactive metadata associated with the state.
876
+ * @param args.callbacks Optional lifecycle callbacks for the context.
877
+ * @param args.actions Optional actions to restrict state mutations [if provided `setState` will be nullified].
878
+ * @returns An object containing:
879
+ * - **`use`** — A custom hook to read and mutate the context state.
880
+ * Supports selectors for granular subscriptions and returns `[state, stateMutator, metadata]`.
881
+ * - **`Provider`** — A React component that provides the context to its descendants.
882
+ * It accepts an optional initial value and `onCreated` callback.
883
+ * - **`Context`** — The raw React `Context` object for advanced usage, such as integration with
884
+ * external tools or non-React consumers.
885
+ */
886
+ <State, Metadata extends BaseMetadata, ActionsConfig extends ContextActionCollectionConfig<State, Metadata>>(
887
+ /**
888
+ * @description Initial state value or initializer function.
889
+ */
890
+ value: State | (() => State),
891
+ /**
892
+ * @description Additional configuration for the context.
893
+ * @param args.name Optional name for debugging purposes.
894
+ * @param args.metadata Optional non-reactive metadata associated with the state.
895
+ * @param args.callbacks Optional lifecycle callbacks for the context.
896
+ * @param args.actions Optional actions to restrict state mutations [if provided `setState` will be nullified].
897
+ */
898
+ args: {
899
+ name?: string;
900
+ metadata?: Metadata | (() => Metadata);
901
+ callbacks?: ContextStoreCallbacks<Any, AnyActions, Any>;
902
+ actions: ActionsConfig;
903
+ }): {
904
+ /**
905
+ * @description Hook and API for interacting with the context.
906
+ * This hook provides access to the context state, actions, and metadata.
907
+ *
908
+ * There are two ways to use the hook
909
+ * @example
910
+ * The more simple and familiar way is to use it as a regular hook
911
+ *
912
+ * ```tsx
913
+ * const { Context, Provider, user: useUser} = createContext({ name: 'John', age: 30 });
914
+ *
915
+ * function UserProfile() {
916
+ * const [state, setState, metadata] = useUser();
917
+ *
918
+ * ....
919
+ * }
920
+ * ```
921
+ *
922
+ * @example
923
+ * The recommended, more sematic and easier to read way:
924
+ *
925
+ * ```tsx
926
+ * const user = createContext({ name: 'John', age: 30 });
927
+ *
928
+ * <user.Provider>
929
+ * <UserProfile />
930
+ * </user.Provider>
931
+ *
932
+ * function UserProfile() {
933
+ * const [state, setState, metadata] = user.use();
934
+ * const userContext = user.use.api();
935
+ * const userName = user.use.select(s => s.name);
936
+ * // ...
937
+ * }
938
+ */
939
+ use: ContextHook<State, ContextActionCollectionResult<State, Metadata, ActionsConfig>, Metadata>;
940
+ /**
941
+ * @description Provider for the context
942
+ */
943
+ Provider: ContextProvider<State, ContextActionCollectionResult<State, Metadata, ActionsConfig>, Metadata>;
944
+ /**
945
+ * @description Raw React Context object
946
+ */
947
+ Context: ReactContext<ContextHook<State, ContextActionCollectionResult<State, Metadata, ActionsConfig>, Metadata> | null>;
948
+ };
949
+ }
950
+ /**
951
+ * @description Infers the context API type
952
+ *
953
+ * @example
954
+ * ```ts
955
+ * const counter = createContext(0);
956
+ *
957
+ * type CounterContextApi = InferContextApi<typeof counter.Context>;
958
+ *
959
+ * // Equivalent to:
960
+ * ContextApi<number, React.Dispatch<React.SetStateAction<number>>, BaseMetadata>;
961
+ * ```
962
+ */
963
+ export type InferContextApi<Context extends ReactContext<ContextHook<Any, Any, Any> | null>> = NonNullable<React.ContextType<Context>> extends ContextHook<infer State, infer StateMutator, infer Metadata> ? ContextStoreTools<State, StateMutator extends AnyFunction ? null : StateMutator, Metadata> : never;
964
+ /**
965
+ * Typescript is unable to infer the actions type correctly for the lifecycle callbacks
966
+ * so we use a generic AnyActions type here to bypass that limitation.
967
+ *
968
+ * The parameter could still be typed before using it with
969
+ * ```ts
970
+ * type StoreTools = InferStateApi<typeof <hook>>;
971
+ *
972
+ * onInit: (tools) => {
973
+ * const storeTools = tools as StoreTools;
974
+ * // ...
975
+ * }
976
+ *
977
+ * or when dealing with context:
978
+ *
979
+ * type ContextApi = InferContextApi<typeof <context>>;
980
+ *
981
+ * onInit: (tools) => {
982
+ * const storeTools = tools as ContextApi;
983
+ * // ...
984
+ * }
985
+ * ```
986
+ */
987
+ export type AnyActions = Record<string, (...args: Any[]) => Any>;
988
+ export interface CreateGlobalState {
989
+ /**
990
+ * Creates a global state hook.
991
+ * @param state initial state value or a callback function that returns the initial state
992
+ * @returns a state hook for your components
993
+ *
994
+ * @example
995
+ * ```tsx
996
+ * const useCounter = createGlobalState(0);
997
+ *
998
+ * function Counter() {
999
+ * const [count, setCount] = useCounter();
1000
+ * return (
1001
+ * <div>
1002
+ * <p>Count: {count}</p>
1003
+ * <button onClick={() =>
1004
+ * setCount(prev => prev + 1)
1005
+ * }>Increment</button>
1006
+ * </div>
1007
+ * );
1008
+ * }
1009
+ * ```
1010
+ *
1011
+ * @example Using a callback to initialize state
1012
+ * ```tsx
1013
+ * const useCounter = createGlobalState(() => {
1014
+ * // Expensive computation or conditional logic
1015
+ * return localStorage.getItem('count') ? parseInt(localStorage.getItem('count')) : 0;
1016
+ * });
1017
+ * ```
1018
+ *
1019
+ * @example You can also use a more semantic and declarative approach
1020
+ * ```tsx
1021
+ * const counter = createGlobalState(0);
1022
+ *
1023
+ * function Counter() {
1024
+ * const [count, setCount] = counter.use();
1025
+ * const count = counter.use.select();
1026
+ *
1027
+ * counter.setState(prev => prev + 1);
1028
+ *
1029
+ * // if you have actions
1030
+ * counter.actions.someAction();
1031
+ * ```
1032
+ */
1033
+ <State>(state: State | (() => State)): StateHook<State, React.Dispatch<React.SetStateAction<State>>, BaseMetadata>;
1034
+ /**
1035
+ * Creates a global state hook that you can use across your application
1036
+ * @param state initial state value or a callback function that returns the initial state
1037
+ * @param args additional configuration for the global state
1038
+ * @param args.name optional name for debugging purposes
1039
+ * @param args.metadata optional non-reactive metadata associated with the state (can be a value or callback)
1040
+ * @param args.callbacks optional lifecycle callbacks for the global state
1041
+ * @param args.actions optional actions to restrict state mutations [if provided `setState` will be nullified]
1042
+ * @returns a state hook that you can use in your components
1043
+ *
1044
+ * @example
1045
+ * ```tsx
1046
+ * const useCounter = createGlobalState(0, {
1047
+ * actions: {
1048
+ * increase() {
1049
+ * return ({ setState }) => {
1050
+ * setState((c) => c + 1);
1051
+ * };
1052
+ * },
1053
+ * decrease(amount: number) {
1054
+ * return ({ setState }) => {
1055
+ * setState((c) => c - amount);
1056
+ * };
1057
+ * },
1058
+ * },
1059
+ * });
1060
+ *
1061
+ * function Counter() {
1062
+ * const [count, {
1063
+ * increase,
1064
+ * decrease
1065
+ * }] = useCounter();
1066
+ *
1067
+ * return (
1068
+ * <div>
1069
+ * <p>Count: {count}</p>
1070
+ * <button onClick={increase}>
1071
+ * Increment
1072
+ * </button>
1073
+ * <button onClick={() => {
1074
+ * decrease(1);
1075
+ * }}>
1076
+ * Decrement
1077
+ * </button>
1078
+ * </div>
1079
+ * );
1080
+ * }
1081
+ * ```
1082
+ *
1083
+ * @example Using callbacks for state and metadata initialization
1084
+ * ```tsx
1085
+ * const useAuth = createGlobalState(
1086
+ * () => ({ user: null, token: localStorage.getItem('token') }),
1087
+ * {
1088
+ * metadata: () => ({ createdAt: Date.now() }),
1089
+ * }
1090
+ * );
1091
+ * ```
1092
+ */
1093
+ <State, Metadata extends BaseMetadata, ActionsConfig extends ActionCollectionConfig<State, Metadata> | null | {}, PublicStateMutator = keyof ActionsConfig extends never | undefined ? React.Dispatch<React.SetStateAction<State>> : ActionCollectionResult<State, Metadata, NonNullable<ActionsConfig>>>(state: State | (() => State), args: {
1094
+ name?: string;
1095
+ metadata?: Metadata | (() => Metadata);
1096
+ callbacks?: GlobalStoreCallbacks<Any, AnyActions, Any>;
1097
+ actions?: ActionsConfig;
1098
+ }): StateHook<State, PublicStateMutator, Metadata>;
1099
+ /**
1100
+ * Creates a global state hook that you can use across your application
1101
+ * @param state initial state value or a callback function that returns the initial state
1102
+ * @param args additional configuration for the global state
1103
+ * @param args.name optional name for debugging purposes
1104
+ * @param args.metadata optional non-reactive metadata associated with the state (can be a value or callback)
1105
+ * @param args.callbacks optional lifecycle callbacks for the global state
1106
+ * @param args.actions optional actions to restrict state mutations [if provided `setState` will be nullified]
1107
+ * @returns a state hook that you can use in your components
1108
+ *
1109
+ * @example
1110
+ * ```tsx
1111
+ * const useCounter = createGlobalState(0, {
1112
+ * actions: {
1113
+ * increase() {
1114
+ * return ({ setState }) => {
1115
+ * setState((c) => c + 1);
1116
+ * };
1117
+ * },
1118
+ * decrease(amount: number) {
1119
+ * return ({ setState }) => {
1120
+ * setState((c) => c - amount);
1121
+ * };
1122
+ * },
1123
+ * },
1124
+ * });
1125
+ *
1126
+ * function Counter() {
1127
+ * const [count, {
1128
+ * increase,
1129
+ * decrease
1130
+ * }] = useCounter();
1131
+ *
1132
+ * return (
1133
+ * <div>
1134
+ * <p>Count: {count}</p>
1135
+ * <button onClick={increase}>
1136
+ * Increment
1137
+ * </button>
1138
+ * <button onClick={() => {
1139
+ * decrease(1);
1140
+ * }}>
1141
+ * Decrement
1142
+ * </button>
1143
+ * </div>
1144
+ * );
1145
+ * }
1146
+ * ```
1147
+ */
1148
+ <State, Metadata extends BaseMetadata, ActionsConfig extends ActionCollectionConfig<State, Metadata>, PublicStateMutator = ActionCollectionResult<State, Metadata, NonNullable<ActionsConfig>>>(state: State | (() => State), args: {
1149
+ name?: string;
1150
+ metadata?: Metadata | (() => Metadata);
1151
+ callbacks?: GlobalStoreCallbacks<Any, AnyActions, Any>;
1152
+ actions: ActionsConfig;
1153
+ }): StateHook<State, PublicStateMutator, Metadata>;
1154
+ }
1155
+ /**
1156
+ * Infers the actions type from a StateHook
1157
+ * @example
1158
+ * ```ts
1159
+ * type CounterActions = InferActionsType<typeof useCounter>;
1160
+ * ```
1161
+ */
1162
+ export type InferActionsType<Hook extends StateHook<Any, Any, Any>> = ReturnType<Hook>['1'];
1163
+ /**
1164
+ * Infers the StoreTools type from a StateHook, useful to split actions code
1165
+ *
1166
+ * @example
1167
+ * ```ts
1168
+ * type CounterStoreTools = InferStateApi<typeof useCounter>;
1169
+ * ```
1170
+ */
1171
+ export type InferStateApi<Hook extends StateHook<Any, Any, Any>> = Hook extends StateHook<infer State, infer PublicStateMutator, infer Metadata> ? StoreTools<State, PublicStateMutator, Metadata> : never;
1172
+ /**
1173
+ * Branded unique identifier
1174
+ */
1175
+ export type BrandedId<T extends string | undefined> = `${T extends string ? T : ''}${string}` & {
1176
+ __brand: T;
1177
+ };
1178
+ export interface UniqueId {
1179
+ /**
1180
+ * Generates a unique identifier string, optionally prefixed.
1181
+ *
1182
+ * @example
1183
+ * uniqueId(); // "k9j3n5x8q2"
1184
+ * type Id1 = `${string}` & { __brand: '' };
1185
+ */
1186
+ (): BrandedId<''>;
1187
+ /**
1188
+ * Generates a unique identifier string, optionally prefixed.
1189
+ *
1190
+ * @example
1191
+ * uniqueId('user:'); // "user:k9j3n5x8q2"
1192
+ * type Id2 = `user:${string}` & { __brand: 'user:' };
1193
+ */
1194
+ <T extends string>(prefix: T): BrandedId<T>;
1195
+ /**
1196
+ * Creates a reusable unique ID generator for a specific prefix.
1197
+ *
1198
+ * @example
1199
+ * const makeOrderId = uniqueId.for('order:');
1200
+ * const id = makeOrderId(); // "order:k9j3n5x8q2"
1201
+ * type OrderId = `order:${string}` & { __brand: 'order:' };
1202
+ */
1203
+ for<T extends string>(prefix: T): {
1204
+ (): `${T}${string}` & {
1205
+ __brand: T;
1206
+ };
1207
+ /**
1208
+ * Checks if the given value matches the branded ID for this prefix.
1209
+ */
1210
+ is(value: unknown): value is `${T}${string}` & {
1211
+ __brand: T;
1212
+ };
1213
+ /**
1214
+ * Asserts that the value matches this branded ID, throws otherwise.
1215
+ */
1216
+ assert(value: unknown): asserts value is `${T}${string}` & {
1217
+ __brand: T;
1218
+ };
1219
+ /**
1220
+ * Returns a strictly branded generator using a custom symbol brand.
1221
+ */
1222
+ strict<Brand extends symbol>(): {
1223
+ (): `${T}${string}` & {
1224
+ __brand: Brand;
1225
+ };
1226
+ is(value: unknown): value is `${T}${string}` & {
1227
+ __brand: Brand;
1228
+ };
1229
+ assert(value: unknown): asserts value is `${T}${string}` & {
1230
+ __brand: Brand;
1231
+ };
1232
+ };
1233
+ };
1234
+ /**
1235
+ * Creates a reusable unique ID generator without a prefix.
1236
+ */
1237
+ of<T extends string>(): () => string & {
1238
+ __brand: T;
1239
+ };
1240
+ /**
1241
+ * Creates a strictly branded unique ID generator without a prefix.
1242
+ */
1243
+ strict<Brand extends symbol>(): () => string & {
1244
+ __brand: Brand;
1245
+ };
1246
+ }
1247
+ /**
1248
+ * Infers the appropriate API of the store
1249
+ */
1250
+ export type InferAPI<T> = T extends React.Context<Any> ? InferContextApi<T> : T extends StateHook<Any, Any, Any> ? InferStateApi<T> : T extends {
1251
+ Context: React.Context<Any>;
1252
+ } ? InferContextApi<T['Context']> : never;
1253
+ export type DerivedActionsConfig<ParentApi extends StoreTools<Any, Any, Any>> = {
1254
+ readonly [key: string]: {
1255
+ (...parameters: Any[]): (storeTools: StoreTools<ReturnType<ParentApi['getState']>, ParentApi['actions'], ReturnType<ParentApi['getMetadata']>>) => Any;
1256
+ };
1257
+ };
1258
+ export type DerivedActionsBuilder<ParentApi extends StoreTools<Any, Any, Any>> = {
1259
+ /**
1260
+ * @description
1261
+ *
1262
+ */
1263
+ <ActionsConfig extends {
1264
+ readonly [key: string]: {
1265
+ (...parameters: Any[]): (storeTools: StoreTools<ReturnType<ParentApi['getState']>, ParentApi['actions'], ReturnType<ParentApi['getMetadata']>>) => Any;
1266
+ };
1267
+ }>(actions: ActionsConfig): (api: StoreTools<Any, Any, Any>) => ActionCollectionResult<ReturnType<ParentApi['getState']>, ReturnType<ParentApi['getMetadata']>, ActionsConfig>;
1268
+ };