orchestore 0.1.4 → 0.1.6

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 CHANGED
@@ -4,9 +4,22 @@
4
4
 
5
5
  OrcheStore is currently under active development and is not yet ready for production use.
6
6
 
7
- > 📅 **Planned First Stable Release:** **2026-06-30**
7
+ ### 📅 Future Release Plans
8
8
 
9
- > ⚠️ APIs, behavior, and internal implementation details may change without notice until the first stable release.
9
+ **Planned First Stable Release (v1.0.0):** **2026-06-30**
10
+
11
+ Development is ongoing, and progress is published regularly. New commits are pushed periodically to the public GitHub repository, and pre-release versions (v0.x.x) may be published to npm before the first stable release.
12
+
13
+ ### ⚠️ Pre-release Notice
14
+
15
+ OrcheStore is currently in a pre-release phase.
16
+
17
+ * Not recommended for production use
18
+ * APIs may change before the first stable release
19
+ * Internal architecture and runtime behavior are still evolving
20
+ * Documentation and feature coverage are actively being expanded
21
+
22
+ Stay tuned for updates.
10
23
 
11
24
  ---
12
25
 
@@ -22,6 +35,49 @@ The goal is simple:
22
35
 
23
36
  > ⚡ Spend less time wiring state management infrastructure and more time building application features.
24
37
 
38
+ ## Table of Contents
39
+
40
+ - [Introduction](#orchestore)
41
+ - [Core Principles](#core-principles)
42
+ - [Why OrcheStore?](#why-orchestore)
43
+ - [Redux Toolkit Comparison](#redux-toolkit-comparison)
44
+ - [Architecture Overview](#architecture-overview)
45
+
46
+ - [Quick Example](#quick-example)
47
+
48
+ - [Slice Layers](#slice-layers)
49
+ - [name](#name)
50
+ - [state](#state)
51
+ - [Mutations](#mutations)
52
+ - [Methods](#methods)
53
+ - [Computed State (Planned)](#computed-state-planned)
54
+ - [Nested Slices (Planned)](#nested-slices-planned)
55
+ - [Runtime Paths](#runtime-paths-planned)
56
+
57
+ - [State Access & Subscriptions](#state-access--subscriptions)
58
+ - [State Snapshots](#state-snapshots)
59
+ - [State Subscriptions](#state-subscriptions)
60
+ - [Draft State](#draft-state)
61
+
62
+ - [Store Integration](#store-integration)
63
+ - [Creating the Store](#creating-the-store)
64
+ - [Store Provider](#store-provider)
65
+ - [Accessing Slices through Store](#accessing-slices-through-store)
66
+ - [Accessing Store from Slices](#accessing-store-from-slices)
67
+ - [Root Store Type Extension (Planned)](#root-store-type-extension-planned)
68
+
69
+ - [Global Utilities](#global-utilities)
70
+ - [Accessing Global Utilities](#accessing-global-utilities)
71
+ - [Utilities Type Extension](#utilities-type-extension)
72
+ - [Providing Runtime Utilities](#providing-runtime-utilities)
73
+ - [Using Global Utilities in Slices](#using-global-utilities-in-slices)
74
+
75
+ - [TypeScript Inference](#typescript-inference)
76
+
77
+ - [Status](#status)
78
+
79
+ ---
80
+
25
81
  ## Core Principles
26
82
 
27
83
  - Simplify state management architecture
@@ -33,26 +89,1050 @@ The goal is simple:
33
89
  - Maintain strong TypeScript inference
34
90
  - Scale naturally through composition
35
91
 
92
+ ## Why OrcheStore?
93
+
94
+ Redux Toolkit significantly improves the Redux developer experience, but many applications still require developers to coordinate logic across multiple concepts:
95
+
96
+ - reducers
97
+ - action creators
98
+ - thunks
99
+ - selectors
100
+ - middleware
101
+ - hooks
102
+ - utility functions
103
+
104
+ As applications grow, state management often becomes less about solving business problems and more about connecting infrastructure.
105
+
106
+ OrcheStore reduces that coordination overhead by exposing state management through unified slice modules.
107
+
108
+ A slice is more than a state container. It is a runtime module that can encapsulate:
109
+
110
+ - state
111
+ - computed state
112
+ - mutations
113
+ - methods
114
+ - selectors
115
+ - child slices
116
+ - shared utilities
117
+
118
+ This allows state and application logic to evolve together within the same domain boundary.
119
+
120
+ Many common Redux patterns are automated by default:
121
+
122
+ | Traditional Redux Pattern | OrcheStore |
123
+ | ----------------------------- | ------------------------- |
124
+ | Action creators | Direct callable mutations |
125
+ | Thunks | Built-in methods |
126
+ | Dispatch calls | Direct function calls |
127
+ | `PayloadAction` wrappers | Native function arguments |
128
+ | Cross-slice imports | Root store access |
129
+ | Shared service wiring | Global utilities |
130
+ | Manual state tree composition | Nested slices |
131
+ | Complex type declarations | Automatic inference |
132
+
133
+ The result is a simpler architecture with fewer moving parts, less boilerplate, and a more direct development experience.
134
+
135
+ Developers can focus on application behavior rather than framework plumbing.
136
+
137
+ ## Redux Toolkit Comparison
138
+
139
+ OrcheStore builds on top of Redux Toolkit while providing a higher-level API for organizing state and behavior.
140
+
141
+ | Feature | OrcheStore | Redux Toolkit |
142
+ | ------------------------------ | ---------- | ------------- |
143
+ | Direct callable mutations | ✅ | ❌ |
144
+ | Multiple mutation arguments | ✅ | ❌ |
145
+ | Dispatch required | ❌ | ✅ |
146
+ | `PayloadAction` wrappers | ❌ | ✅ |
147
+ | Built-in orchestration methods | ✅ | ❌ |
148
+ | Nested slice composition | ⌛ Planned | ⚠️ Manual |
149
+ | Automatic path generation | ⌛ Planned | ⚠️ Manual |
150
+ | Global utilities | ✅ | ❌ |
151
+ | Unified slice API | ✅ | ❌ |
152
+ | Per-slice React hooks | ✅ | ❌ |
153
+ | Deep TypeScript inference | ✅ | ⚠️ Partial |
154
+
155
+ OrcheStore does not replace Redux Toolkit. Instead, it builds on top of it by automating common patterns and providing a more cohesive developer experience.
156
+
157
+ ## Architecture Overview
158
+
159
+ | Layer | Responsibility |
160
+ | ----------- | ------------------------------ |
161
+ | `name` | Unique slice identifier |
162
+ | `state` | Slice data storage definition |
163
+ | `mutations` | Synchronous state transitions |
164
+ | `methods` | Orchestration and side effects |
165
+ | `computed` | Derived and computed state |
166
+ | `children` | Nested slice composition |
167
+ | `getState` | Imperative state access |
168
+ | `useSelect` | Reactive state subscriptions |
169
+ | `getPath` | Hierarchical slice path |
170
+
36
171
  ---
37
172
 
38
- ## Release Status
173
+ # Quick Example
39
174
 
40
- ### Pre-release Notice
175
+ **Comparing OrcheStore with Redux Toolkit**
41
176
 
42
- OrcheStore is currently in a pre-release phase.
177
+ ## Slice Creation
43
178
 
44
- - Not recommended for production use
45
- - APIs may change before the first stable release
46
- - Internal architecture and runtime behavior are still evolving
47
- - Documentation and feature coverage are actively being expanded
179
+ ✔️ OrcheStore centrelize unified options API.
48
180
 
49
- ### Roadmap
181
+ ```tsx
182
+ import { createSlice } from "orchestore";
50
183
 
51
- | Milestone | Status |
52
- | -------------------- | -------------- |
53
- | Core Architecture | 🚧 In Progress |
54
- | Documentation | 🚧 In Progress |
55
- | Public Preview | 🚧 Planned |
56
- | First Stable Release | 📅 2026-06-30 |
184
+ export const counter = createSlice({
185
+ name: "counter",
57
186
 
58
- Stay tuned for updates.
187
+ state: {
188
+ value: 0,
189
+ },
190
+
191
+ mutations: {
192
+ // Direct arguments without PayloadAction wrappers
193
+ increment(state, amount: number) {
194
+ state.value += amount;
195
+ },
196
+
197
+ // Multiple typed arguments without payload objects
198
+ incrementLimited(state, amount: number, max = Infinity) {
199
+ state.value = Math.min(state.value + amount, max);
200
+ },
201
+ },
202
+
203
+ methods: {
204
+ // Reusable async method inside slice
205
+ sleep(delay: number) {
206
+ return new Promise((resolve) => setTimeout(resolve, delay));
207
+ },
208
+
209
+ // Orchestration layer with full slice access via `this`
210
+ async incrementAfter(amount: number, delay: number) {
211
+ await this.sleep(delay);
212
+ this.increment(amount);
213
+ },
214
+ },
215
+ });
216
+ ```
217
+
218
+ ⛔ Redux Toolkit splits logic between reducers, extra reducers, actions, and async workflows.
219
+
220
+ ```tsx
221
+ import { createSlice, createAsyncThunk, type PayloadAction } from "@reduxjs/toolkit";
222
+
223
+ export const counter = createSlice({
224
+ name: "counter",
225
+
226
+ initialState: {
227
+ value: 0,
228
+ },
229
+
230
+ reducers: {
231
+ // PayloadAction wrapper required
232
+ increment(state, action: PayloadAction<number>) {
233
+ state.value += action.payload;
234
+ },
235
+
236
+ // Multiple arguments must be wrapped into a payload object
237
+ incrementLimited(state, action: PayloadAction<{ amount: number; max?: number }>) {
238
+ state.value = Math.min(
239
+ state.value + action.payload.amount,
240
+ action.payload.max ?? Infinity
241
+ );
242
+ },
243
+ },
244
+ });
245
+
246
+ // Separate APIs for async workflows
247
+ export const incrementAfter = createAsyncThunk(
248
+ "counter/incrementAfter",
249
+ async (
250
+ { amount, delay }: { amount: number; delay: number },
251
+ { dispatch }
252
+ ) => {
253
+ await new Promise((resolve) => setTimeout(resolve, delay));
254
+
255
+ dispatch(counter.actions.increment(amount));
256
+ }
257
+ );
258
+ ```
259
+
260
+ ## Slice Usage
261
+
262
+ ✔️ OrcheStore exposes direct callable mutations and methods.
263
+
264
+ ```ts
265
+ counter.increment(4);
266
+ counter.incrementLimited(1, 50);
267
+ counter.incrementAfter(5, 1000);
268
+ ```
269
+
270
+ ⛔ Redux Toolkit follows dispatch-based execution
271
+
272
+ ```ts
273
+ import { useDispatch } from "react-redux";
274
+
275
+ const dispatch = useDispatch();
276
+
277
+ dispatch(counter.actions.increment(4));
278
+ dispatch(counter.actions.incrementLimited({ amount: 1, max: 50 }));
279
+ dispatch(incrementAfter({ amount: 5, delay: 1000 }));
280
+ ```
281
+
282
+ ## Store Integration & Context Providing
283
+
284
+ ✔️ OrcheStore exposes fully typed slice APIs directly.
285
+
286
+ ```ts
287
+ import { createStore } from "orchestore";
288
+ import { counter } from "./counterSlice";
289
+
290
+ export const store = createStore({
291
+ slices: {
292
+ counter,
293
+ },
294
+ });
295
+ ```
296
+
297
+ ```tsx
298
+ import { StoreProvider } from "orchestore";
299
+ import { store } from "./store";
300
+
301
+ export default function App() {
302
+ return (
303
+ <StoreProvider store={store}>
304
+ <CounterComponent />
305
+ </StoreProvider>
306
+ );
307
+ }
308
+ ```
309
+
310
+ ⛔ Redux Toolkit requires manual store configuration and type wiring.
311
+
312
+ ```ts
313
+ import { configureStore } from "@reduxjs/toolkit";
314
+ import { counter } from "./counterSlice";
315
+ import { useDispatch, useSelector } from "react-redux";
316
+
317
+ export const store = configureStore({
318
+ reducer: {
319
+ counter: counter.reducer,
320
+ },
321
+ });
322
+
323
+ export type RootState = ReturnType<typeof store.getState>;
324
+ export type AppDispatch = typeof store.dispatch;
325
+
326
+ export const useAppDispatch = useDispatch.withTypes<AppDispatch>();
327
+ export const useAppSelector = useSelector.withTypes<RootState>();
328
+ ```
329
+
330
+ ```tsx
331
+ import { Provider } from "react-redux";
332
+ import { store } from "./store/index";
333
+
334
+ export default function App() {
335
+ return (
336
+ <Provider store={store}>
337
+ <CounterComponent />
338
+ </Provider>
339
+ );
340
+ }
341
+ ```
342
+
343
+ ## React Usage
344
+
345
+ OrcheStore exposes direct usable child slices through a unified store instance.
346
+
347
+ ```tsx
348
+ // store.counter is equivalent to counter, import only one and use it
349
+ import { store } from "./store/index";
350
+ import { counter } from "./store/counterSlice";
351
+
352
+ export function CounterComponent() {
353
+ const value = counter.useSelect((state) => state.value);
354
+ const alias = store.counter.useSelect((state) => state.value);
355
+
356
+ return (
357
+ <>
358
+ <div>Counter: {value}</div>
359
+
360
+ <button onClick={() => counter.increment(1)}>Increment</button>
361
+
362
+ <button onClick={() => store.counter.incrementAfter(1, 1000)}>Increment Later</button>
363
+ </>
364
+ );
365
+ }
366
+ ```
367
+
368
+ ---
369
+
370
+ # Slice Layers
371
+
372
+ Slices are created using `createSlice`.
373
+
374
+ ```ts
375
+ import { createSlice } from "orchestore";
376
+
377
+ const counter = createSlice({
378
+ name: "counter",
379
+
380
+ state: {},
381
+
382
+ computed: {}, // Planned
383
+
384
+ mutations: {},
385
+
386
+ methods: {},
387
+
388
+ children: {}, // Planned
389
+
390
+ subscribe: {}, // Planned
391
+ });
392
+ ```
393
+
394
+ ## name
395
+
396
+ Every slice requires a unique and stable name.
397
+
398
+ The name primarily exists to ensure slice uniqueness and path generation.
399
+
400
+ ```ts
401
+ const counter = createSlice({
402
+ name: "counter",
403
+ });
404
+ ```
405
+
406
+ The name is exposed at runtime:
407
+
408
+ ```ts
409
+ console.log(counter.name); // "counter"
410
+ ```
411
+
412
+ It is also accessible inside methods:
413
+
414
+ ```ts
415
+ methods: {
416
+ logName() {
417
+ console.log(this.name);
418
+ }
419
+ }
420
+ ```
421
+
422
+ **Rules:**
423
+
424
+ - Slice names should not contain `"."` or `"/"`, because dots are reserved for nested slice paths
425
+ - Two slices cannot share the same name
426
+ - Registering the same slice instance multiple times with the same name is not allowed
427
+
428
+ ---
429
+
430
+ ## state
431
+
432
+ The `state` property defines the initial slice state.
433
+
434
+ ```ts
435
+ const counter = createSlice({
436
+ name: "counter",
437
+
438
+ state: {
439
+ value: 0,
440
+ },
441
+ });
442
+ ```
443
+
444
+ State can also be initialized lazily by providing a function.
445
+
446
+ The initializer runs only once before the first state access.
447
+
448
+ ```ts
449
+ const counter = createSlice({
450
+ name: "counter",
451
+
452
+ state: () => ({
453
+ value: 0,
454
+ }),
455
+ });
456
+ ```
457
+
458
+ **Useful for:**
459
+
460
+ - expensive initialization
461
+ - persisted state restoration
462
+ - runtime-dependent values
463
+
464
+ ---
465
+
466
+ ## Mutations
467
+
468
+ Mutations are synchronous state transition functions.
469
+
470
+ **Characteristics:**
471
+
472
+ - receive mutable draft state as the first parameter
473
+ - support multiple user-defined arguments
474
+ - directly callable from the exposed slices or store
475
+
476
+ **Responsibilities:**
477
+
478
+ Mutations should contain:
479
+
480
+ - synchronous state updates
481
+ - deterministic state transitions
482
+ - normalization logic
483
+
484
+ For anything else, use methods instead.
485
+
486
+ **Example:**
487
+
488
+ ```ts
489
+ const counter = createSlice({
490
+ name: "counter",
491
+
492
+ state: {
493
+ value: 0,
494
+ },
495
+
496
+ mutations: {
497
+ increment(state, amount = 1, max = Infinity) {
498
+ state.value = Math.min(state.value + amount, max);
499
+ },
500
+ },
501
+ });
502
+
503
+ counter.increment(1, 50);
504
+ ```
505
+
506
+ ---
507
+
508
+ ## Methods
509
+
510
+ Methods are the orchestration layer of a slice.
511
+
512
+ **Characteristics:**
513
+
514
+ - receive any number of arguments
515
+ - can return synchronous values or Promises
516
+ - can access:
517
+ - state, mutations, slibling methods, nested slices (through `this`)
518
+ - Application-wide utilities (`this.global`)
519
+
520
+ Methods are not serialized, replayable, or represented in Redux DevTools action history.
521
+
522
+ **Responsibilities:**
523
+
524
+ Methods are designed for centralizing any slice-related logic:
525
+
526
+ - asynchronous workflows
527
+ - business logic orchestration
528
+ - API communication and network calls
529
+ - timers, delayed or scheduled executions, such as `setTimeout` or event listeners
530
+ - side effects such as `localStorage`, `sessionStorage` and DOM manipulation
531
+ - Slice-related React hooks such as `slice.useSelect`, tanstack `useQuery` and `useMutation`
532
+
533
+ **Restrictions:**
534
+
535
+ Methods should NOT:
536
+
537
+ - include slice-unrelated logic.
538
+ - mutate state directly. use mutations instead.
539
+
540
+ Prefer:
541
+
542
+ ```ts
543
+ this.increment(1);
544
+ ```
545
+
546
+ Instead of:
547
+
548
+ ```ts
549
+ this.getState().value++;
550
+ ```
551
+
552
+ **Example:**
553
+
554
+ ```ts
555
+ const counter = createSlice({
556
+ name: "counter",
557
+
558
+ state: {
559
+ value: 0,
560
+ },
561
+
562
+ mutations: {
563
+ increment(state, amount: number) {
564
+ state.value += amount;
565
+ },
566
+ },
567
+
568
+ methods: {
569
+ async incrementAfter(amount: number, delay = 1000) {
570
+ await new Promise((resolve) => setTimeout(resolve, delay));
571
+ this.increment(amount);
572
+ this.global.logger.info("Counter incremented");
573
+ },
574
+ },
575
+ });
576
+ ```
577
+
578
+ ---
579
+
580
+ ## Computed State (Planned)
581
+
582
+ This currently not supported
583
+
584
+ ~~The `computed` property provides reusable derived state.~~
585
+
586
+ ```ts
587
+ // const counter = createSlice({
588
+ // name: "counter",
589
+
590
+ // state: {
591
+ // value: 10,
592
+ // },
593
+
594
+ // computed: {
595
+ // doubled(state) {
596
+ // return state.value * 2;
597
+ // },
598
+
599
+ // multiplied(state, amount: number) {
600
+ // return state.value * amount;
601
+ // },
602
+ // },
603
+ // });
604
+ ```
605
+
606
+ ---
607
+
608
+ ## Nested Slices (Planned)
609
+
610
+ This currently not supported
611
+
612
+ ~~Slices can be composed by registering other slices through the `children` property.~~
613
+
614
+ ~~This allows related state and behavior to be organized into a hierarchical structure while preserving full type inference.~~
615
+
616
+ ```ts
617
+ // import { counter } from "./counterSlice";
618
+ // import { users } from "./usersSlice";
619
+
620
+ // export const app = createSlice({
621
+ // name: "app",
622
+
623
+ // state: {},
624
+
625
+ // children: {
626
+ // counter,
627
+ // users,
628
+ // },
629
+ // });
630
+ ```
631
+
632
+ **~~Rules:~~**
633
+
634
+ - ~~A slice instance can only be registered once within the same state tree~~
635
+ - ~~Existing slice instances cannot be wrapped again with `createSlice`~~
636
+ - ~~Child slices must be registered through the `children` property~~
637
+
638
+ **~~Accessing Child Slices:~~**
639
+
640
+ ~~Child slices are exposed directly on their parent slice.~~
641
+
642
+ ```ts
643
+ // app.counter.increment(1);
644
+
645
+ // console.log(app.counter.getState());
646
+ ```
647
+
648
+ ~~Deeply nested slice hierarchies are fully supported.~~
649
+
650
+ ```ts
651
+ // admin.users.permissions.grant(...);
652
+
653
+ // console.log(admin.users.permissions.getState());
654
+ ```
655
+
656
+ ### Runtime Paths (Planned)
657
+
658
+ ~~Every slice exposes a runtime path through `getPath`.~~
659
+
660
+ ```ts
661
+ // store.counter.name; // "counter"
662
+ // store.counter.getPath(); // "counter"
663
+ ```
664
+
665
+ ~~Nested slices automatically inherit their parent path.~~
666
+
667
+ ```ts
668
+ // store.admin.users.name; // "users"
669
+ // store.admin.users.getPath(); // "admin.users"
670
+
671
+ // store.admin.users.permissions.name; // "permissions"
672
+ // store.admin.users.permissions.getPath(); // "admin.users.permissions"
673
+ ```
674
+
675
+ **~~Notes:~~**
676
+
677
+ ~~Paths are generated automatically from the slice hierarchy.~~
678
+
679
+ ~~OrcheStore builds on the same concepts as Redux Toolkit's `reducerPath` and `combinedReducers`, but automates path generation, reducer registration, and nested slice composition.~~
680
+
681
+ ~~No manual path configuration or reducer injection is required.~~
682
+
683
+ ---
684
+
685
+ # State Access & Subscriptions
686
+
687
+ OrcheStore provides multiple ways to access state depending on the context.
688
+
689
+ | API | Purpose |
690
+ | ------------------- | -------------------------------------------------------- |
691
+ | `slice.getState()` | Read the current state snapshot |
692
+ | `slice.useSelect()` | Subscribe to state changes inside React |
693
+ | Draft state | Temporary state access available during state evaluation |
694
+
695
+ ## State Snapshots
696
+
697
+ `getState()` returns the latest immutable state snapshot.
698
+
699
+ Use it whenever you need to read state imperatively outside of React subscriptions.
700
+
701
+ ```ts
702
+ counter.getState().value;
703
+ ```
704
+
705
+ **Available:**
706
+
707
+ - outside React
708
+ - inside methods
709
+
710
+ **Notes:**
711
+
712
+ Each call returns the current state at the moment it is executed.
713
+
714
+ Previously captured snapshots are not updated after future mutations.
715
+
716
+ ```ts
717
+ methods: {
718
+ logValue() {
719
+ this.changeValue("John");
720
+
721
+ const snapshot = this.getState();
722
+
723
+ this.changeValue("Alice");
724
+
725
+ console.log(snapshot.value); // "John"
726
+ console.log(this.getState().value); // "Alice"
727
+ }
728
+ }
729
+ ```
730
+
731
+ ---
732
+
733
+ ## State Subscriptions
734
+
735
+ `useSelect()` provides reactive state subscriptions for React components.
736
+
737
+ Internally, it is powered by Redux Toolkit's `useSelector`, while exposing a fully typed, slice-scoped API.
738
+
739
+ **Available:**
740
+
741
+ - inside React components
742
+ - inside slice methods that serve as custom React hooks
743
+
744
+ **Notes:**
745
+
746
+ Components automatically re-render when the selected value changes.
747
+
748
+ ```tsx
749
+ const value = counter.useSelect((state) => state.value);
750
+ ```
751
+
752
+ Selectors can also be accessed through the store to access multiple slices.
753
+
754
+ ```tsx
755
+ const canEdit = store.useSelect((state) => {
756
+ return (
757
+ state.auth.isAuthenticated &&
758
+ state.users.permissions.list.includes("edit_user")
759
+ );
760
+ });
761
+ ```
762
+
763
+ ---
764
+
765
+ ## Draft State
766
+
767
+ Draft state provides temporary access to slice state during state evaluation.
768
+
769
+ It is context-dependent and only available within specific APIs:
770
+
771
+ - Inside Mutations
772
+
773
+ Mutations receive a mutable draft state that can be updated directly.
774
+
775
+ ```ts
776
+ mutations: {
777
+ setName(state, name: string) {
778
+ state.name = name;
779
+ }
780
+ }
781
+ ```
782
+
783
+ ~~- Inside Computed State~~
784
+
785
+ ~~Computed functions receive a read-only draft state extended with additional runtime helpers.~~
786
+
787
+ ```ts
788
+ // computed: {
789
+ // fullName(state) {
790
+ // return `${state.firstName} ${state.lastName}`;
791
+ // }
792
+ // }
793
+ ```
794
+
795
+ - Inside useSelect
796
+
797
+ Selectors receive a read-only draft state extended ~~with additional runtime helpers~~.
798
+
799
+ ```tsx
800
+ const displayName = users.useSelect((state) => {
801
+ return state.user.name;
802
+ });
803
+ ```
804
+
805
+ **~~Available additions include:~~**
806
+
807
+ ~~- `state.computed` — computed state access~~
808
+
809
+ ---
810
+
811
+ # Store Integration
812
+
813
+ ## Creating the Store
814
+
815
+ Create the root store using `createStore`.
816
+
817
+ ```ts
818
+ import { createStore } from "orchestore";
819
+ import { counter } from "./counterSlice";
820
+ import { users } from "./usersSlice";
821
+
822
+ export const store = createStore({
823
+ slices: {
824
+ counter,
825
+ users,
826
+ },
827
+ });
828
+ ```
829
+
830
+ ## Store Provider
831
+
832
+ Wrapping the application component inside this provider is required.
833
+
834
+ ```tsx
835
+ import { StoreProvider } from "orchestore";
836
+ import { store } from "./store";
837
+
838
+ export default function App() {
839
+ return (
840
+ <StoreProvider store={store}>
841
+ <Routes />
842
+ </StoreProvider>
843
+ );
844
+ }
845
+ ```
846
+
847
+ ## Accessing Slices through Store
848
+
849
+ The root store behaves similarly to a parent slice and exposes all registered slices.
850
+
851
+ ```ts
852
+ store.counter.increment(1);
853
+
854
+ console.log(store.counter.getState());
855
+ ```
856
+
857
+ ## Accessing Store from Slices
858
+
859
+ Every slice has access to the root store instance through `this.root`.
860
+
861
+ ```ts
862
+ this.root.auth.getState().isAuthenticated;
863
+ ```
864
+
865
+ **Useful for:**
866
+
867
+ - cross-slice coordination
868
+ - avoiding circular imports
869
+ - application-wide orchestration
870
+
871
+ ## Root Store Type Extension (Planned)
872
+
873
+ Overriding `OrcheStore.Slots.root` provides full root store typing throughout the application.
874
+
875
+ > 🐞 Under active development: this currently causes a circular type inference limitation.
876
+
877
+ ```ts
878
+ import { createStore } from "orchestore";
879
+
880
+ export const store = createStore({
881
+ slices: {
882
+ counter,
883
+ },
884
+ });
885
+
886
+ declare module "orchestore" {
887
+ namespace OrcheStore {
888
+ interface Slots {
889
+ root: typeof store; // Bugfix: Causes a circular type inference
890
+ }
891
+ }
892
+ }
893
+ ```
894
+
895
+ ```ts
896
+ this.root; // Before: any
897
+ this.root; // After: fully typed store
898
+ ```
899
+
900
+ **Rules:**
901
+
902
+ - `root` must be a store instance created using `createStore`
903
+ - `null` and `undefined` are excluded automatically
904
+ - `typeof store | null | undefined` is equivalent to `typeof store`
905
+ - Invalid types fall back to `any`
906
+
907
+ ---
908
+
909
+ # Global Utilities
910
+
911
+ Global utilities allow slices and the root store to access shared runtime services through `global`.
912
+
913
+ Common use cases include:
914
+
915
+ - notifications and toasts
916
+ - navigation and routing
917
+ - analytics and tracking
918
+ - API clients and service wrappers
919
+ - runtime values that are difficult to access directly from slices
920
+ - integrations with React hooks and third-party libraries
921
+
922
+ Utilities are registered using `provideGlobalUtils` and are accessible from any slice or the root store.
923
+
924
+ ## Accessing Global Utilities
925
+
926
+ **Available:**
927
+
928
+ - Through the exposed store or slice instances
929
+
930
+ ```ts
931
+ store.global;
932
+ slice.global;
933
+ ```
934
+
935
+ - Inside slice methods
936
+
937
+ ```ts
938
+ this.global.notify("success", "Saved!");
939
+ ```
940
+
941
+ ## Utilities Type Extension
942
+
943
+ Overriding `OrcheStore.Slots.global` provides full typing everywhere.
944
+
945
+ ```ts
946
+ import type { NavigateFunction } from "react-router";
947
+
948
+ declare module "orchestore" {
949
+ namespace OrcheStore {
950
+ interface Slots {
951
+ global: {
952
+ navigate: NavigateFunction;
953
+
954
+ notify(type: "info" | "error" | "success", message: string): void;
955
+ };
956
+ }
957
+ }
958
+ }
959
+ ```
960
+
961
+ ```ts
962
+ this.global; // Before: any
963
+ this.global; // After: fully typed
964
+ ```
965
+
966
+ **Rules:**
967
+
968
+ - `global` must be an object
969
+ - `null` and `undefined` are excluded automatically
970
+ - `object | null | undefined` is equivalent to `object`
971
+ - Invalid types fall back to `any`
972
+
973
+ ## Providing Runtime Utilities
974
+
975
+ Global utility values can be registered or updated at runtime.
976
+
977
+ ```ts
978
+ import { useEffect } from "react";
979
+ import { useNavigate } from "react-router";
980
+ import { provideGlobalUtils } from "orchestore";
981
+ import { feedbacks } from "./ui-feedbacks";
982
+ import { store } from "./store";
983
+
984
+ provideGlobalUtils({
985
+ notify(type, message) {
986
+ feedbacks.notify(type, message);
987
+ },
988
+ });
989
+
990
+ export default function App() {
991
+ const navigate = useNavigate();
992
+
993
+ useEffect(() => {
994
+ provideGlobalUtils({ navigate });
995
+ }, [navigate]);
996
+
997
+ return (
998
+ <StoreProvider store={store}>
999
+ <Routes />
1000
+ </StoreProvider>
1001
+ );
1002
+ }
1003
+ ```
1004
+
1005
+ ## Using Global Utilities in Slices
1006
+
1007
+ Global utilities can be used anywhere a slice instance is available.
1008
+
1009
+ ```ts
1010
+ methods: {
1011
+ async insertUser(data: UserInput) {
1012
+ try {
1013
+ this.setLoading(true);
1014
+
1015
+ const response = await api.users.add(data);
1016
+
1017
+ this.global.notify("success", "User added successfully!");
1018
+
1019
+ this.setLoading(false);
1020
+
1021
+ this.global.navigate("/users/" + response.id);
1022
+ } catch (error) {
1023
+ this.global.notify("error", "Failed to add user");
1024
+
1025
+ console.error(error);
1026
+ }
1027
+ }
1028
+ }
1029
+ ```
1030
+
1031
+ ---
1032
+
1033
+ # TypeScript Inference
1034
+
1035
+ OrcheStore is designed around deep TypeScript inference.
1036
+
1037
+ ```ts
1038
+ const counter = createSlice({
1039
+ name: "counter",
1040
+
1041
+ state: {
1042
+ value: 0,
1043
+ },
1044
+
1045
+ mutations: {
1046
+ increment(state, amount: number) {
1047
+ state.value += amount;
1048
+ },
1049
+ },
1050
+
1051
+ methods: {
1052
+ async incrementAfter(amount: number, delay = 1000) {
1053
+ await new Promise((resolve) => setTimeout(resolve, delay));
1054
+ this.increment(amount);
1055
+ },
1056
+ },
1057
+
1058
+ // children: {
1059
+ // subCounter: createSlice({
1060
+ // name: "subCounter",
1061
+
1062
+ // state: {
1063
+ // value: 0,
1064
+ // },
1065
+ // }),
1066
+ // },
1067
+ });
1068
+ ```
1069
+
1070
+ Automatically produces:
1071
+
1072
+ ```ts
1073
+ counter.getState();
1074
+ // { value: number }
1075
+ // { value: number, subCounter: { value: number } } // Planned
1076
+
1077
+ // counter.subCounter.getState();
1078
+ // { value: number } // Planned
1079
+
1080
+ counter.increment(amount: number): void;
1081
+
1082
+ counter.incrementAfter(amount: number, delay?: number): Promise<number>;
1083
+ ```
1084
+
1085
+ No manual type declarations required.
1086
+
1087
+ ## Framework Type Extensions
1088
+
1089
+ OrcheStore also exposes user-definable type slots through `OrcheStore.Slots`.
1090
+
1091
+ These slots allow application-specific types to be injected into the framework and become available everywhere with full type safety.
1092
+
1093
+ | Slot | Purpose |
1094
+ | -------------------------- | ----------------------- |
1095
+ | `OrcheStore.Slots.root` | Root store typing |
1096
+ | `OrcheStore.Slots.global` | Global utilities typing |
1097
+
1098
+ ```ts
1099
+ declare module "orhestore" {
1100
+ namespace OrcheStore {
1101
+ interface Slots {
1102
+ root: typeof store; // Bugfix: Causes a circular type inference
1103
+
1104
+ global: {
1105
+ navigate: NavigateFunction;
1106
+ notify(type: "info" | "error" | "success", message: string): void;
1107
+ };
1108
+ }
1109
+ }
1110
+ }
1111
+ ```
1112
+
1113
+ This provides full typing for APIs such as:
1114
+
1115
+ ```ts
1116
+ this.root;
1117
+ this.global;
1118
+
1119
+ store.global;
1120
+ counter.global;
1121
+ ```
1122
+
1123
+ ---
1124
+
1125
+ # Status
1126
+
1127
+ OrcheStore is currently experimental and under active development.
1128
+
1129
+ Planned features:
1130
+
1131
+ - middleware and plugin system
1132
+ - persistence utilities
1133
+ - SSR support
1134
+ - deep readonly enforcement
1135
+ - lifecycle hooks
1136
+ - enhanced DevTools integration
1137
+
1138
+ ---