orchestore 0.1.7 → 0.1.9
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 +304 -281
- package/dist/index.cjs +6 -25
- package/dist/index.d.cts +739 -107
- package/dist/index.d.ts +739 -107
- package/dist/index.js +6 -25
- package/package.json +12 -3
package/README.md
CHANGED
|
@@ -25,9 +25,9 @@ Stay tuned for updates.
|
|
|
25
25
|
|
|
26
26
|
## About
|
|
27
27
|
|
|
28
|
-
> 🧩 A function-oriented state orchestration architecture built on top of Redux Toolkit.
|
|
28
|
+
> 🧩 A function-oriented state orchestration architecture built on top of Redux Toolkit, inspired by Vuex.
|
|
29
29
|
|
|
30
|
-
OrcheStore
|
|
30
|
+
OrcheStore brings a Vuex-inspired developer experience to React and Redux Toolkit applications by unifying state and behavior into directly callable runtime modules.
|
|
31
31
|
|
|
32
32
|
Instead of distributing logic across reducers, actions, thunks, selectors, hooks, middleware, and utility files, OrcheStore organizes related functionality into cohesive slice modules.
|
|
33
33
|
|
|
@@ -35,133 +35,124 @@ The goal is simple:
|
|
|
35
35
|
|
|
36
36
|
> ⚡ Spend less time wiring state management infrastructure and more time building application features.
|
|
37
37
|
|
|
38
|
-
##
|
|
38
|
+
## Installation
|
|
39
39
|
|
|
40
|
-
|
|
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](#nested-slices)
|
|
55
|
-
- [Reusing Slices](#reusing-slices)
|
|
56
|
-
- [Runtime Paths](#runtime-paths)
|
|
57
|
-
|
|
58
|
-
- [State Access & Subscriptions](#state-access--subscriptions)
|
|
59
|
-
- [State Snapshots](#state-snapshots)
|
|
60
|
-
- [State Subscriptions](#state-subscriptions)
|
|
61
|
-
- [Draft State](#draft-state)
|
|
62
|
-
|
|
63
|
-
- [Store Integration](#store-integration)
|
|
64
|
-
- [Creating the Store](#creating-the-store)
|
|
65
|
-
- [Store Provider](#store-provider)
|
|
66
|
-
- [Accessing Slices through Store](#accessing-slices-through-store)
|
|
67
|
-
- [Accessing Store from Slices](#accessing-store-from-slices)
|
|
68
|
-
- [Root Store Type Extension (Planned)](#root-store-type-extension-planned)
|
|
69
|
-
|
|
70
|
-
- [Lineage & Clones](#lineage--clones)
|
|
71
|
-
- [Automatic Cloning](#automatic-cloning)
|
|
72
|
-
- [Manual Cloning](#manual-cloning)
|
|
73
|
-
- [Inspecting a Lineage](#inspecting-a-lineage)
|
|
74
|
-
- [Definition Type Checking](#definition-type-checking)
|
|
75
|
-
|
|
76
|
-
- [Global Utilities](#global-utilities)
|
|
77
|
-
- [Accessing Global Utilities](#accessing-global-utilities)
|
|
78
|
-
- [Utilities Type Extension](#utilities-type-extension)
|
|
79
|
-
- [Providing Runtime Utilities](#providing-runtime-utilities)
|
|
80
|
-
- [Using Global Utilities in Slices](#using-global-utilities-in-slices)
|
|
81
|
-
|
|
82
|
-
- [TypeScript Inference](#typescript-inference)
|
|
83
|
-
|
|
84
|
-
- [Status](#status)
|
|
40
|
+
With npm:
|
|
85
41
|
|
|
86
|
-
|
|
42
|
+
```bash
|
|
43
|
+
npm install orchestore
|
|
44
|
+
```
|
|
87
45
|
|
|
88
|
-
|
|
46
|
+
Or with Yarn:
|
|
89
47
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
- Centralize state and application logic
|
|
94
|
-
- Provide direct and intuitive APIs
|
|
95
|
-
- Preserve predictable state transitions
|
|
96
|
-
- Maintain strong TypeScript inference
|
|
97
|
-
- Scale naturally through composition
|
|
48
|
+
```bash
|
|
49
|
+
yarn add orchestore
|
|
50
|
+
```
|
|
98
51
|
|
|
99
|
-
|
|
52
|
+
Or with pnpm:
|
|
100
53
|
|
|
101
|
-
|
|
54
|
+
```bash
|
|
55
|
+
pnpm add orchestore
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
**Peer Dependencies**
|
|
102
59
|
|
|
103
|
-
|
|
104
|
-
- action creators
|
|
105
|
-
- thunks
|
|
106
|
-
- selectors
|
|
107
|
-
- middleware
|
|
108
|
-
- hooks
|
|
109
|
-
- utility functions
|
|
60
|
+
OrcheStore requires:
|
|
110
61
|
|
|
111
|
-
|
|
62
|
+
* React 16.9+
|
|
63
|
+
* React DOM 16.9+
|
|
112
64
|
|
|
113
|
-
|
|
65
|
+
**Included Dependencies**
|
|
114
66
|
|
|
115
|
-
|
|
67
|
+
`@reduxjs/toolkit` and `react-redux` are installed automatically with OrcheStore.
|
|
116
68
|
|
|
117
|
-
|
|
118
|
-
- computed state
|
|
119
|
-
- mutations
|
|
120
|
-
- methods
|
|
121
|
-
- selectors
|
|
122
|
-
- child slices
|
|
123
|
-
- shared utilities
|
|
69
|
+
## Table of Contents
|
|
124
70
|
|
|
125
|
-
|
|
71
|
+
* [Introduction](#orchestore)
|
|
72
|
+
* [Installation](#installation)
|
|
73
|
+
* [Core Principles](#core-principles)
|
|
74
|
+
* [Why OrcheStore?](#why-orchestore)
|
|
75
|
+
* [Architecture Overview](#architecture-overview)
|
|
76
|
+
|
|
77
|
+
* [Quick Example](#quick-example)
|
|
78
|
+
|
|
79
|
+
* [Slice Layers](#slice-layers)
|
|
80
|
+
* [name](#name)
|
|
81
|
+
* [state](#state)
|
|
82
|
+
* [Mutations](#mutations)
|
|
83
|
+
* [Methods](#methods)
|
|
84
|
+
* [Computed State (Planned)](#computed-state-planned)
|
|
85
|
+
* [Nested Slices](#nested-slices)
|
|
86
|
+
* [Accessing Children through Parent Slice](#accessing-children-through-parent-slice)
|
|
87
|
+
* [Accessing Parent Slice from Children](#accessing-parent-slice-from-children)
|
|
88
|
+
* [Reusing Slices](#reusing-slices)
|
|
89
|
+
* [Runtime Paths](#runtime-paths)
|
|
90
|
+
|
|
91
|
+
* [State Access & Subscriptions](#state-access--subscriptions)
|
|
92
|
+
* [State Snapshots](#state-snapshots)
|
|
93
|
+
* [State Subscriptions](#state-subscriptions)
|
|
94
|
+
* [Draft State](#draft-state)
|
|
95
|
+
|
|
96
|
+
* [Store Integration](#store-integration)
|
|
97
|
+
* [Creating the Store](#creating-the-store)
|
|
98
|
+
* [Store Provider](#store-provider)
|
|
99
|
+
* [Accessing Slices through Store](#accessing-slices-through-store)
|
|
100
|
+
* [Accessing Store from Slices](#accessing-store-from-slices)
|
|
101
|
+
|
|
102
|
+
* [Lineage & Clones](#lineage--clones)
|
|
103
|
+
* [Manual Cloning](#manual-cloning)
|
|
104
|
+
* [Automatic Cloning](#automatic-cloning)
|
|
105
|
+
* [Inspecting a Lineage](#inspecting-a-lineage)
|
|
106
|
+
* [Definition Type Checking](#definition-type-checking)
|
|
107
|
+
|
|
108
|
+
* [Utilities](#utilities)
|
|
109
|
+
* [Accessing Utilities](#accessing-utilities)
|
|
110
|
+
* [Utilities Type Extension](#utilities-type-extension)
|
|
111
|
+
* [Providing Runtime Utilities](#providing-runtime-utilities)
|
|
112
|
+
* [Using Utilities in Slices](#using-utilities-in-slices)
|
|
113
|
+
|
|
114
|
+
* [TypeScript Inference](#typescript-inference)
|
|
115
|
+
|
|
116
|
+
* [Status](#status)
|
|
117
|
+
|
|
118
|
+
---
|
|
126
119
|
|
|
127
|
-
|
|
120
|
+
## Core Principles
|
|
128
121
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
| Manual state tree composition | Nested slices with automatic cloning & isolation |
|
|
138
|
-
| Complex type declarations | Automatic inference |
|
|
139
|
-
| Instance identity management | Lineage-based slice model (shared definition, isolated mounts) |
|
|
122
|
+
* Simplify state management architecture
|
|
123
|
+
* Automate repetitive Redux patterns
|
|
124
|
+
* Reduce infrastructure code
|
|
125
|
+
* Centralize state and application logic
|
|
126
|
+
* Provide direct and intuitive APIs
|
|
127
|
+
* Preserve predictable state transitions
|
|
128
|
+
* Maintain strong TypeScript inference
|
|
129
|
+
* Scale naturally through composition
|
|
140
130
|
|
|
141
|
-
|
|
131
|
+
## Why OrcheStore?
|
|
142
132
|
|
|
143
|
-
|
|
133
|
+
Redux Toolkit greatly improves the Redux experience, but applications still often require developers to coordinate reducers, actions, selectors, thunks, hooks, utilities, and state composition.
|
|
144
134
|
|
|
145
|
-
|
|
135
|
+
OrcheStore builds on top of Redux Toolkit and combines these patterns into a unified slice model. A slice can encapsulate state, mutations, methods, computed values, child slices, and shared application utilities within a single module.
|
|
146
136
|
|
|
147
|
-
|
|
137
|
+
The goal is to reduce framework plumbing and allow application behavior to remain close to the state it operates on.
|
|
148
138
|
|
|
149
|
-
|
|
|
150
|
-
|
|
|
151
|
-
|
|
|
152
|
-
|
|
|
153
|
-
|
|
|
154
|
-
|
|
|
155
|
-
|
|
|
156
|
-
|
|
|
157
|
-
|
|
|
158
|
-
|
|
|
159
|
-
|
|
|
160
|
-
|
|
|
161
|
-
|
|
|
162
|
-
|
|
|
139
|
+
| Concern | Redux Toolkit | OrcheStore |
|
|
140
|
+
| ------------------ | ---------------------------------------- | ----------------------------------------------- |
|
|
141
|
+
| State updates | Actions + dispatch | Direct callable mutations |
|
|
142
|
+
| Mutation arguments | `PayloadAction` wrappers | Native function arguments |
|
|
143
|
+
| Async logic | Separate thunks | Built-in methods |
|
|
144
|
+
| State selection | Global store selector hooks | Global + slice-scoped selection hooks |
|
|
145
|
+
| Cross-slice access | Imports & wiring | Runtime tree access (Root / Parent / Children) |
|
|
146
|
+
| Shared services | Manual integration | Application-wide utilities |
|
|
147
|
+
| State composition | Manual reducer composition | Nested slices |
|
|
148
|
+
| Identity model | Singleton-like slice definition | Lineage-based identity system |
|
|
149
|
+
| Instance reuse | Function-level reuse of slice reducers | Reused slices create isolated runtime instances |
|
|
150
|
+
| Cloning model | Factory pattern required for re-creation | Built-in cloning with lineage tracking |
|
|
151
|
+
| Exposed API | Reducers, actions and some helpers | Direct usable slice APIs |
|
|
152
|
+
| Type inference | Strong | Deep end-to-end inference |
|
|
153
|
+
| Developer focus | Connect infrastructure | Implement behavior |
|
|
163
154
|
|
|
164
|
-
OrcheStore does not replace Redux Toolkit.
|
|
155
|
+
OrcheStore does not replace Redux Toolkit. It builds on top of it, providing a higher-level API for organizing state and application behavior with stronger automation of Redux patterns and reduced coordination overhead for developers.
|
|
165
156
|
|
|
166
157
|
## Architecture Overview
|
|
167
158
|
|
|
@@ -230,6 +221,15 @@ export const counter = createSlice({
|
|
|
230
221
|
```tsx
|
|
231
222
|
import { createSlice, createAsyncThunk, type PayloadAction } from "@reduxjs/toolkit";
|
|
232
223
|
|
|
224
|
+
// Separate APIs for async workflows
|
|
225
|
+
export const incrementAfter = createAsyncThunk(
|
|
226
|
+
"counter/incrementAfter",
|
|
227
|
+
async ({ amount, delay }: { amount: number; delay: number }) => {
|
|
228
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
229
|
+
return amount;
|
|
230
|
+
}
|
|
231
|
+
);
|
|
232
|
+
|
|
233
233
|
export const counter = createSlice({
|
|
234
234
|
name: "counter",
|
|
235
235
|
|
|
@@ -251,20 +251,14 @@ export const counter = createSlice({
|
|
|
251
251
|
);
|
|
252
252
|
},
|
|
253
253
|
},
|
|
254
|
-
});
|
|
255
|
-
|
|
256
|
-
// Separate APIs for async workflows
|
|
257
|
-
export const incrementAfter = createAsyncThunk(
|
|
258
|
-
"counter/incrementAfter",
|
|
259
|
-
async (
|
|
260
|
-
{ amount, delay }: { amount: number; delay: number },
|
|
261
|
-
{ dispatch }
|
|
262
|
-
) => {
|
|
263
|
-
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
264
254
|
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
)
|
|
255
|
+
extraReducers: (builder) => {
|
|
256
|
+
// Handles fulfilled async thunk result
|
|
257
|
+
builder.addCase(incrementAfter.fulfilled, (state, action) => {
|
|
258
|
+
state.value += action.payload;
|
|
259
|
+
});
|
|
260
|
+
},
|
|
261
|
+
});
|
|
268
262
|
```
|
|
269
263
|
|
|
270
264
|
## Slice Usage
|
|
@@ -434,9 +428,8 @@ methods: {
|
|
|
434
428
|
|
|
435
429
|
**Rules:**
|
|
436
430
|
|
|
437
|
-
-
|
|
431
|
+
- Names should not contain `"."` or `"/"`, because they are reserved for nested slice paths
|
|
438
432
|
- Two slices cannot share the same name
|
|
439
|
-
- Registering the same slice instance multiple times with the same name is not allowed
|
|
440
433
|
|
|
441
434
|
---
|
|
442
435
|
|
|
@@ -527,8 +520,10 @@ Methods are the orchestration layer of a slice.
|
|
|
527
520
|
- receive any number of arguments
|
|
528
521
|
- can return synchronous values or Promises
|
|
529
522
|
- can access:
|
|
530
|
-
|
|
531
|
-
|
|
523
|
+
- state, mutations, slibling methods, nested slices (through `this`)
|
|
524
|
+
- Root store (`this.root`)
|
|
525
|
+
- Parent slice (`this.parent`)
|
|
526
|
+
- Application-wide utilities (`this.utils`)
|
|
532
527
|
|
|
533
528
|
Methods are not serialized, replayable, or represented in Redux DevTools action history.
|
|
534
529
|
|
|
@@ -548,20 +543,9 @@ Methods are designed for centralizing any slice-related logic:
|
|
|
548
543
|
Methods should NOT:
|
|
549
544
|
|
|
550
545
|
- include slice-unrelated logic.
|
|
546
|
+
- include UI layer logic.
|
|
551
547
|
- mutate state directly. use mutations instead.
|
|
552
548
|
|
|
553
|
-
Prefer:
|
|
554
|
-
|
|
555
|
-
```ts
|
|
556
|
-
this.increment(1);
|
|
557
|
-
```
|
|
558
|
-
|
|
559
|
-
Instead of:
|
|
560
|
-
|
|
561
|
-
```ts
|
|
562
|
-
this.getState().value++;
|
|
563
|
-
```
|
|
564
|
-
|
|
565
549
|
**Example:**
|
|
566
550
|
|
|
567
551
|
```ts
|
|
@@ -582,7 +566,7 @@ const counter = createSlice({
|
|
|
582
566
|
async incrementAfter(amount: number, delay = 1000) {
|
|
583
567
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
584
568
|
this.increment(amount);
|
|
585
|
-
this.
|
|
569
|
+
this.utils.logger.info("Counter incremented");
|
|
586
570
|
},
|
|
587
571
|
},
|
|
588
572
|
});
|
|
@@ -622,27 +606,27 @@ This currently not supported
|
|
|
622
606
|
|
|
623
607
|
Slices can be composed by registering other slice instances through the `children` property.
|
|
624
608
|
|
|
625
|
-
This allows related state and behavior to be
|
|
609
|
+
This allows related state and behavior to be structured into a hierarchical ownership tree, while preserving full type inference, runtime path resolution, and instance isolation.
|
|
626
610
|
|
|
627
611
|
```ts
|
|
628
612
|
import { products } from "./productsSlice";
|
|
629
613
|
import { categories } from "./categoriesSlice";
|
|
630
614
|
|
|
631
615
|
export const shop = createSlice({
|
|
632
|
-
|
|
616
|
+
name: "shop",
|
|
633
617
|
|
|
634
|
-
|
|
618
|
+
state: {},
|
|
635
619
|
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
620
|
+
children: {
|
|
621
|
+
products,
|
|
622
|
+
categories,
|
|
623
|
+
},
|
|
640
624
|
});
|
|
641
625
|
```
|
|
642
626
|
|
|
643
|
-
|
|
627
|
+
### Accessing Children through Parent Slice
|
|
644
628
|
|
|
645
|
-
Child slices are exposed directly on their parent slice.
|
|
629
|
+
Child slices are exposed directly on their parent slice instance.
|
|
646
630
|
|
|
647
631
|
```ts
|
|
648
632
|
shop.products.add(...)
|
|
@@ -651,7 +635,7 @@ shop.categories.create(...)
|
|
|
651
635
|
console.log(shop.products.getState());
|
|
652
636
|
```
|
|
653
637
|
|
|
654
|
-
Deeply nested slice
|
|
638
|
+
Deeply nested slice trees are fully supported, including recursive composition.
|
|
655
639
|
|
|
656
640
|
```ts
|
|
657
641
|
admin.users.permissions.grant(...);
|
|
@@ -659,6 +643,16 @@ admin.users.permissions.grant(...);
|
|
|
659
643
|
console.log(admin.users.permissions.getState());
|
|
660
644
|
```
|
|
661
645
|
|
|
646
|
+
### Accessing Parent Slice from Children
|
|
647
|
+
|
|
648
|
+
Every slice instance has a runtime reference to its parent via `slice.parent`.
|
|
649
|
+
|
|
650
|
+
This allows also child slices to interact with sibling slices through the parent ownership scope.
|
|
651
|
+
|
|
652
|
+
```ts
|
|
653
|
+
this.parent.categories.getState().list;
|
|
654
|
+
```
|
|
655
|
+
|
|
662
656
|
### Reusing Slices
|
|
663
657
|
|
|
664
658
|
A slice can be mounted multiple times within the same tree.
|
|
@@ -916,7 +910,7 @@ console.log(store.counter.getState());
|
|
|
916
910
|
|
|
917
911
|
## Accessing Store from Slices
|
|
918
912
|
|
|
919
|
-
Every slice has access to the root store instance
|
|
913
|
+
Every slice has access to the root store instance via `slice.root`.
|
|
920
914
|
|
|
921
915
|
```ts
|
|
922
916
|
this.root.auth.getState().isAuthenticated;
|
|
@@ -928,68 +922,103 @@ this.root.auth.getState().isAuthenticated;
|
|
|
928
922
|
- avoiding circular imports
|
|
929
923
|
- application-wide orchestration
|
|
930
924
|
|
|
931
|
-
|
|
925
|
+
---
|
|
932
926
|
|
|
933
|
-
|
|
927
|
+
# Lineage & Clones
|
|
934
928
|
|
|
935
|
-
|
|
929
|
+
OrcheStore uses a lineage-based model for slice identity.
|
|
936
930
|
|
|
937
|
-
|
|
938
|
-
import { createStore } from "orchestore";
|
|
931
|
+
**Why?**
|
|
939
932
|
|
|
940
|
-
|
|
941
|
-
slices: {
|
|
942
|
-
counter,
|
|
943
|
-
},
|
|
944
|
-
});
|
|
933
|
+
Slices can be used in multiple places in the store tree.
|
|
945
934
|
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
935
|
+
When this happens, OrcheStore creates a separate runtime instance for each usage. These instances are called **clones**.
|
|
936
|
+
|
|
937
|
+
A clone is an independent instance of a slice at runtime. It has its own state and runs separately from other clones, while still remaining part of a shared lineage.
|
|
938
|
+
|
|
939
|
+
A lineage (or family) is the set of all runtime instances that originate from the same slice definition.
|
|
940
|
+
|
|
941
|
+
**This means:**
|
|
942
|
+
|
|
943
|
+
- slices are not singletons
|
|
944
|
+
- a slice can appear multiple times in a tree
|
|
945
|
+
- each clone is fully isolated
|
|
946
|
+
- all instances created from the same definition are linked through lineage
|
|
947
|
+
|
|
948
|
+
## Manual Cloning
|
|
949
|
+
|
|
950
|
+
A new detached clone can be created manually from any slice instance.
|
|
951
|
+
|
|
952
|
+
### 1. Clone without state transformation
|
|
954
953
|
|
|
955
954
|
```ts
|
|
956
|
-
|
|
957
|
-
this.root; // After: fully typed store
|
|
955
|
+
const clone = slice.prototype.clone();
|
|
958
956
|
```
|
|
959
957
|
|
|
960
|
-
|
|
958
|
+
The new instance:
|
|
961
959
|
|
|
962
|
-
-
|
|
963
|
-
-
|
|
964
|
-
|
|
965
|
-
-
|
|
960
|
+
- belongs to the same lineage
|
|
961
|
+
- starts detached from the tree
|
|
962
|
+
- has no mounted path initially
|
|
963
|
+
- has its own ownership context
|
|
964
|
+
- uses the exact initial state of the source slice
|
|
966
965
|
|
|
967
|
-
|
|
966
|
+
### 2. Clone with state transformation
|
|
968
967
|
|
|
969
|
-
|
|
968
|
+
```ts
|
|
969
|
+
const clone = slice.prototype.clone((state) => newState);
|
|
970
|
+
```
|
|
970
971
|
|
|
971
|
-
|
|
972
|
+
The provided function receives the fully resolved initial state (including nested slices) and returns the modified state for the new instance.
|
|
972
973
|
|
|
973
|
-
|
|
974
|
+
The state transformer:
|
|
974
975
|
|
|
975
|
-
|
|
976
|
+
- does not affect other lineage members
|
|
977
|
+
- supports nested slice state updates
|
|
978
|
+
- supports immutable updates (returning a new state object)
|
|
979
|
+
- supports mutable updates (Immer-style — no return required)
|
|
976
980
|
|
|
977
|
-
|
|
981
|
+
**Example:**
|
|
978
982
|
|
|
979
|
-
|
|
983
|
+
```ts
|
|
984
|
+
const crudSlice = createSlice({
|
|
985
|
+
name: "CRUD-Slice",
|
|
980
986
|
|
|
981
|
-
|
|
987
|
+
state: {
|
|
988
|
+
endpoint: "",
|
|
989
|
+
},
|
|
982
990
|
|
|
983
|
-
|
|
991
|
+
children: {
|
|
992
|
+
pagination: paginationSlice,
|
|
993
|
+
dropdown: searchDropdownSlice,
|
|
994
|
+
},
|
|
995
|
+
});
|
|
984
996
|
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
997
|
+
// Immutable style (returns new state object)
|
|
998
|
+
const productsSlice = crudSlice.prototype.clone((state) => ({
|
|
999
|
+
...state,
|
|
1000
|
+
endpoint: "api/v1/products",
|
|
1001
|
+
|
|
1002
|
+
dropdown: {
|
|
1003
|
+
...state.dropdown,
|
|
1004
|
+
supported: false,
|
|
1005
|
+
},
|
|
1006
|
+
}));
|
|
1007
|
+
|
|
1008
|
+
// Immer-style mutation (no return needed)
|
|
1009
|
+
const categoriesSlice = crudSlice.prototype.clone((state) => {
|
|
1010
|
+
state.endpoint = "api/v1/categories";
|
|
1011
|
+
state.pagination.supported = false;
|
|
1012
|
+
});
|
|
1013
|
+
```
|
|
989
1014
|
|
|
990
1015
|
## Automatic Cloning
|
|
991
1016
|
|
|
992
|
-
|
|
1017
|
+
OrcheStore creates slice instances automatically in two cases:
|
|
1018
|
+
|
|
1019
|
+
### 1. Reuse through `children` / `slices`
|
|
1020
|
+
|
|
1021
|
+
When a slice is reused through `children` (or `slices`), OrcheStore creates a new mounted instance for each usage.
|
|
993
1022
|
|
|
994
1023
|
```ts
|
|
995
1024
|
const paginationSlice = createSlice({ ... });
|
|
@@ -1024,36 +1053,49 @@ shopSlice.a !== adminSlice.a;
|
|
|
1024
1053
|
shopSlice.b !== adminSlice.a;
|
|
1025
1054
|
```
|
|
1026
1055
|
|
|
1027
|
-
Each instance
|
|
1056
|
+
Each instance has its own ownership context:
|
|
1028
1057
|
|
|
1029
1058
|
```ts
|
|
1030
|
-
shopSlice.a.path;
|
|
1031
|
-
shopSlice.b.path;
|
|
1032
|
-
adminSlice.a.path;
|
|
1059
|
+
shopSlice.a.path; // "shop.a"
|
|
1060
|
+
shopSlice.b.path; // "shop.b"
|
|
1061
|
+
adminSlice.a.path; // "admin.a"
|
|
1033
1062
|
```
|
|
1034
1063
|
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
## Manual Cloning
|
|
1064
|
+
### 2. Cloning a parent slice (cascade cloning)
|
|
1038
1065
|
|
|
1039
|
-
|
|
1066
|
+
When a parent slice is cloned or reused, all its nested children are automatically cloned as well.
|
|
1040
1067
|
|
|
1041
1068
|
```ts
|
|
1042
|
-
const
|
|
1069
|
+
const crudSlice = createSlice({
|
|
1070
|
+
name: "CRUD-Slice",
|
|
1071
|
+
|
|
1072
|
+
state: {
|
|
1073
|
+
endpoint: "",
|
|
1074
|
+
},
|
|
1075
|
+
|
|
1076
|
+
children: {
|
|
1077
|
+
pagination: paginationSlice,
|
|
1078
|
+
dropdown: searchDropdownSlice,
|
|
1079
|
+
},
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
const productsSlice = crudSlice.prototype.clone();
|
|
1083
|
+
const categoriesSlice = crudSlice.prototype.clone();
|
|
1043
1084
|
```
|
|
1044
1085
|
|
|
1045
|
-
|
|
1086
|
+
Each clone receives its own independent subtree:
|
|
1046
1087
|
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1088
|
+
```ts
|
|
1089
|
+
productsSlice.pagination !== categoriesSlice.pagination;
|
|
1090
|
+
```
|
|
1091
|
+
|
|
1092
|
+
This ensures that both parent and child slices remain fully isolated across all clone instances.
|
|
1051
1093
|
|
|
1052
1094
|
## Inspecting a Lineage
|
|
1053
1095
|
|
|
1054
1096
|
**Get All Related Instances:**
|
|
1055
1097
|
|
|
1056
|
-
Returns every instance in the lineage, **including** the current
|
|
1098
|
+
Returns every instance in the lineage, **including** the current instance.
|
|
1057
1099
|
|
|
1058
1100
|
```ts
|
|
1059
1101
|
const lineage = slice.prototype.getLineage();
|
|
@@ -1065,9 +1107,9 @@ Useful for:
|
|
|
1065
1107
|
- inspecting mounted instances
|
|
1066
1108
|
- understanding tree distribution
|
|
1067
1109
|
|
|
1068
|
-
**Get Clones:**
|
|
1110
|
+
**Get Sibling Clones:**
|
|
1069
1111
|
|
|
1070
|
-
Returns
|
|
1112
|
+
Returns every sibling in the lineage, **except** the current instance.
|
|
1071
1113
|
|
|
1072
1114
|
```ts
|
|
1073
1115
|
const siblings = slice.prototype.getClones();
|
|
@@ -1081,8 +1123,6 @@ Useful for:
|
|
|
1081
1123
|
|
|
1082
1124
|
## Definition Type Checking
|
|
1083
1125
|
|
|
1084
|
-
You can determine whether two slices belong to the same lineage:
|
|
1085
|
-
|
|
1086
1126
|
You can check whether two slices belong to the same lineage:
|
|
1087
1127
|
|
|
1088
1128
|
```ts
|
|
@@ -1108,19 +1148,20 @@ slice2.prototype.isTypeOf(clone1); // false
|
|
|
1108
1148
|
|
|
1109
1149
|
## Summary
|
|
1110
1150
|
|
|
1111
|
-
- Reusing a slice automatically creates mounted clones.
|
|
1112
1151
|
- `clone()` creates a new detached lineage member.
|
|
1152
|
+
- `clone(stateTransformer)` allows per-instance state customization at creation time.
|
|
1153
|
+
- Reusing a slice automatically creates mounted clones.
|
|
1113
1154
|
- Every clone is isolated at runtime.
|
|
1114
1155
|
- All clones from the same definition belong to a shared lineage.
|
|
1115
1156
|
- `getLineage()` returns all instances in a lineage.
|
|
1116
|
-
- `getClones()` returns all related instances except the current
|
|
1157
|
+
- `getClones()` returns all related instances except the current instance.
|
|
1117
1158
|
- `isTypeOf()` checks whether two instances belong to the same lineage.
|
|
1118
1159
|
|
|
1119
1160
|
---
|
|
1120
1161
|
|
|
1121
|
-
#
|
|
1162
|
+
# Utilities
|
|
1122
1163
|
|
|
1123
|
-
|
|
1164
|
+
Application-wide utilities allow slices and stores to access shared runtime services through `utils`.
|
|
1124
1165
|
|
|
1125
1166
|
Common use cases include:
|
|
1126
1167
|
|
|
@@ -1131,28 +1172,33 @@ Common use cases include:
|
|
|
1131
1172
|
- runtime values that are difficult to access directly from slices
|
|
1132
1173
|
- integrations with React hooks and third-party libraries
|
|
1133
1174
|
|
|
1134
|
-
Utilities are registered using `
|
|
1175
|
+
Utilities are registered using `setUtils` and are accessible from any slice or the root store.
|
|
1135
1176
|
|
|
1136
|
-
## Accessing
|
|
1177
|
+
## Accessing Utilities
|
|
1137
1178
|
|
|
1138
1179
|
**Available:**
|
|
1139
1180
|
|
|
1140
|
-
- Through
|
|
1181
|
+
- Through exposed store and slice instances
|
|
1141
1182
|
|
|
1142
1183
|
```ts
|
|
1143
|
-
store.
|
|
1144
|
-
slice.
|
|
1184
|
+
store.utils;
|
|
1185
|
+
slice.utils;
|
|
1186
|
+
this.utils; // Inside slice methods and mutations
|
|
1145
1187
|
```
|
|
1146
1188
|
|
|
1147
|
-
-
|
|
1189
|
+
- Through `getUtils`
|
|
1148
1190
|
|
|
1149
1191
|
```ts
|
|
1150
|
-
|
|
1192
|
+
import { getUtils } from "orchestore";
|
|
1193
|
+
|
|
1194
|
+
const utils = getUtils();
|
|
1151
1195
|
```
|
|
1152
1196
|
|
|
1153
1197
|
## Utilities Type Extension
|
|
1154
1198
|
|
|
1155
|
-
|
|
1199
|
+
OrcheStore exposes user-definable type slots through `OrcheStore.Slots`.
|
|
1200
|
+
|
|
1201
|
+
By overriding `OrcheStore.Slots.utils`, application-specific utilities become available throughout the framework with full type safety.
|
|
1156
1202
|
|
|
1157
1203
|
```ts
|
|
1158
1204
|
import type { NavigateFunction } from "react-router";
|
|
@@ -1160,7 +1206,7 @@ import type { NavigateFunction } from "react-router";
|
|
|
1160
1206
|
declare module "orchestore" {
|
|
1161
1207
|
namespace OrcheStore {
|
|
1162
1208
|
interface Slots {
|
|
1163
|
-
|
|
1209
|
+
utils: {
|
|
1164
1210
|
navigate: NavigateFunction;
|
|
1165
1211
|
|
|
1166
1212
|
notify(type: "info" | "error" | "success", message: string): void;
|
|
@@ -1170,30 +1216,43 @@ declare module "orchestore" {
|
|
|
1170
1216
|
}
|
|
1171
1217
|
```
|
|
1172
1218
|
|
|
1173
|
-
|
|
1174
|
-
this.global; // Before: any
|
|
1175
|
-
this.global; // After: fully typed
|
|
1176
|
-
```
|
|
1219
|
+
**Notes:**
|
|
1177
1220
|
|
|
1178
|
-
|
|
1221
|
+
* `OrcheStore.Slots` can be extended using either `declare module "orchestore"` or `declare global`, depending on your project's type organization preferences.
|
|
1222
|
+
* If your project uses JavaScript without TypeScript, you can still extend these types by creating a separate declaration file (for example, `orchestore.d.ts`). This allows editors and tooling to provide type checking and IntelliSense while keeping your application code in JavaScript.
|
|
1223
|
+
|
|
1224
|
+
**Rules**
|
|
1225
|
+
|
|
1226
|
+
* `utils` must resolve to an object type; otherwise, it falls back to `any`.
|
|
1227
|
+
* `null` and `undefined` are automatically excluded. For example, `object | null | undefined` resolves to `object`.
|
|
1228
|
+
|
|
1229
|
+
**Type Safety**
|
|
1230
|
+
|
|
1231
|
+
Before extending `OrcheStore.Slots.utils`, all utility-related APIs are typed as `any`.
|
|
1232
|
+
|
|
1233
|
+
After extending `OrcheStore.Slots.utils`, the inferred type is automatically applied throughout the framework, including:
|
|
1234
|
+
|
|
1235
|
+
* `store.utils`
|
|
1236
|
+
* `slice.utils`
|
|
1237
|
+
* `this.utils` inside mutations and methods
|
|
1238
|
+
* `getUtils()`
|
|
1239
|
+
* `setUtils()`
|
|
1240
|
+
* `type Utils`
|
|
1179
1241
|
|
|
1180
|
-
|
|
1181
|
-
- `null` and `undefined` are excluded automatically
|
|
1182
|
-
- `object | null | undefined` is equivalent to `object`
|
|
1183
|
-
- Invalid types fall back to `any`
|
|
1242
|
+
This provides consistent type safety and IntelliSense across all utility access points without requiring additional type declarations.
|
|
1184
1243
|
|
|
1185
1244
|
## Providing Runtime Utilities
|
|
1186
1245
|
|
|
1187
|
-
|
|
1246
|
+
Application-wide utility values can be registered or updated at runtime.
|
|
1188
1247
|
|
|
1189
1248
|
```ts
|
|
1190
1249
|
import { useEffect } from "react";
|
|
1191
1250
|
import { useNavigate } from "react-router";
|
|
1192
|
-
import {
|
|
1251
|
+
import { setUtils } from "orchestore";
|
|
1193
1252
|
import { feedbacks } from "./ui-feedbacks";
|
|
1194
1253
|
import { store } from "./store";
|
|
1195
1254
|
|
|
1196
|
-
|
|
1255
|
+
setUtils({
|
|
1197
1256
|
notify(type, message) {
|
|
1198
1257
|
feedbacks.notify(type, message);
|
|
1199
1258
|
},
|
|
@@ -1203,7 +1262,7 @@ export default function App() {
|
|
|
1203
1262
|
const navigate = useNavigate();
|
|
1204
1263
|
|
|
1205
1264
|
useEffect(() => {
|
|
1206
|
-
|
|
1265
|
+
setUtils({ navigate });
|
|
1207
1266
|
}, [navigate]);
|
|
1208
1267
|
|
|
1209
1268
|
return (
|
|
@@ -1214,9 +1273,9 @@ export default function App() {
|
|
|
1214
1273
|
}
|
|
1215
1274
|
```
|
|
1216
1275
|
|
|
1217
|
-
## Using
|
|
1276
|
+
## Using Utilities in Slices
|
|
1218
1277
|
|
|
1219
|
-
|
|
1278
|
+
Application-wide utilities can be used anywhere a slice instance is available.
|
|
1220
1279
|
|
|
1221
1280
|
```ts
|
|
1222
1281
|
methods: {
|
|
@@ -1226,13 +1285,13 @@ methods: {
|
|
|
1226
1285
|
|
|
1227
1286
|
const response = await api.users.add(data);
|
|
1228
1287
|
|
|
1229
|
-
this.
|
|
1288
|
+
this.utils.notify("success", "User added successfully!");
|
|
1230
1289
|
|
|
1231
1290
|
this.setLoading(false);
|
|
1232
1291
|
|
|
1233
|
-
this.
|
|
1292
|
+
this.utils.navigate("/users/" + response.id);
|
|
1234
1293
|
} catch (error) {
|
|
1235
|
-
this.
|
|
1294
|
+
this.utils.notify("error", "Failed to add user");
|
|
1236
1295
|
|
|
1237
1296
|
console.error(error);
|
|
1238
1297
|
}
|
|
@@ -1268,13 +1327,13 @@ const counter = createSlice({
|
|
|
1268
1327
|
},
|
|
1269
1328
|
|
|
1270
1329
|
children: {
|
|
1271
|
-
|
|
1272
|
-
|
|
1330
|
+
subCounter: createSlice({
|
|
1331
|
+
name: "subCounter",
|
|
1273
1332
|
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1333
|
+
state: {
|
|
1334
|
+
value: 0,
|
|
1335
|
+
},
|
|
1336
|
+
}),
|
|
1278
1337
|
},
|
|
1279
1338
|
});
|
|
1280
1339
|
```
|
|
@@ -1295,42 +1354,6 @@ counter.incrementAfter(amount: number, delay?: number): Promise<number>;
|
|
|
1295
1354
|
|
|
1296
1355
|
No manual type declarations required.
|
|
1297
1356
|
|
|
1298
|
-
## Framework Type Extensions
|
|
1299
|
-
|
|
1300
|
-
OrcheStore also exposes user-definable type slots through `OrcheStore.Slots`.
|
|
1301
|
-
|
|
1302
|
-
These slots allow application-specific types to be injected into the framework and become available everywhere with full type safety.
|
|
1303
|
-
|
|
1304
|
-
| Slot | Purpose |
|
|
1305
|
-
| -------------------------- | ----------------------- |
|
|
1306
|
-
| `OrcheStore.Slots.root` | Root store typing |
|
|
1307
|
-
| `OrcheStore.Slots.global` | Global utilities typing |
|
|
1308
|
-
|
|
1309
|
-
```ts
|
|
1310
|
-
declare module "orhestore" {
|
|
1311
|
-
namespace OrcheStore {
|
|
1312
|
-
interface Slots {
|
|
1313
|
-
root: typeof store; // Bugfix: Causes a circular type inference
|
|
1314
|
-
|
|
1315
|
-
global: {
|
|
1316
|
-
navigate: NavigateFunction;
|
|
1317
|
-
notify(type: "info" | "error" | "success", message: string): void;
|
|
1318
|
-
};
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
}
|
|
1322
|
-
```
|
|
1323
|
-
|
|
1324
|
-
This provides full typing for APIs such as:
|
|
1325
|
-
|
|
1326
|
-
```ts
|
|
1327
|
-
this.root;
|
|
1328
|
-
this.global;
|
|
1329
|
-
|
|
1330
|
-
store.global;
|
|
1331
|
-
counter.global;
|
|
1332
|
-
```
|
|
1333
|
-
|
|
1334
1357
|
---
|
|
1335
1358
|
|
|
1336
1359
|
# Status
|