@biglogic/rgs 2.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/CODEOWNERS +1 -0
- package/dist/CONTRIBUTING.md +65 -0
- package/dist/COPYRIGHT.md +4 -0
- package/dist/LICENSE +9 -0
- package/dist/README.md +267 -0
- package/dist/SECURITY.md +3 -0
- package/dist/advanced.d.ts +9 -0
- package/dist/advanced.js +1 -0
- package/dist/core/advanced.d.ts +5 -0
- package/dist/core/async.d.ts +8 -0
- package/dist/core/hooks.d.ts +8 -0
- package/dist/core/security.d.ts +54 -0
- package/dist/core/store.d.ts +7 -0
- package/dist/core/types.d.ts +134 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +1 -0
- package/dist/markdown/SUMMARY.md +55 -0
- package/dist/markdown/api.md +342 -0
- package/dist/markdown/chapters/01-philosophy.md +54 -0
- package/dist/markdown/chapters/02-getting-started.md +68 -0
- package/dist/markdown/chapters/03-the-magnetar-way.md +62 -0
- package/dist/markdown/chapters/04-persistence-and-safety.md +84 -0
- package/dist/markdown/chapters/05-plugin-sdk.md +290 -0
- package/dist/markdown/chapters/05-plugins-and-extensibility.md +174 -0
- package/dist/markdown/chapters/06-case-studies.md +69 -0
- package/dist/markdown/chapters/07-faq.md +53 -0
- package/dist/markdown/chapters/08-migration-guide.md +206 -0
- package/dist/package.json +81 -0
- package/dist/plugins/index.d.ts +13 -0
- package/dist/plugins/official/analytics.plugin.d.ts +9 -0
- package/dist/plugins/official/debug.plugin.d.ts +2 -0
- package/dist/plugins/official/devtools.plugin.d.ts +4 -0
- package/dist/plugins/official/guard.plugin.d.ts +2 -0
- package/dist/plugins/official/immer.plugin.d.ts +2 -0
- package/dist/plugins/official/schema.plugin.d.ts +2 -0
- package/dist/plugins/official/snapshot.plugin.d.ts +2 -0
- package/dist/plugins/official/sync.plugin.d.ts +4 -0
- package/dist/plugins/official/undo-redo.plugin.d.ts +4 -0
- package/package.json +81 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# π Argis (RGS) - React Globo State: The Final Guide
|
|
2
|
+
|
|
3
|
+
Welcome to the definitive documentation for **React Globo State (RGS)**. If you are here, you're likely tired of endless boilerplate, complex configurations, and state management tools that seem to require a PhD in rocket science.
|
|
4
|
+
|
|
5
|
+
This documentation is written for everyone: from **easy setup** for those who just want things to work, to **advanced implementation** for those who want to master the engine.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## πΊοΈ Summary
|
|
10
|
+
|
|
11
|
+
- **[The Philosophy: Panzer vs. Bicycle](chapters/01-philosophy.md)**
|
|
12
|
+
- Reliability and Security as First-Class Citizens.
|
|
13
|
+
- The "Ironclad" Core: Simplicity meets Power.
|
|
14
|
+
|
|
15
|
+
- **[Quick Start: 30-Second Setup](chapters/02-getting-started.md)**
|
|
16
|
+
- Deploying the RGS Panzer in your React project.
|
|
17
|
+
|
|
18
|
+
- **[The Magnetar Way: One-Liner Power](chapters/03-the-magnetar-way.md)**
|
|
19
|
+
- Creating stores and hooks simultaneously. Types included.
|
|
20
|
+
|
|
21
|
+
- **[Persistence and Safety](chapters/04-persistence-and-safety.md)**
|
|
22
|
+
- Never lose user data again (without localStorage headaches).
|
|
23
|
+
- Native immutability with Immer (Stellar Engine).
|
|
24
|
+
|
|
25
|
+
- **[Ecosystem and Plugins](chapters/05-plugins-and-extensibility.md)**
|
|
26
|
+
- DevTools, Cross-Tab Sync, Analytics, and Typed Plugins.
|
|
27
|
+
|
|
28
|
+
- **[Plugin SDK: Build Your Own Extensions](chapters/05-plugin-sdk.md)**
|
|
29
|
+
- Create custom plugins with lifecycle hooks.
|
|
30
|
+
- Register methods via `store.plugins`.
|
|
31
|
+
- Full API reference and examples.
|
|
32
|
+
|
|
33
|
+
- **[Case Studies: Real World Strategies](chapters/06-case-studies.md)**
|
|
34
|
+
- **E-commerce**: Cart isolation and atomic updates.
|
|
35
|
+
- **Dashboards**: Multi-store strategies and complex flows.
|
|
36
|
+
|
|
37
|
+
- **[Architectural Insights (FAQ)](chapters/07-faq.md)**
|
|
38
|
+
- Honest answers on security, performance, and Proxies.
|
|
39
|
+
|
|
40
|
+
- **[Migration Guide](chapters/08-migration-guide.md)**
|
|
41
|
+
- Upgrading to latest version (Enterprise Isolation)
|
|
42
|
+
- Upgrading to previous version (`secure` β `encoded`)
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Reference
|
|
47
|
+
|
|
48
|
+
- **[API Reference](api.md)**
|
|
49
|
+
- Complete API documentation
|
|
50
|
+
- Type definitions
|
|
51
|
+
- Plugin hooks
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
> *"Make things simple, but not simpler than necessary."* β RGS Team
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# π API Reference
|
|
2
|
+
|
|
3
|
+
Complete API reference for RGS (Argis) - React Globo State.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Core Functions
|
|
8
|
+
|
|
9
|
+
### `initState`
|
|
10
|
+
|
|
11
|
+
Initializes a global store instance.
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
function initState<S extends Record<string, unknown>>(
|
|
15
|
+
config?: StoreConfig<S>
|
|
16
|
+
): IStore<S>
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**Parameters:**
|
|
20
|
+
- `config` - Optional store configuration
|
|
21
|
+
|
|
22
|
+
**Returns:** `IStore<S>`
|
|
23
|
+
|
|
24
|
+
**Example:**
|
|
25
|
+
```typescript
|
|
26
|
+
const store = initState({
|
|
27
|
+
namespace: 'myApp',
|
|
28
|
+
version: 1,
|
|
29
|
+
persist: true
|
|
30
|
+
})
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
---
|
|
34
|
+
|
|
35
|
+
### `useStore`
|
|
36
|
+
|
|
37
|
+
React hook for reactive state.
|
|
38
|
+
|
|
39
|
+
```typescript
|
|
40
|
+
function useStore<T = unknown, S extends Record<string, unknown> = Record<string, unknown>>(
|
|
41
|
+
key: string,
|
|
42
|
+
store?: IStore<S>
|
|
43
|
+
): readonly [T | undefined, (val: T | StateUpdater<T>, options?: PersistOptions) => boolean]
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Parameters:**
|
|
47
|
+
- `key` - State key to subscribe to
|
|
48
|
+
- `store` - Optional store instance
|
|
49
|
+
|
|
50
|
+
**Returns:** Tuple of `[value, setter]`
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
### `createStore`
|
|
55
|
+
|
|
56
|
+
Creates a new store instance.
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
function createStore<S extends Record<string, unknown>>(
|
|
60
|
+
config?: StoreConfig<S>
|
|
61
|
+
): IStore<S>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Store Interface (`IStore`)
|
|
67
|
+
|
|
68
|
+
### State Operations
|
|
69
|
+
|
|
70
|
+
#### `set`
|
|
71
|
+
|
|
72
|
+
Sets a value in the store.
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
store.set<T>(key: string, value: T | StateUpdater<T>, options?: PersistOptions): boolean
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
#### `get`
|
|
79
|
+
|
|
80
|
+
Gets a value from the store.
|
|
81
|
+
|
|
82
|
+
```typescript
|
|
83
|
+
store.get<T>(key: string): T | null
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
#### `remove` / `delete`
|
|
87
|
+
|
|
88
|
+
Removes a value from the store.
|
|
89
|
+
|
|
90
|
+
```typescript
|
|
91
|
+
store.remove(key: string): boolean
|
|
92
|
+
store.delete(key: string): boolean
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### `deleteAll`
|
|
96
|
+
|
|
97
|
+
Removes all values from the store.
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
store.deleteAll(): void
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
#### `list`
|
|
104
|
+
|
|
105
|
+
Returns all key-value pairs.
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
store.list(): Record<string, unknown>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
### Computed Values
|
|
114
|
+
|
|
115
|
+
#### `compute`
|
|
116
|
+
|
|
117
|
+
Creates or retrieves a computed (derived) value.
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
store.compute<T>(key: string, selector: ComputedSelector<T>): T
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
**Example:**
|
|
124
|
+
```typescript
|
|
125
|
+
const fullName = store.compute('fullName', (get) => {
|
|
126
|
+
const first = get<string>('firstName')
|
|
127
|
+
const last = get<string>('lastName')
|
|
128
|
+
return `${first} ${last}`
|
|
129
|
+
})
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
### Watching Changes
|
|
135
|
+
|
|
136
|
+
#### `watch`
|
|
137
|
+
|
|
138
|
+
Watches for changes on a specific key.
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
store.watch<T>(key: string, callback: WatcherCallback<T>): () => void
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**Returns:** Unsubscribe function
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### Transactions
|
|
149
|
+
|
|
150
|
+
#### `transaction`
|
|
151
|
+
|
|
152
|
+
Groups multiple operations into a single transaction.
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
store.transaction(fn: () => void): void
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
### Middleware
|
|
161
|
+
|
|
162
|
+
#### `use`
|
|
163
|
+
|
|
164
|
+
Adds a middleware function.
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
store.use(middleware: Middleware): void
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
### Lifecycle
|
|
173
|
+
|
|
174
|
+
#### `destroy`
|
|
175
|
+
|
|
176
|
+
Destroys the store and cleans up resources.
|
|
177
|
+
|
|
178
|
+
```typescript
|
|
179
|
+
store.destroy(): void
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
---
|
|
183
|
+
|
|
184
|
+
## Plugin API
|
|
185
|
+
|
|
186
|
+
### `_addPlugin`
|
|
187
|
+
|
|
188
|
+
Adds a plugin to the store.
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
store._addPlugin(plugin: IPlugin): void
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
### `_removePlugin`
|
|
195
|
+
|
|
196
|
+
Removes a plugin from the store.
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
store._removePlugin(name: string): void
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### `_registerMethod`
|
|
203
|
+
|
|
204
|
+
Registers a custom method on the store.
|
|
205
|
+
|
|
206
|
+
```typescript
|
|
207
|
+
// New signature (recommended)
|
|
208
|
+
store._registerMethod(pluginName: string, methodName: string, fn: (...args) => unknown): void
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
213
|
+
## Plugins Property
|
|
214
|
+
|
|
215
|
+
Access plugin methods via `store.plugins`:
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
store.plugins.undoRedo.undo()
|
|
219
|
+
store.plugins.undoRedo.redo()
|
|
220
|
+
store.plugins.counter.increment()
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Configuration (`StoreConfig`)
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
interface StoreConfig<S> {
|
|
229
|
+
/** Unique namespace for this store */
|
|
230
|
+
namespace?: string
|
|
231
|
+
/** Schema version */
|
|
232
|
+
version?: number
|
|
233
|
+
/** Suppress console warnings */
|
|
234
|
+
silent?: boolean
|
|
235
|
+
/** Debounce time for disk flush (default: 150ms) */
|
|
236
|
+
debounceTime?: number
|
|
237
|
+
/** Custom storage adapter */
|
|
238
|
+
storage?: CustomStorage | Storage
|
|
239
|
+
/** Migration function */
|
|
240
|
+
migrate?: (oldState: Record<string, unknown>, oldVersion: number) => S
|
|
241
|
+
/** Error handler */
|
|
242
|
+
onError?: (error: Error, context: { operation: string; key?: string }) => void
|
|
243
|
+
/** Max object size in bytes (default: 5MB) */
|
|
244
|
+
maxObjectSize?: number
|
|
245
|
+
/** Max total store size in bytes (default: 50MB) */
|
|
246
|
+
maxTotalSize?: number
|
|
247
|
+
/** AES-256-GCM encryption key */
|
|
248
|
+
encryptionKey?: EncryptionKey
|
|
249
|
+
/** Enable audit logging */
|
|
250
|
+
auditEnabled?: boolean
|
|
251
|
+
/** Current user ID for audit */
|
|
252
|
+
userId?: string
|
|
253
|
+
/** Enable input validation */
|
|
254
|
+
validateInput?: boolean
|
|
255
|
+
/** Access control rules */
|
|
256
|
+
accessRules?: Array<{
|
|
257
|
+
pattern: string | ((key: string, userId?: string) => boolean)
|
|
258
|
+
permissions: Permission[]
|
|
259
|
+
}>
|
|
260
|
+
/** Enable Immer (default: true) */
|
|
261
|
+
immer?: boolean
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
267
|
+
## Persistence Options (`PersistOptions`)
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
interface PersistOptions {
|
|
271
|
+
/** Persist to storage (default: localStorage) */
|
|
272
|
+
persist?: boolean
|
|
273
|
+
/** Base64 encode the value */
|
|
274
|
+
encoded?: boolean
|
|
275
|
+
/** AES-256-GCM encryption */
|
|
276
|
+
encrypted?: boolean
|
|
277
|
+
/** Time-to-live in milliseconds */
|
|
278
|
+
ttl?: number
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
---
|
|
283
|
+
|
|
284
|
+
## Types
|
|
285
|
+
|
|
286
|
+
### `StateUpdater`
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
type StateUpdater<T> = (draft: T) => void | T
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
### `ComputedSelector`
|
|
293
|
+
|
|
294
|
+
```typescript
|
|
295
|
+
type ComputedSelector<T> = (get: <V>(key: string) => V | null) => T
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
### `WatcherCallback`
|
|
299
|
+
|
|
300
|
+
```typescript
|
|
301
|
+
type WatcherCallback<T> = (value: T | null) => void
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### `Middleware`
|
|
305
|
+
|
|
306
|
+
```typescript
|
|
307
|
+
type Middleware<T = unknown> = (key: string, value: T, meta: StoreMetadata) => void
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
## Security Types
|
|
313
|
+
|
|
314
|
+
### `Permission`
|
|
315
|
+
|
|
316
|
+
```typescript
|
|
317
|
+
type Permission = 'read' | 'write' | 'delete' | 'admin'
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### `AccessRule`
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
interface AccessRule {
|
|
324
|
+
pattern: string | ((key: string, userId?: string) => boolean)
|
|
325
|
+
permissions: Permission[]
|
|
326
|
+
}
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## Plugin Hooks
|
|
332
|
+
|
|
333
|
+
| Hook | Description |
|
|
334
|
+
|------|-------------|
|
|
335
|
+
| `onInit` | Called when plugin is first initialized |
|
|
336
|
+
| `onInstall` | Called when plugin is added to store |
|
|
337
|
+
| `onBeforeSet` | Called before a value is set |
|
|
338
|
+
| `onSet` | Called after a value is set |
|
|
339
|
+
| `onGet` | Called when a value is retrieved |
|
|
340
|
+
| `onRemove` | Called when a value is removed |
|
|
341
|
+
| `onDestroy` | Called when store is destroyed |
|
|
342
|
+
| `onTransaction` | Called during a transaction |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# π§ Chapter 1: The Philosophy - Panzer vs. Bicycle
|
|
2
|
+
|
|
3
|
+
You are a developer. You have deadlines. You have bugs. But more importantly, you have a reputation to protect.
|
|
4
|
+
|
|
5
|
+
## π Choose Your Vehicle
|
|
6
|
+
|
|
7
|
+
Most state managers focus on being "lightweight" or "easy." **RGS (Argis)** has evolved beyond that. It is a framework designed for high-stakes applications where data leakage is a disaster and production uptime is non-negotiable.
|
|
8
|
+
|
|
9
|
+
### The Bicycle (Standard Hooks/Context)
|
|
10
|
+
|
|
11
|
+
If you just need a counter for a simple todo list, use a basic hook or `useState`. You don't need a Panzer to go to the grocery store. It's light, it's fast to set up, but it offers zero protection when the road gets rough.
|
|
12
|
+
|
|
13
|
+
### The Panzer (RGS)
|
|
14
|
+
|
|
15
|
+
If you are building an Enterprise platform where:
|
|
16
|
+
|
|
17
|
+
- **Isolation is King**: User A's data must never collide with User B's state.
|
|
18
|
+
- **Fail-Safe Security**: RBAC (Role-Based Access Control) is built into the state layer.
|
|
19
|
+
- **Resilience**: The app must survive uninitialized states without crashing (`Ghost Stores`).
|
|
20
|
+
|
|
21
|
+
...then you need a Panzer. **You need RGS.**
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## π Surgical Performance: Atomic Access
|
|
26
|
+
|
|
27
|
+
RGS was born from a "crazy" idea: **State should be global by nature, but atomic by access.**
|
|
28
|
+
|
|
29
|
+
### π΄ Camp A: Context API (The Re-render Rain)
|
|
30
|
+
|
|
31
|
+
In React Context, any change in the provider forces a global re-render of the entire tree consuming it. Change a theme brand color? The whole product list re-renders.
|
|
32
|
+
|
|
33
|
+
### π’ Camp B: RGS (The Sniper)
|
|
34
|
+
|
|
35
|
+
In RGS, only the component observing a specific key wakes up. Change the 'cart'? Only the cart icon updates. Total silence for the rest of the application.
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## π‘οΈ Defensive Engineering
|
|
40
|
+
|
|
41
|
+
We don't just manage state; we protect it:
|
|
42
|
+
|
|
43
|
+
- **Zero Boilerplate**: No actions, no reducers. Just `.set()` and Move on.
|
|
44
|
+
- **Fail-Closed Security**: If a pattern is defined but doesn't explicitly match the action, access is denied. No exceptions.
|
|
45
|
+
- **Ghost Stores**: If a component accesses an uninitialized store, we return a Proxy that warns you but **keeps the UI alive**. No more `ReferenceError: store is not defined`.
|
|
46
|
+
|
|
47
|
+
## π‘ Case Study: "No-Stress" E-commerce
|
|
48
|
+
|
|
49
|
+
Imagine a shopping cart.
|
|
50
|
+
|
|
51
|
+
- **Standard Approach**: The user adds a sock, and the entire product list (2000 items) re-renders because they share a Context.
|
|
52
|
+
- **RGS Approach**: Adds the item with `set('cart', [...])`. Only the tiny cart badge updates. 3ms execution time. Happy client, happy developer.
|
|
53
|
+
|
|
54
|
+
**Next step:** [Quick Start: 30-Second Setup](02-getting-started.md)
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# β‘ Chapter 2: Quick Start - From Zero to State in 30 Seconds
|
|
2
|
+
|
|
3
|
+
Stop wasting time on boilerplate. Here is how you deploy the RGS Panzer in your React project.
|
|
4
|
+
|
|
5
|
+
## 1. Installation
|
|
6
|
+
|
|
7
|
+
The engine is lightweight but armored.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install rgs
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## 2. Initialization: The "Big Bang"
|
|
14
|
+
|
|
15
|
+
In your main entry file (e.g., `main.tsx` or `App.tsx`), wake up the engine once.
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { initState } from 'argis';
|
|
19
|
+
|
|
20
|
+
// Initialize with optional settings
|
|
21
|
+
initState({
|
|
22
|
+
namespace: 'my-awesome-app',
|
|
23
|
+
persistence: true // Optional: Saves everything to localStorage automatically
|
|
24
|
+
});
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 3. Usage: Instant Reactions
|
|
28
|
+
|
|
29
|
+
Use the `useStore` hook. No providers, no wrappers. Just raw, atomic power.
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { useStore } from 'argis';
|
|
33
|
+
|
|
34
|
+
function Counter() {
|
|
35
|
+
// If 'count' doesn't exist yet, it defaults to undefined. Easy.
|
|
36
|
+
const [count, setCount] = useStore<number>('count');
|
|
37
|
+
|
|
38
|
+
return (
|
|
39
|
+
<div className="card">
|
|
40
|
+
<h1>Power Level: {count ?? 0}</h1>
|
|
41
|
+
<button onClick={() => setCount((prev) => (prev || 0) + 1)}>
|
|
42
|
+
Boost Power π₯
|
|
43
|
+
</button>
|
|
44
|
+
</div>
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## π§ What just happened?
|
|
50
|
+
|
|
51
|
+
- **Reactive Subscription**: `useStore('count')` tells React to watch the 'count' key. Surgical updates only.
|
|
52
|
+
- **Global Scope**: `setCount` updates the value everywhere in the app, instantly.
|
|
53
|
+
- **Resilient Nature**: If you access a key that hasn't been set yet, RGS returns `undefined` gracefully instead of throwing a tantrum.
|
|
54
|
+
|
|
55
|
+
## π¨ Pro Tip: Direct Store Access
|
|
56
|
+
|
|
57
|
+
Need to access state outside of React components? Simple.
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
import { getStore } from 'argis';
|
|
61
|
+
|
|
62
|
+
const value = getStore()?.get('count');
|
|
63
|
+
getStore()?.set('count', 9001);
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
**Next step:** [The Magnetar Way: One-Liner Power](03-the-magnetar-way.md)
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# π Chapter 3: The Magnetar Way - Peak Simplicity
|
|
2
|
+
|
|
3
|
+
If you are a fan of "Clean Code" or just hate writing imports, the **Magnetar** method is your new best friend.
|
|
4
|
+
|
|
5
|
+
It's a single function that creates the store and the hook simultaneously. **Zero config, 100% typed.**
|
|
6
|
+
|
|
7
|
+
## π οΈ The "Master" Example
|
|
8
|
+
|
|
9
|
+
Let's create a User Profile module.
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { gstate } from 'argis';
|
|
13
|
+
|
|
14
|
+
// 1. Define the state and create everything in ONE shot
|
|
15
|
+
export const useUser = gstate({
|
|
16
|
+
name: 'Guest',
|
|
17
|
+
isLogged: false,
|
|
18
|
+
preferences: { theme: 'dark', lang: 'en' }
|
|
19
|
+
}, 'user_module'); // Optional namespace for persistence
|
|
20
|
+
|
|
21
|
+
// 2. Use it anywhere
|
|
22
|
+
function ProfileHeader() {
|
|
23
|
+
const [name, setName] = useUser('name');
|
|
24
|
+
// Note: 'setName' is already typed! It only accepts strings.
|
|
25
|
+
|
|
26
|
+
return <h1>Hello, {name}!</h1>;
|
|
27
|
+
}
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## π Why Magnetar Rocks
|
|
31
|
+
|
|
32
|
+
1. **Inferred Types**: No need to define `<string>`. TypeScript looks at the initial value you passed to `gstate` and figures it out.
|
|
33
|
+
2. **Auto-Namespace**: If you enable persistence, Magnetar uses the name you gave it (`user_module`) to isolate data in the local database.
|
|
34
|
+
3. **Store Methods Included**: The object returned by `gstate` is not just a hook. It's the store itself!
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// You can access the store OUTSIDE components!
|
|
38
|
+
const currentUser = useUser.get('name');
|
|
39
|
+
useUser.set('isLogged', true);
|
|
40
|
+
|
|
41
|
+
// You can even compute data on the fly (Computed State)
|
|
42
|
+
useUser.compute('fullName', ['firstName', 'lastName'], (s) => `${s.firstName} ${s.lastName}`);
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## π Production Strategy: "The Store Folder"
|
|
46
|
+
|
|
47
|
+
Don't scatter stores everywhere. Create a `src/stores` folder and put your Magnetar files there:
|
|
48
|
+
|
|
49
|
+
- `auth.ts`
|
|
50
|
+
- `cart.ts`
|
|
51
|
+
- `settings.ts`
|
|
52
|
+
|
|
53
|
+
Then import them whenever needed. Itβs clean, fast, and professional.
|
|
54
|
+
|
|
55
|
+
## π§ Reflection for the Senior Dev
|
|
56
|
+
|
|
57
|
+
*"Wait, does Magnetar create a new store every time?"*
|
|
58
|
+
Yes, and that's the point! You can have isolated micro-stores for every domain of your app, while still keeping the ability to make them talk to each other. Itβs the evolution of the "Atomic State" concept.
|
|
59
|
+
|
|
60
|
+
---
|
|
61
|
+
|
|
62
|
+
**Next step:** [Persistence and Safety: Data Never Dies](04-persistence-and-safety.md)
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# ποΈ Chapter 4: Persistence and Safety - Built like a Tank
|
|
2
|
+
|
|
3
|
+
In a real-world application, state that vanishes on page refresh is a developer failure. RGS handles data saving **intelligently**.
|
|
4
|
+
|
|
5
|
+
## πΎ Persistence: "Set and Forget"
|
|
6
|
+
|
|
7
|
+
When you initialize RGS or a Magnetar, you can enable persistence. But it's not a simple `localStorage.set`.
|
|
8
|
+
|
|
9
|
+
```typescript
|
|
10
|
+
initState({
|
|
11
|
+
persist: true,
|
|
12
|
+
storage: 'local' // or 'session' or a custom adapter
|
|
13
|
+
});
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### What happens under the hood?
|
|
17
|
+
|
|
18
|
+
```mermaid
|
|
19
|
+
sequenceDiagram
|
|
20
|
+
participant C as Component
|
|
21
|
+
participant RGS as Globo State
|
|
22
|
+
participant S as LocalStorage
|
|
23
|
+
|
|
24
|
+
Note over C,RGS: User clicks a button
|
|
25
|
+
C->>RGS: set('count', 10)
|
|
26
|
+
RGS-->>C: Update UI (Instant)
|
|
27
|
+
|
|
28
|
+
Note over RGS: Waiting 300ms (Debounce)
|
|
29
|
+
|
|
30
|
+
RGS->>S: Write to Disk
|
|
31
|
+
Note right of S: Data saved successfully
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
- **Debouncing**: If you update the state 100 times in one second, RGS writes to the disk only once at the end. This saves battery life and browser performance.
|
|
35
|
+
- **Selective Persistence**: Don't want to save everything? You can tell RGS which keys to ignore or which ones to save only temporarily.
|
|
36
|
+
|
|
37
|
+
## π‘οΈ Immutability: The Immer Shield
|
|
38
|
+
|
|
39
|
+
Have you ever had bugs where state changed "mysteriously" because you mutated an array directly? RGS uses **Immer** at its core (the Stellar Engine).
|
|
40
|
+
|
|
41
|
+
**Dangerous Code (Standard JS):**
|
|
42
|
+
|
|
43
|
+
```javascript
|
|
44
|
+
const user = store.get('user');
|
|
45
|
+
user.permissions.push('admin'); // BOOM! You mutated the original without triggering a re-render.
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
**The RGS Way:**
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
store.set('user', (draft) => {
|
|
52
|
+
draft.permissions.push('admin'); // SAFE! RGS creates an immutable copy for you.
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## π΅οΈ Validation: Schema Plugin
|
|
57
|
+
|
|
58
|
+
Never trust data coming back from the server or saved in the browser 6 months ago. Use the **schemaPlugin**.
|
|
59
|
+
|
|
60
|
+
```typescript
|
|
61
|
+
import { schemaPlugin } from 'argis';
|
|
62
|
+
import { z } from 'zod'; // Recommended!
|
|
63
|
+
|
|
64
|
+
const store = initState();
|
|
65
|
+
store._addPlugin(schemaPlugin({
|
|
66
|
+
price: (val) => typeof val === 'number' && val > 0,
|
|
67
|
+
email: (val) => val.includes('@')
|
|
68
|
+
}));
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If anyone tries to `set('price', -50)`, RGS will block the operation and warn you. **Clean State = Happy App.**
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## π‘ Case Study: The Cart that Never Lost an Item
|
|
76
|
+
|
|
77
|
+
**Challenge**: User adds products, closes the browser, comes back after two days. The cart must still be there.
|
|
78
|
+
**RGS Solution**:
|
|
79
|
+
|
|
80
|
+
1. Enable `persist: true` in the cart store.
|
|
81
|
+
2. Use `createAsyncStore` (Chapter 5) to sync local data with the remote database as soon as a connection is available.
|
|
82
|
+
3. Result? 5-star UX.
|
|
83
|
+
|
|
84
|
+
**Next step:** [Ecosystem and Plugins: Extending the Power](05-plugins-and-extensibility.md)
|