@biglogic/rgs 3.9.6 → 3.9.7
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/docs/README.md +479 -0
- package/docs/SUMMARY.md +55 -0
- package/docs/_config.yml +1 -0
- package/docs/markdown/api.md +381 -0
- package/docs/markdown/case-studies.md +69 -0
- package/docs/markdown/faq.md +53 -0
- package/docs/markdown/getting-started.md +68 -0
- package/docs/markdown/local-first-sync.md +146 -0
- package/docs/markdown/migration-guide.md +284 -0
- package/docs/markdown/persistence-and-safety.md +125 -0
- package/docs/markdown/philosophy.md +54 -0
- package/docs/markdown/plugin-sdk.md +161 -0
- package/docs/markdown/plugins-and-extensibility.md +82 -0
- package/docs/markdown/security-architecture.md +50 -0
- package/docs/markdown/the-magnetar-way.md +69 -0
- package/index.cjs +752 -281
- package/index.js +951 -482
- package/package.json +2 -1
|
@@ -0,0 +1,381 @@
|
|
|
1
|
+
# 📚 API Reference
|
|
2
|
+
|
|
3
|
+
Complete API reference for RGS (Argis) - Reactive Global 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
|
+
persistByDefault: true,
|
|
30
|
+
onError: (error, context) => {
|
|
31
|
+
console.error(`Error in ${context.operation}:`, error.message)
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
### `useStore`
|
|
39
|
+
|
|
40
|
+
React hook for reactive state.
|
|
41
|
+
|
|
42
|
+
```typescript
|
|
43
|
+
function useStore<T = unknown, S extends Record<string, unknown> = Record<string, unknown>>(
|
|
44
|
+
key: string,
|
|
45
|
+
store?: IStore<S>
|
|
46
|
+
): readonly [T | undefined, (val: T | StateUpdater<T>, options?: PersistOptions) => boolean]
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**Parameters:**
|
|
50
|
+
- `key` - State key to subscribe to
|
|
51
|
+
- `store` - Optional store instance
|
|
52
|
+
|
|
53
|
+
**Returns:** Tuple of `[value, setter]`
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
### `createStore`
|
|
58
|
+
|
|
59
|
+
Creates a new store instance.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
function createStore<S extends Record<string, unknown>>(
|
|
63
|
+
config?: StoreConfig<S>
|
|
64
|
+
): IStore<S>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
### `getStore`
|
|
70
|
+
|
|
71
|
+
Retrieves the currently active default store instance. Useful for accessing the store outside of React components or in utility functions.
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
function getStore(): IStore<Record<string, unknown>> | null
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Returns:** The active `IStore` or `null` if no store was initialized via `initState`.
|
|
78
|
+
|
|
79
|
+
---
|
|
80
|
+
|
|
81
|
+
## Store Interface (`IStore`)
|
|
82
|
+
|
|
83
|
+
### State Operations
|
|
84
|
+
|
|
85
|
+
#### `set`
|
|
86
|
+
|
|
87
|
+
Sets a value in the store.
|
|
88
|
+
|
|
89
|
+
```typescript
|
|
90
|
+
store.set<T>(key: string, value: T | StateUpdater<T>, options?: PersistOptions): boolean
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
#### `get`
|
|
94
|
+
|
|
95
|
+
Gets a value from the store.
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
store.get<T>(key: string): T | null
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
#### `remove` / `delete`
|
|
102
|
+
|
|
103
|
+
Removes a value from the store.
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
store.remove(key: string): boolean
|
|
107
|
+
store.delete(key: string): boolean
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
#### `deleteAll`
|
|
111
|
+
|
|
112
|
+
Removes all values from the store.
|
|
113
|
+
|
|
114
|
+
```typescript
|
|
115
|
+
store.deleteAll(): void
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### `list`
|
|
119
|
+
|
|
120
|
+
Returns all key-value pairs.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
store.list(): Record<string, unknown>
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
### Metadata Properties
|
|
129
|
+
|
|
130
|
+
#### `namespace`
|
|
131
|
+
|
|
132
|
+
The unique namespace of the store (read-only).
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
store.namespace: string
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
#### `userId`
|
|
139
|
+
|
|
140
|
+
The current user ID associated with the store for RBAC and audit logs (read-only).
|
|
141
|
+
|
|
142
|
+
```typescript
|
|
143
|
+
store.userId?: string
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
### Computed Values
|
|
149
|
+
|
|
150
|
+
#### `compute`
|
|
151
|
+
|
|
152
|
+
Creates or retrieves a computed (derived) value.
|
|
153
|
+
|
|
154
|
+
```typescript
|
|
155
|
+
store.compute<T>(key: string, selector: ComputedSelector<T>): T
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Example:**
|
|
159
|
+
```typescript
|
|
160
|
+
const fullName = store.compute('fullName', (get) => {
|
|
161
|
+
const first = get<string>('firstName')
|
|
162
|
+
const last = get<string>('lastName')
|
|
163
|
+
return `${first} ${last}`
|
|
164
|
+
})
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
> **Note:** RGS supports **nested computed dependencies**. A computed value can reactively depend on other computed values in the same store.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
### Watching Changes
|
|
172
|
+
|
|
173
|
+
#### `watch`
|
|
174
|
+
|
|
175
|
+
Watches for changes on a specific key.
|
|
176
|
+
|
|
177
|
+
```typescript
|
|
178
|
+
store.watch<T>(key: string, callback: WatcherCallback<T>): () => void
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
**Returns:** Unsubscribe function
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
### Transactions
|
|
186
|
+
|
|
187
|
+
#### `transaction`
|
|
188
|
+
|
|
189
|
+
Groups multiple operations into a single transaction.
|
|
190
|
+
|
|
191
|
+
```typescript
|
|
192
|
+
store.transaction(fn: () => void): void
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
---
|
|
196
|
+
|
|
197
|
+
### Middleware
|
|
198
|
+
|
|
199
|
+
#### `use`
|
|
200
|
+
|
|
201
|
+
Adds a middleware function.
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
store.use(middleware: Middleware): void
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
---
|
|
208
|
+
|
|
209
|
+
### Lifecycle
|
|
210
|
+
|
|
211
|
+
#### `destroy`
|
|
212
|
+
|
|
213
|
+
Destroys the store and cleans up resources.
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
store.destroy(): void
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
---
|
|
220
|
+
|
|
221
|
+
## Plugin API
|
|
222
|
+
|
|
223
|
+
### `_addPlugin`
|
|
224
|
+
|
|
225
|
+
Adds a plugin to the store.
|
|
226
|
+
|
|
227
|
+
```typescript
|
|
228
|
+
store._addPlugin(plugin: IPlugin): void
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### `_removePlugin`
|
|
232
|
+
|
|
233
|
+
Removes a plugin from the store.
|
|
234
|
+
|
|
235
|
+
```typescript
|
|
236
|
+
store._removePlugin(name: string): void
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### `_registerMethod`
|
|
240
|
+
|
|
241
|
+
Registers a custom method on the store.
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// New signature (recommended)
|
|
245
|
+
store._registerMethod(pluginName: string, methodName: string, fn: (...args) => unknown): void
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Plugins Property
|
|
251
|
+
|
|
252
|
+
Access plugin methods via `store.plugins`:
|
|
253
|
+
|
|
254
|
+
```typescript
|
|
255
|
+
store.plugins.undoRedo.undo()
|
|
256
|
+
store.plugins.undoRedo.redo()
|
|
257
|
+
store.plugins.counter.increment()
|
|
258
|
+
store.plugins.cloudSync.sync()
|
|
259
|
+
store.plugins.cloudSync.getStats()
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Configuration (`StoreConfig`)
|
|
265
|
+
|
|
266
|
+
```typescript
|
|
267
|
+
interface StoreConfig<S> {
|
|
268
|
+
/** Unique namespace for this store */
|
|
269
|
+
namespace?: string
|
|
270
|
+
/** Schema version */
|
|
271
|
+
version?: number
|
|
272
|
+
/** Suppress console warnings */
|
|
273
|
+
silent?: boolean
|
|
274
|
+
/** Debounce time for disk flush (default: 150ms) */
|
|
275
|
+
debounceTime?: number
|
|
276
|
+
/** Custom storage adapter */
|
|
277
|
+
storage?: CustomStorage | Storage
|
|
278
|
+
/** Migration function */
|
|
279
|
+
migrate?: (oldState: Record<string, unknown>, oldVersion: number) => S
|
|
280
|
+
/** Error handler */
|
|
281
|
+
onError?: (error: Error, context: { operation: string; key?: string }) => void
|
|
282
|
+
/** Max object size in bytes (default: 5MB) */
|
|
283
|
+
maxObjectSize?: number
|
|
284
|
+
/** Max total store size in bytes (default: 50MB) */
|
|
285
|
+
maxTotalSize?: number
|
|
286
|
+
/** AES-256-GCM encryption key */
|
|
287
|
+
encryptionKey?: EncryptionKey
|
|
288
|
+
/** Enable audit logging */
|
|
289
|
+
auditEnabled?: boolean
|
|
290
|
+
/** Current user ID for audit */
|
|
291
|
+
userId?: string
|
|
292
|
+
/** Enable input validation */
|
|
293
|
+
validateInput?: boolean
|
|
294
|
+
/** Access control rules */
|
|
295
|
+
accessRules?: Array<{
|
|
296
|
+
pattern: string | ((key: string, userId?: string) => boolean)
|
|
297
|
+
permissions: Permission[]
|
|
298
|
+
}>
|
|
299
|
+
/** Enable Immer (default: true) */
|
|
300
|
+
immer?: boolean
|
|
301
|
+
}
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
---
|
|
305
|
+
|
|
306
|
+
## Persistence Options (`PersistOptions`)
|
|
307
|
+
|
|
308
|
+
```typescript
|
|
309
|
+
interface PersistOptions {
|
|
310
|
+
/** Persist to storage (default: localStorage) */
|
|
311
|
+
persist?: boolean
|
|
312
|
+
/** Base64 encode the value */
|
|
313
|
+
encoded?: boolean
|
|
314
|
+
/** AES-256-GCM encryption */
|
|
315
|
+
encrypted?: boolean
|
|
316
|
+
/** Time-to-live in milliseconds */
|
|
317
|
+
ttl?: number
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
---
|
|
322
|
+
|
|
323
|
+
## Types
|
|
324
|
+
|
|
325
|
+
### `StateUpdater`
|
|
326
|
+
|
|
327
|
+
```typescript
|
|
328
|
+
type StateUpdater<T> = (draft: T) => void | T
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### `ComputedSelector`
|
|
332
|
+
|
|
333
|
+
```typescript
|
|
334
|
+
type ComputedSelector<T> = (get: <V>(key: string) => V | null) => T
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
### `WatcherCallback`
|
|
338
|
+
|
|
339
|
+
```typescript
|
|
340
|
+
type WatcherCallback<T> = (value: T | null) => void
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
### `Middleware`
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
type Middleware<T = unknown> = (key: string, value: T, meta: StoreMetadata) => void
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
---
|
|
350
|
+
|
|
351
|
+
## Security Types
|
|
352
|
+
|
|
353
|
+
### `Permission`
|
|
354
|
+
|
|
355
|
+
```typescript
|
|
356
|
+
type Permission = 'read' | 'write' | 'delete' | 'admin'
|
|
357
|
+
```
|
|
358
|
+
|
|
359
|
+
### `AccessRule`
|
|
360
|
+
|
|
361
|
+
```typescript
|
|
362
|
+
interface AccessRule {
|
|
363
|
+
pattern: string | ((key: string, userId?: string) => boolean)
|
|
364
|
+
permissions: Permission[]
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
370
|
+
## Plugin Hooks
|
|
371
|
+
|
|
372
|
+
| Hook | Description |
|
|
373
|
+
|------|-------------|
|
|
374
|
+
| `onInit` | Called when plugin is first initialized |
|
|
375
|
+
| `onInstall` | Called when plugin is added to store |
|
|
376
|
+
| `onBeforeSet` | Called before a value is set |
|
|
377
|
+
| `onSet` | Called after a value is set |
|
|
378
|
+
| `onGet` | Called when a value is retrieved |
|
|
379
|
+
| `onRemove` | Called when a value is removed |
|
|
380
|
+
| `onDestroy` | Called when store is destroyed |
|
|
381
|
+
| `onTransaction` | Called during a transaction |
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# 🛒 Chapter 6: Case Studies - Real Strategies
|
|
2
|
+
|
|
3
|
+
In this chapter, we put theory aside and see how RGS solves the problems that keep you up at night (or at least frustrated in the office).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 🍎 Case 1: High-Performance E-commerce
|
|
8
|
+
|
|
9
|
+
**The Problem**: A shopping cart with 50 items, complex sidebar filters, and a product list that must update without "jumping" the whole page.
|
|
10
|
+
|
|
11
|
+
**The RGS Strategy**:
|
|
12
|
+
|
|
13
|
+
1. **Atomic Filters**: Don't save the entire filters object. Use separate keys (`category`, `priceRange`, `search`). This way, if the user only changes the price, the search bar doesn't re-render.
|
|
14
|
+
2. **Persistent Cart**: Use `gstate` with a `cart` namespace.
|
|
15
|
+
3. **Computed State for Totals**:
|
|
16
|
+
|
|
17
|
+
```javascript
|
|
18
|
+
cartStore.compute('totalAmount', ['items'], (s) =>
|
|
19
|
+
s.items.reduce((acc, curr) => acc + curr.price, 0)
|
|
20
|
+
);
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
**Why do this?** Because the component displaying the total price updates *only* when the `items` array changes, not when the user's name or shipping address changes. **Zero waste.**
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 📊 Case 2: Real-time Dashboard (Sockets/Events)
|
|
28
|
+
|
|
29
|
+
**The Problem**: You receive thousands of updates via WebSocket (e.g., crypto prices or server notifications), and React can't keep up.
|
|
30
|
+
|
|
31
|
+
**The RGS Strategy**:
|
|
32
|
+
|
|
33
|
+
1. **Atomic Transactions**: In RGS, you can group updates.
|
|
34
|
+
|
|
35
|
+
```javascript
|
|
36
|
+
socket.on('bulk_update', (data) => {
|
|
37
|
+
store.transaction(() => {
|
|
38
|
+
data.forEach(item => store.set(`price_${item.id}`, item.price));
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
**Why do this?** Instead of triggering 100 React updates, the `transaction` triggers **only one** at the end. Your dashboard's performance will go from "tractor" to "Ferrari".
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
## 🏦 Case 3: Multi-Step Forms (User Onboarding)
|
|
48
|
+
|
|
49
|
+
**The Problem**: A signup form with 5 steps. If the user hits "Back" or refreshes the page, they lose everything.
|
|
50
|
+
|
|
51
|
+
**The RGS Strategy**:
|
|
52
|
+
|
|
53
|
+
1. Use a dedicated `gstate` called `onboarding`.
|
|
54
|
+
2. Enable `persist: true`.
|
|
55
|
+
3. At each step, just call `set('step1', values)`.
|
|
56
|
+
**Why do this?** Because you don't have to manage manual saving logic. When the user returns, the fields are already populated. At the very end (Step 5), call `store.destroy()` to clean up. Clean and elegant.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 🛡️ Message for Advanced Architects
|
|
61
|
+
|
|
62
|
+
*"But I could do all this with a custom cache and an event bus..."*
|
|
63
|
+
Sure you could. You could also walk to work instead of driving. But RGS is the car: it's tested, it handles edge cases (closed tabs, full storage, corrupted types), and it lets you focus on **business logic**, not infrastructure.
|
|
64
|
+
|
|
65
|
+
Stop reinventing the wheel. Use RGS.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
**Next step:** [FAQ: For the Skeptics and the Curious](faq.md)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# ❓ Chapter 7: FAQ - Architectural Insights
|
|
2
|
+
|
|
3
|
+
This section provides technical context for the design decisions behind RGS (Argis) - Reactive Global State.
|
|
4
|
+
|
|
5
|
+
## 1. "Why integrate Security and GDPR into the State layer?"
|
|
6
|
+
|
|
7
|
+
**The Rationale:** In enterprise environments, ensuring that every data access is authorized is critical. By integrating **RBAC** and **Auditing** directly into the store instance, we provide a "Secure-by-Default" architecture. This prevents common oversights where developers might forget to apply permission checks in custom middleware or component logic.
|
|
8
|
+
|
|
9
|
+
## 2. "How does RGS compare to the React Context API?"
|
|
10
|
+
|
|
11
|
+
**The Technical Difference:** Context is a dependency injection tool. When values change, React often triggers broad re-renders across the consumer tree. RGS uses a **Surgical Subscription** model. Only components observing a specific key are notified of changes, ensuring optimal performance even in data-heavy applications.
|
|
12
|
+
|
|
13
|
+
## 3. "Is there a performance overhead for Safety Features?"
|
|
14
|
+
|
|
15
|
+
**The Trade-off:** Capabilities like deep freezing (immutability) and sanitization do introduce a small computational cost (typically 1-2ms). We believe this is a worthwhile investment to prevent accidental state mutations and security vulnerabilities. For performance-critical scenarios (like high-frequency animations), these features can be selectively disabled.
|
|
16
|
+
|
|
17
|
+
## 4. "How is Type Safety handled with string keys?"
|
|
18
|
+
|
|
19
|
+
**The Approach:** String keys provide the flexibility needed for dynamic and runtime-generated state namespaces. For developers requiring strict type safety, RGS offers the `gstate` factory and `GStatePlugins` augmentation, allowing you to define a fully typed interface for your store and its plugins.
|
|
20
|
+
|
|
21
|
+
## 5. "What logic dictates the use of 'Ghost Stores'?"
|
|
22
|
+
|
|
23
|
+
**Operational Resilience:** Many applications experience race conditions during hydration or initialization. Instead of allowing the application to crash due to an uninitialized reference, RGS returns a protective Proxy. This Proxy logs a developer warning while providing a safe fallback, ensuring the user interface remains functional while the developer addresses the initialization sequence.
|
|
24
|
+
|
|
25
|
+
## 6. "Is it compatible with modern React patterns (SSR/Next.js)?"
|
|
26
|
+
|
|
27
|
+
**Yes.** RGS is built on top of `useSyncExternalStore` and is fully compatible with Concurrent Rendering and Server-Side Rendering (SSR). It works seamlessly with Next.js, Remix, and other modern frameworks without hydration mismatch issues.
|
|
28
|
+
|
|
29
|
+
## 7. "Where do the best practices and improvements come from?"
|
|
30
|
+
|
|
31
|
+
**The Process:** All improvements and best practices are based on:
|
|
32
|
+
|
|
33
|
+
- **Official React Documentation** - useSyncExternalStore for SSR, hooks rules, React 18/19 features
|
|
34
|
+
- **TypeScript Best Practices** - Type safety patterns, generics, strict mode
|
|
35
|
+
- **Security Standards** - OWASP for XSS prevention, AES-256-GCM encryption, RBAC patterns
|
|
36
|
+
- **Community Libraries** - Patterns from Zustand, Redux, Jotai for plugin architecture
|
|
37
|
+
- **Enterprise Patterns** - Error handling, multi-store isolation, GDPR compliance
|
|
38
|
+
|
|
39
|
+
Key fixes (like security isolation per-store, Immer optional loading) come from common issues in similar libraries.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## 🛑 Best Practices: Maximizing Reliability
|
|
44
|
+
|
|
45
|
+
1. **State Granularity**: Use RGS for global, persistent, or secured data. For transient UI state (like toggle transitions), standard `useState` is more appropriate.
|
|
46
|
+
2. **Namespace Management**: Always define a unique namespace for your store to prevent data collisions in shared domain environments.
|
|
47
|
+
3. **Rule Validation**: Ensure your RBAC rules are tested against your expected key patterns to maintain a robust security posture.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## 👋 Conclusion
|
|
52
|
+
|
|
53
|
+
RGS is designed for teams that prioritize long-term maintainability and system stability. By handling the complexities of security and persistence at the architectural level, we allow developers to focus on building features with confidence.
|
|
@@ -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, useStore } from '@biglogic/rgs';
|
|
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 '@biglogic/rgs';
|
|
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 '@biglogic/rgs';
|
|
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](the-magnetar-way.md)
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
# 🚀 Chapter 10: Local-First Sync Engine
|
|
2
|
+
|
|
3
|
+
RGS now includes a powerful **Local-First Sync Engine** that makes your app work offline by default and automatically synchronize when connectivity is restored.
|
|
4
|
+
|
|
5
|
+
## Why Local-First?
|
|
6
|
+
|
|
7
|
+
Traditional apps require an internet connection to work. Local-First apps work immediately with local data and sync in the background when possible.
|
|
8
|
+
|
|
9
|
+
### Benefits
|
|
10
|
+
|
|
11
|
+
- **Instant Load** - No waiting for server responses
|
|
12
|
+
- **Works Offline** - App functions without internet
|
|
13
|
+
- **Better UX** - No loading spinners for data
|
|
14
|
+
- **Conflict Resolution** - Smart merge strategies
|
|
15
|
+
|
|
16
|
+
## Quick Start
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
import { gstate, useSyncedState } from '@biglogic/rgs'
|
|
20
|
+
|
|
21
|
+
// Create store with sync enabled
|
|
22
|
+
const store = gstate({
|
|
23
|
+
todos: [],
|
|
24
|
+
user: null
|
|
25
|
+
}, {
|
|
26
|
+
namespace: 'myapp',
|
|
27
|
+
sync: {
|
|
28
|
+
endpoint: 'https://api.example.com/sync',
|
|
29
|
+
// Use a getter function for secure token retrieval
|
|
30
|
+
authToken: () => localStorage.getItem('auth_token'),
|
|
31
|
+
autoSyncInterval: 30000, // Sync every 30s
|
|
32
|
+
syncOnReconnect: true // Auto-sync when back online
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
// Use in React components
|
|
37
|
+
function TodoList() {
|
|
38
|
+
const [todos, setTodos] = useSyncedState('todos')
|
|
39
|
+
|
|
40
|
+
// Add todo - automatically queued for sync
|
|
41
|
+
const addTodo = (text) => {
|
|
42
|
+
setTodos([...todos, { id: Date.now(), text }])
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return <div>{/* ... */}</div>
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
## Configuration Options
|
|
50
|
+
|
|
51
|
+
| Option | Type | Default | Description |
|
|
52
|
+
|--------|------|---------|-------------|
|
|
53
|
+
| `endpoint` | `string` | required | Remote sync server URL |
|
|
54
|
+
| `authToken` | `string` or `() => string \| null` | - | Authentication token or getter function for secure retrieval |
|
|
55
|
+
| `strategy` | `string` | `'last-write-wins'` | Conflict resolution |
|
|
56
|
+
| `autoSyncInterval` | `number` | `30000` | Auto-sync interval (ms) |
|
|
57
|
+
| `syncOnReconnect` | `boolean` | `true` | Sync on network restore |
|
|
58
|
+
| `debounceTime` | `number` | `1000` | Batch changes (ms) |
|
|
59
|
+
| `maxRetries` | `number` | `3` | Failed sync retries |
|
|
60
|
+
| `onConflict` | `function` | - | Custom conflict handler |
|
|
61
|
+
| `onSync` | `function` | - | Sync completion callback |
|
|
62
|
+
|
|
63
|
+
## Conflict Resolution Strategies
|
|
64
|
+
|
|
65
|
+
### 1. Last-Write-Wins (Default)
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
sync: { strategy: 'last-write-wins' }
|
|
69
|
+
```
|
|
70
|
+
Latest timestamp wins - simplest strategy.
|
|
71
|
+
|
|
72
|
+
### 2. Server-Wins
|
|
73
|
+
|
|
74
|
+
```typescript
|
|
75
|
+
sync: { strategy: 'server-wins' }
|
|
76
|
+
```
|
|
77
|
+
Always prefer remote values - useful for read-heavy apps.
|
|
78
|
+
|
|
79
|
+
### 3. Client-Wins
|
|
80
|
+
|
|
81
|
+
```typescript
|
|
82
|
+
sync: { strategy: 'client-wins' }
|
|
83
|
+
```
|
|
84
|
+
Always prefer local values - useful for write-heavy apps.
|
|
85
|
+
|
|
86
|
+
### 4. Custom Merge
|
|
87
|
+
|
|
88
|
+
```typescript
|
|
89
|
+
sync: {
|
|
90
|
+
strategy: 'merge',
|
|
91
|
+
onConflict: (conflict) => {
|
|
92
|
+
// Custom logic for merging
|
|
93
|
+
return {
|
|
94
|
+
action: 'merge',
|
|
95
|
+
value: { /* merged result */ }
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Hook API
|
|
102
|
+
|
|
103
|
+
### useSyncedState
|
|
104
|
+
|
|
105
|
+
```typescript
|
|
106
|
+
const [value, setValue, syncState] = useSyncedState('key')
|
|
107
|
+
|
|
108
|
+
// syncState contains:
|
|
109
|
+
// - isOnline: boolean
|
|
110
|
+
// - isSyncing: boolean
|
|
111
|
+
// - pendingChanges: number
|
|
112
|
+
// - conflicts: number
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### useSyncStatus
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const status = useSyncStatus()
|
|
119
|
+
// Global sync status across all stores
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Manual Sync Control
|
|
123
|
+
|
|
124
|
+
```typescript
|
|
125
|
+
// Force sync
|
|
126
|
+
await store.plugins.sync.flush()
|
|
127
|
+
|
|
128
|
+
// Get sync state
|
|
129
|
+
const state = store.plugins.sync.getState()
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Integration with Persistence
|
|
133
|
+
|
|
134
|
+
The Sync Engine works seamlessly with RGS's existing persistence layer:
|
|
135
|
+
|
|
136
|
+
- **Local Storage** - Data persists locally first
|
|
137
|
+
- **IndexedDB** - For larger datasets
|
|
138
|
+
- **Cloud Sync** - Optional remote backup
|
|
139
|
+
|
|
140
|
+
Your data survives browser refresh, works offline, and stays synchronized across devices.
|
|
141
|
+
|
|
142
|
+
## Next Steps
|
|
143
|
+
|
|
144
|
+
- Learn about [Security Architecture](security-architecture.md)
|
|
145
|
+
- Explore [Plugin SDK](plugin-sdk.md)
|
|
146
|
+
- Check [Migration Guide](migration-guide.md)
|