orchestore 0.1.8 → 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 +303 -279
- package/dist/index.cjs +6 -25
- package/dist/index.d.cts +738 -106
- package/dist/index.d.ts +738 -106
- package/dist/index.js +6 -25
- package/package.json +2 -2
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
|
|
@@ -526,8 +520,10 @@ Methods are the orchestration layer of a slice.
|
|
|
526
520
|
- receive any number of arguments
|
|
527
521
|
- can return synchronous values or Promises
|
|
528
522
|
- can access:
|
|
529
|
-
|
|
530
|
-
|
|
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`)
|
|
531
527
|
|
|
532
528
|
Methods are not serialized, replayable, or represented in Redux DevTools action history.
|
|
533
529
|
|
|
@@ -547,20 +543,9 @@ Methods are designed for centralizing any slice-related logic:
|
|
|
547
543
|
Methods should NOT:
|
|
548
544
|
|
|
549
545
|
- include slice-unrelated logic.
|
|
546
|
+
- include UI layer logic.
|
|
550
547
|
- mutate state directly. use mutations instead.
|
|
551
548
|
|
|
552
|
-
Prefer:
|
|
553
|
-
|
|
554
|
-
```ts
|
|
555
|
-
this.increment(1);
|
|
556
|
-
```
|
|
557
|
-
|
|
558
|
-
Instead of:
|
|
559
|
-
|
|
560
|
-
```ts
|
|
561
|
-
this.getState().value++;
|
|
562
|
-
```
|
|
563
|
-
|
|
564
549
|
**Example:**
|
|
565
550
|
|
|
566
551
|
```ts
|
|
@@ -581,7 +566,7 @@ const counter = createSlice({
|
|
|
581
566
|
async incrementAfter(amount: number, delay = 1000) {
|
|
582
567
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
583
568
|
this.increment(amount);
|
|
584
|
-
this.
|
|
569
|
+
this.utils.logger.info("Counter incremented");
|
|
585
570
|
},
|
|
586
571
|
},
|
|
587
572
|
});
|
|
@@ -621,27 +606,27 @@ This currently not supported
|
|
|
621
606
|
|
|
622
607
|
Slices can be composed by registering other slice instances through the `children` property.
|
|
623
608
|
|
|
624
|
-
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.
|
|
625
610
|
|
|
626
611
|
```ts
|
|
627
612
|
import { products } from "./productsSlice";
|
|
628
613
|
import { categories } from "./categoriesSlice";
|
|
629
614
|
|
|
630
615
|
export const shop = createSlice({
|
|
631
|
-
|
|
616
|
+
name: "shop",
|
|
632
617
|
|
|
633
|
-
|
|
618
|
+
state: {},
|
|
634
619
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
620
|
+
children: {
|
|
621
|
+
products,
|
|
622
|
+
categories,
|
|
623
|
+
},
|
|
639
624
|
});
|
|
640
625
|
```
|
|
641
626
|
|
|
642
|
-
|
|
627
|
+
### Accessing Children through Parent Slice
|
|
643
628
|
|
|
644
|
-
Child slices are exposed directly on their parent slice.
|
|
629
|
+
Child slices are exposed directly on their parent slice instance.
|
|
645
630
|
|
|
646
631
|
```ts
|
|
647
632
|
shop.products.add(...)
|
|
@@ -650,7 +635,7 @@ shop.categories.create(...)
|
|
|
650
635
|
console.log(shop.products.getState());
|
|
651
636
|
```
|
|
652
637
|
|
|
653
|
-
Deeply nested slice
|
|
638
|
+
Deeply nested slice trees are fully supported, including recursive composition.
|
|
654
639
|
|
|
655
640
|
```ts
|
|
656
641
|
admin.users.permissions.grant(...);
|
|
@@ -658,6 +643,16 @@ admin.users.permissions.grant(...);
|
|
|
658
643
|
console.log(admin.users.permissions.getState());
|
|
659
644
|
```
|
|
660
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
|
+
|
|
661
656
|
### Reusing Slices
|
|
662
657
|
|
|
663
658
|
A slice can be mounted multiple times within the same tree.
|
|
@@ -915,7 +910,7 @@ console.log(store.counter.getState());
|
|
|
915
910
|
|
|
916
911
|
## Accessing Store from Slices
|
|
917
912
|
|
|
918
|
-
Every slice has access to the root store instance
|
|
913
|
+
Every slice has access to the root store instance via `slice.root`.
|
|
919
914
|
|
|
920
915
|
```ts
|
|
921
916
|
this.root.auth.getState().isAuthenticated;
|
|
@@ -927,68 +922,103 @@ this.root.auth.getState().isAuthenticated;
|
|
|
927
922
|
- avoiding circular imports
|
|
928
923
|
- application-wide orchestration
|
|
929
924
|
|
|
930
|
-
|
|
925
|
+
---
|
|
931
926
|
|
|
932
|
-
|
|
927
|
+
# Lineage & Clones
|
|
933
928
|
|
|
934
|
-
|
|
929
|
+
OrcheStore uses a lineage-based model for slice identity.
|
|
935
930
|
|
|
936
|
-
|
|
937
|
-
import { createStore } from "orchestore";
|
|
931
|
+
**Why?**
|
|
938
932
|
|
|
939
|
-
|
|
940
|
-
slices: {
|
|
941
|
-
counter,
|
|
942
|
-
},
|
|
943
|
-
});
|
|
933
|
+
Slices can be used in multiple places in the store tree.
|
|
944
934
|
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
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
|
|
953
953
|
|
|
954
954
|
```ts
|
|
955
|
-
|
|
956
|
-
this.root; // After: fully typed store
|
|
955
|
+
const clone = slice.prototype.clone();
|
|
957
956
|
```
|
|
958
957
|
|
|
959
|
-
|
|
958
|
+
The new instance:
|
|
960
959
|
|
|
961
|
-
-
|
|
962
|
-
-
|
|
963
|
-
|
|
964
|
-
-
|
|
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
|
|
965
965
|
|
|
966
|
-
|
|
966
|
+
### 2. Clone with state transformation
|
|
967
967
|
|
|
968
|
-
|
|
968
|
+
```ts
|
|
969
|
+
const clone = slice.prototype.clone((state) => newState);
|
|
970
|
+
```
|
|
969
971
|
|
|
970
|
-
|
|
972
|
+
The provided function receives the fully resolved initial state (including nested slices) and returns the modified state for the new instance.
|
|
971
973
|
|
|
972
|
-
|
|
974
|
+
The state transformer:
|
|
973
975
|
|
|
974
|
-
|
|
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)
|
|
975
980
|
|
|
976
|
-
|
|
981
|
+
**Example:**
|
|
977
982
|
|
|
978
|
-
|
|
983
|
+
```ts
|
|
984
|
+
const crudSlice = createSlice({
|
|
985
|
+
name: "CRUD-Slice",
|
|
979
986
|
|
|
980
|
-
|
|
987
|
+
state: {
|
|
988
|
+
endpoint: "",
|
|
989
|
+
},
|
|
981
990
|
|
|
982
|
-
|
|
991
|
+
children: {
|
|
992
|
+
pagination: paginationSlice,
|
|
993
|
+
dropdown: searchDropdownSlice,
|
|
994
|
+
},
|
|
995
|
+
});
|
|
983
996
|
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
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
|
+
```
|
|
988
1014
|
|
|
989
1015
|
## Automatic Cloning
|
|
990
1016
|
|
|
991
|
-
|
|
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.
|
|
992
1022
|
|
|
993
1023
|
```ts
|
|
994
1024
|
const paginationSlice = createSlice({ ... });
|
|
@@ -1023,36 +1053,49 @@ shopSlice.a !== adminSlice.a;
|
|
|
1023
1053
|
shopSlice.b !== adminSlice.a;
|
|
1024
1054
|
```
|
|
1025
1055
|
|
|
1026
|
-
Each instance
|
|
1056
|
+
Each instance has its own ownership context:
|
|
1027
1057
|
|
|
1028
1058
|
```ts
|
|
1029
|
-
shopSlice.a.path;
|
|
1030
|
-
shopSlice.b.path;
|
|
1031
|
-
adminSlice.a.path;
|
|
1059
|
+
shopSlice.a.path; // "shop.a"
|
|
1060
|
+
shopSlice.b.path; // "shop.b"
|
|
1061
|
+
adminSlice.a.path; // "admin.a"
|
|
1032
1062
|
```
|
|
1033
1063
|
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
## Manual Cloning
|
|
1064
|
+
### 2. Cloning a parent slice (cascade cloning)
|
|
1037
1065
|
|
|
1038
|
-
|
|
1066
|
+
When a parent slice is cloned or reused, all its nested children are automatically cloned as well.
|
|
1039
1067
|
|
|
1040
1068
|
```ts
|
|
1041
|
-
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();
|
|
1042
1084
|
```
|
|
1043
1085
|
|
|
1044
|
-
|
|
1086
|
+
Each clone receives its own independent subtree:
|
|
1045
1087
|
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
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.
|
|
1050
1093
|
|
|
1051
1094
|
## Inspecting a Lineage
|
|
1052
1095
|
|
|
1053
1096
|
**Get All Related Instances:**
|
|
1054
1097
|
|
|
1055
|
-
Returns every instance in the lineage, **including** the current
|
|
1098
|
+
Returns every instance in the lineage, **including** the current instance.
|
|
1056
1099
|
|
|
1057
1100
|
```ts
|
|
1058
1101
|
const lineage = slice.prototype.getLineage();
|
|
@@ -1064,9 +1107,9 @@ Useful for:
|
|
|
1064
1107
|
- inspecting mounted instances
|
|
1065
1108
|
- understanding tree distribution
|
|
1066
1109
|
|
|
1067
|
-
**Get Clones:**
|
|
1110
|
+
**Get Sibling Clones:**
|
|
1068
1111
|
|
|
1069
|
-
Returns
|
|
1112
|
+
Returns every sibling in the lineage, **except** the current instance.
|
|
1070
1113
|
|
|
1071
1114
|
```ts
|
|
1072
1115
|
const siblings = slice.prototype.getClones();
|
|
@@ -1080,8 +1123,6 @@ Useful for:
|
|
|
1080
1123
|
|
|
1081
1124
|
## Definition Type Checking
|
|
1082
1125
|
|
|
1083
|
-
You can determine whether two slices belong to the same lineage:
|
|
1084
|
-
|
|
1085
1126
|
You can check whether two slices belong to the same lineage:
|
|
1086
1127
|
|
|
1087
1128
|
```ts
|
|
@@ -1107,19 +1148,20 @@ slice2.prototype.isTypeOf(clone1); // false
|
|
|
1107
1148
|
|
|
1108
1149
|
## Summary
|
|
1109
1150
|
|
|
1110
|
-
- Reusing a slice automatically creates mounted clones.
|
|
1111
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.
|
|
1112
1154
|
- Every clone is isolated at runtime.
|
|
1113
1155
|
- All clones from the same definition belong to a shared lineage.
|
|
1114
1156
|
- `getLineage()` returns all instances in a lineage.
|
|
1115
|
-
- `getClones()` returns all related instances except the current
|
|
1157
|
+
- `getClones()` returns all related instances except the current instance.
|
|
1116
1158
|
- `isTypeOf()` checks whether two instances belong to the same lineage.
|
|
1117
1159
|
|
|
1118
1160
|
---
|
|
1119
1161
|
|
|
1120
|
-
#
|
|
1162
|
+
# Utilities
|
|
1121
1163
|
|
|
1122
|
-
|
|
1164
|
+
Application-wide utilities allow slices and stores to access shared runtime services through `utils`.
|
|
1123
1165
|
|
|
1124
1166
|
Common use cases include:
|
|
1125
1167
|
|
|
@@ -1130,28 +1172,33 @@ Common use cases include:
|
|
|
1130
1172
|
- runtime values that are difficult to access directly from slices
|
|
1131
1173
|
- integrations with React hooks and third-party libraries
|
|
1132
1174
|
|
|
1133
|
-
Utilities are registered using `
|
|
1175
|
+
Utilities are registered using `setUtils` and are accessible from any slice or the root store.
|
|
1134
1176
|
|
|
1135
|
-
## Accessing
|
|
1177
|
+
## Accessing Utilities
|
|
1136
1178
|
|
|
1137
1179
|
**Available:**
|
|
1138
1180
|
|
|
1139
|
-
- Through
|
|
1181
|
+
- Through exposed store and slice instances
|
|
1140
1182
|
|
|
1141
1183
|
```ts
|
|
1142
|
-
store.
|
|
1143
|
-
slice.
|
|
1184
|
+
store.utils;
|
|
1185
|
+
slice.utils;
|
|
1186
|
+
this.utils; // Inside slice methods and mutations
|
|
1144
1187
|
```
|
|
1145
1188
|
|
|
1146
|
-
-
|
|
1189
|
+
- Through `getUtils`
|
|
1147
1190
|
|
|
1148
1191
|
```ts
|
|
1149
|
-
|
|
1192
|
+
import { getUtils } from "orchestore";
|
|
1193
|
+
|
|
1194
|
+
const utils = getUtils();
|
|
1150
1195
|
```
|
|
1151
1196
|
|
|
1152
1197
|
## Utilities Type Extension
|
|
1153
1198
|
|
|
1154
|
-
|
|
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.
|
|
1155
1202
|
|
|
1156
1203
|
```ts
|
|
1157
1204
|
import type { NavigateFunction } from "react-router";
|
|
@@ -1159,7 +1206,7 @@ import type { NavigateFunction } from "react-router";
|
|
|
1159
1206
|
declare module "orchestore" {
|
|
1160
1207
|
namespace OrcheStore {
|
|
1161
1208
|
interface Slots {
|
|
1162
|
-
|
|
1209
|
+
utils: {
|
|
1163
1210
|
navigate: NavigateFunction;
|
|
1164
1211
|
|
|
1165
1212
|
notify(type: "info" | "error" | "success", message: string): void;
|
|
@@ -1169,30 +1216,43 @@ declare module "orchestore" {
|
|
|
1169
1216
|
}
|
|
1170
1217
|
```
|
|
1171
1218
|
|
|
1172
|
-
|
|
1173
|
-
this.global; // Before: any
|
|
1174
|
-
this.global; // After: fully typed
|
|
1175
|
-
```
|
|
1219
|
+
**Notes:**
|
|
1176
1220
|
|
|
1177
|
-
|
|
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`
|
|
1178
1241
|
|
|
1179
|
-
|
|
1180
|
-
- `null` and `undefined` are excluded automatically
|
|
1181
|
-
- `object | null | undefined` is equivalent to `object`
|
|
1182
|
-
- Invalid types fall back to `any`
|
|
1242
|
+
This provides consistent type safety and IntelliSense across all utility access points without requiring additional type declarations.
|
|
1183
1243
|
|
|
1184
1244
|
## Providing Runtime Utilities
|
|
1185
1245
|
|
|
1186
|
-
|
|
1246
|
+
Application-wide utility values can be registered or updated at runtime.
|
|
1187
1247
|
|
|
1188
1248
|
```ts
|
|
1189
1249
|
import { useEffect } from "react";
|
|
1190
1250
|
import { useNavigate } from "react-router";
|
|
1191
|
-
import {
|
|
1251
|
+
import { setUtils } from "orchestore";
|
|
1192
1252
|
import { feedbacks } from "./ui-feedbacks";
|
|
1193
1253
|
import { store } from "./store";
|
|
1194
1254
|
|
|
1195
|
-
|
|
1255
|
+
setUtils({
|
|
1196
1256
|
notify(type, message) {
|
|
1197
1257
|
feedbacks.notify(type, message);
|
|
1198
1258
|
},
|
|
@@ -1202,7 +1262,7 @@ export default function App() {
|
|
|
1202
1262
|
const navigate = useNavigate();
|
|
1203
1263
|
|
|
1204
1264
|
useEffect(() => {
|
|
1205
|
-
|
|
1265
|
+
setUtils({ navigate });
|
|
1206
1266
|
}, [navigate]);
|
|
1207
1267
|
|
|
1208
1268
|
return (
|
|
@@ -1213,9 +1273,9 @@ export default function App() {
|
|
|
1213
1273
|
}
|
|
1214
1274
|
```
|
|
1215
1275
|
|
|
1216
|
-
## Using
|
|
1276
|
+
## Using Utilities in Slices
|
|
1217
1277
|
|
|
1218
|
-
|
|
1278
|
+
Application-wide utilities can be used anywhere a slice instance is available.
|
|
1219
1279
|
|
|
1220
1280
|
```ts
|
|
1221
1281
|
methods: {
|
|
@@ -1225,13 +1285,13 @@ methods: {
|
|
|
1225
1285
|
|
|
1226
1286
|
const response = await api.users.add(data);
|
|
1227
1287
|
|
|
1228
|
-
this.
|
|
1288
|
+
this.utils.notify("success", "User added successfully!");
|
|
1229
1289
|
|
|
1230
1290
|
this.setLoading(false);
|
|
1231
1291
|
|
|
1232
|
-
this.
|
|
1292
|
+
this.utils.navigate("/users/" + response.id);
|
|
1233
1293
|
} catch (error) {
|
|
1234
|
-
this.
|
|
1294
|
+
this.utils.notify("error", "Failed to add user");
|
|
1235
1295
|
|
|
1236
1296
|
console.error(error);
|
|
1237
1297
|
}
|
|
@@ -1267,13 +1327,13 @@ const counter = createSlice({
|
|
|
1267
1327
|
},
|
|
1268
1328
|
|
|
1269
1329
|
children: {
|
|
1270
|
-
|
|
1271
|
-
|
|
1330
|
+
subCounter: createSlice({
|
|
1331
|
+
name: "subCounter",
|
|
1272
1332
|
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1333
|
+
state: {
|
|
1334
|
+
value: 0,
|
|
1335
|
+
},
|
|
1336
|
+
}),
|
|
1277
1337
|
},
|
|
1278
1338
|
});
|
|
1279
1339
|
```
|
|
@@ -1294,42 +1354,6 @@ counter.incrementAfter(amount: number, delay?: number): Promise<number>;
|
|
|
1294
1354
|
|
|
1295
1355
|
No manual type declarations required.
|
|
1296
1356
|
|
|
1297
|
-
## Framework Type Extensions
|
|
1298
|
-
|
|
1299
|
-
OrcheStore also exposes user-definable type slots through `OrcheStore.Slots`.
|
|
1300
|
-
|
|
1301
|
-
These slots allow application-specific types to be injected into the framework and become available everywhere with full type safety.
|
|
1302
|
-
|
|
1303
|
-
| Slot | Purpose |
|
|
1304
|
-
| -------------------------- | ----------------------- |
|
|
1305
|
-
| `OrcheStore.Slots.root` | Root store typing |
|
|
1306
|
-
| `OrcheStore.Slots.global` | Global utilities typing |
|
|
1307
|
-
|
|
1308
|
-
```ts
|
|
1309
|
-
declare module "orhestore" {
|
|
1310
|
-
namespace OrcheStore {
|
|
1311
|
-
interface Slots {
|
|
1312
|
-
root: typeof store; // Bugfix: Causes a circular type inference
|
|
1313
|
-
|
|
1314
|
-
global: {
|
|
1315
|
-
navigate: NavigateFunction;
|
|
1316
|
-
notify(type: "info" | "error" | "success", message: string): void;
|
|
1317
|
-
};
|
|
1318
|
-
}
|
|
1319
|
-
}
|
|
1320
|
-
}
|
|
1321
|
-
```
|
|
1322
|
-
|
|
1323
|
-
This provides full typing for APIs such as:
|
|
1324
|
-
|
|
1325
|
-
```ts
|
|
1326
|
-
this.root;
|
|
1327
|
-
this.global;
|
|
1328
|
-
|
|
1329
|
-
store.global;
|
|
1330
|
-
counter.global;
|
|
1331
|
-
```
|
|
1332
|
-
|
|
1333
1357
|
---
|
|
1334
1358
|
|
|
1335
1359
|
# Status
|