@biglogic/rgs 3.1.0 → 3.5.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.
Files changed (83) hide show
  1. package/README.md +24 -6
  2. package/SECURITY.md +1 -1
  3. package/advanced.js +1 -1
  4. package/core/hooks.d.ts +2 -3
  5. package/core/persistence.d.ts +23 -0
  6. package/core/plugins.d.ts +8 -0
  7. package/core/reactivity.d.ts +19 -0
  8. package/core/types.d.ts +1 -1
  9. package/index.js +1 -1
  10. package/package.json +81 -77
  11. package/rgs-extension.vsix +0 -0
  12. package/core/advanced.js +0 -4
  13. package/core/async.js +0 -40
  14. package/core/hooks.js +0 -52
  15. package/core/security.js +0 -124
  16. package/core/store.js +0 -595
  17. package/core/types.js +0 -5
  18. package/core/utils.js +0 -72
  19. package/examples/README.md +0 -41
  20. package/examples/async-data-fetch/UserLoader.d.ts +0 -12
  21. package/examples/async-data-fetch/UserLoader.js +0 -10
  22. package/examples/async-data-fetch/UserLoader.ts +0 -30
  23. package/examples/basic-counter/CounterComponent.d.ts +0 -2
  24. package/examples/basic-counter/CounterComponent.js +0 -7
  25. package/examples/basic-counter/CounterComponent.tsx +0 -22
  26. package/examples/basic-counter/CounterStore.d.ts +0 -7
  27. package/examples/basic-counter/CounterStore.js +0 -13
  28. package/examples/basic-counter/CounterStore.ts +0 -25
  29. package/examples/big-data-indexeddb/BigDataStore.d.ts +0 -10
  30. package/examples/big-data-indexeddb/BigDataStore.js +0 -32
  31. package/examples/big-data-indexeddb/BigDataStore.ts +0 -60
  32. package/examples/global-theme/ThemeManager.d.ts +0 -7
  33. package/examples/global-theme/ThemeManager.js +0 -13
  34. package/examples/global-theme/ThemeManager.ts +0 -32
  35. package/examples/hybrid-cloud-sync/HybridStore.d.ts +0 -19
  36. package/examples/hybrid-cloud-sync/HybridStore.js +0 -44
  37. package/examples/hybrid-cloud-sync/HybridStore.ts +0 -78
  38. package/examples/persistent-cart/CartStore.d.ts +0 -13
  39. package/examples/persistent-cart/CartStore.js +0 -23
  40. package/examples/persistent-cart/CartStore.ts +0 -41
  41. package/examples/rbac-dashboard/DashboardStore.d.ts +0 -47
  42. package/examples/rbac-dashboard/DashboardStore.js +0 -31
  43. package/examples/rbac-dashboard/DashboardStore.ts +0 -46
  44. package/examples/secure-auth/AuthStore.d.ts +0 -14
  45. package/examples/secure-auth/AuthStore.js +0 -20
  46. package/examples/secure-auth/AuthStore.ts +0 -36
  47. package/examples/security-best-practices/SecurityStore.d.ts +0 -22
  48. package/examples/security-best-practices/SecurityStore.js +0 -30
  49. package/examples/security-best-practices/SecurityStore.ts +0 -75
  50. package/examples/stress-tests/StressStore.d.ts +0 -41
  51. package/examples/stress-tests/StressStore.js +0 -41
  52. package/examples/stress-tests/StressStore.ts +0 -61
  53. package/examples/super-easy/EasyStore.d.ts +0 -44
  54. package/examples/super-easy/EasyStore.js +0 -24
  55. package/examples/super-easy/EasyStore.ts +0 -61
  56. package/examples/undo-redo-editor/EditorStore.d.ts +0 -9
  57. package/examples/undo-redo-editor/EditorStore.js +0 -13
  58. package/examples/undo-redo-editor/EditorStore.ts +0 -28
  59. package/markdown/SUMMARY.md +0 -59
  60. package/markdown/api.md +0 -381
  61. package/markdown/chapters/01-philosophy.md +0 -54
  62. package/markdown/chapters/02-getting-started.md +0 -68
  63. package/markdown/chapters/03-the-magnetar-way.md +0 -69
  64. package/markdown/chapters/04-persistence-and-safety.md +0 -125
  65. package/markdown/chapters/05-plugin-sdk.md +0 -290
  66. package/markdown/chapters/05-plugins-and-extensibility.md +0 -190
  67. package/markdown/chapters/06-case-studies.md +0 -69
  68. package/markdown/chapters/07-faq.md +0 -53
  69. package/markdown/chapters/08-migration-guide.md +0 -253
  70. package/markdown/chapters/09-security-architecture.md +0 -40
  71. package/plugins/index.js +0 -34
  72. package/plugins/official/analytics.plugin.js +0 -19
  73. package/plugins/official/cloud-sync.plugin.js +0 -117
  74. package/plugins/official/debug.plugin.js +0 -62
  75. package/plugins/official/devtools.plugin.js +0 -28
  76. package/plugins/official/guard.plugin.js +0 -15
  77. package/plugins/official/immer.plugin.js +0 -10
  78. package/plugins/official/indexeddb.plugin.js +0 -102
  79. package/plugins/official/schema.plugin.js +0 -16
  80. package/plugins/official/snapshot.plugin.js +0 -27
  81. package/plugins/official/sync.plugin.js +0 -37
  82. package/plugins/official/undo-redo.plugin.js +0 -61
  83. package/tsconfig.tsbuildinfo +0 -1
@@ -1,253 +0,0 @@
1
- # 🔄 Migration Guide
2
-
3
- ## Upgrading from Previous Versions
4
-
5
- ### Security: `secure` → `encoded`
6
-
7
- **IMPORTANT:** The `secure` option has been deprecated in favor of `encoded` to clarify that it only applies **base64 encoding**, not encryption.
8
-
9
- #### ❌ Old Code (Deprecated)
10
-
11
- ```typescript
12
- store.set('apiKey', 'secret123', {
13
- persist: true,
14
- secure: true // ⚠️ DEPRECATED: This is NOT encryption!
15
- })
16
- ```
17
-
18
- #### ✅ New Code (Recommended)
19
-
20
- ```typescript
21
- store.set('apiKey', 'secret123', {
22
- persist: true,
23
- encoded: true // ✅ Clear: This is base64 encoding
24
- })
25
- ```
26
-
27
- #### Backward Compatibility
28
-
29
- Your old code will **still work**, but you'll see a deprecation warning in TypeScript:
30
-
31
- ```typescript
32
- /** @deprecated Use 'encoded' instead. 'secure' only applies base64 encoding, not encryption. */
33
- secure?: boolean
34
- ```
35
-
36
- #### Why This Change?
37
-
38
- Base64 encoding is **NOT encryption**. It's trivial to decode:
39
-
40
- ```javascript
41
- // Anyone can decode base64
42
- const encoded = btoa(JSON.stringify({ secret: 'password123' }))
43
- const decoded = JSON.parse(atob(encoded)) // { secret: 'password123' }
44
- ```
45
-
46
- **For real security**, use proper encryption libraries like:
47
-
48
- - `crypto-js` (AES encryption)
49
- - `tweetnacl` (NaCl encryption)
50
- - Web Crypto API (`crypto.subtle`)
51
-
52
- ---
53
-
54
- ### Error Handling: `onError` Callback
55
-
56
- **NEW:** You can now catch and handle errors from plugins, hydration, and other operations.
57
-
58
- #### Example: Custom Error Logging
59
-
60
- ```typescript
61
- import { initState, useStore } from '@biglogic/rgs'
62
-
63
- const store = initState({
64
- namespace: 'myapp',
65
- onError: (error, context) => {
66
- // Send to your error tracking service
67
- console.error(`[gState Error] ${context.operation}:`, error)
68
-
69
- if (context.operation === 'hydration') {
70
- // Handle corrupted localStorage
71
- localStorage.clear()
72
- alert('Storage corrupted, resetting...')
73
- }
74
-
75
- if (context.operation.startsWith('plugin:')) {
76
- // Handle plugin crashes
77
- Sentry.captureException(error, { tags: { plugin: context.operation } })
78
- }
79
- }
80
- })
81
- ```
82
-
83
- #### Error Context
84
-
85
- ```typescript
86
- interface ErrorContext {
87
- operation: string // 'hydration', 'plugin:name:hook', 'set'
88
- key?: string // State key (if applicable)
89
- }
90
- ```
91
-
92
- ---
93
-
94
- ### Performance: `maxObjectSize` Warning
95
-
96
- **NEW:** Get warned when storing objects larger than a configurable limit (default: 5MB).
97
-
98
- #### Example: Custom Size Limit
99
-
100
- ```typescript
101
- import { createStore } from '@biglogic/rgs'
102
-
103
- const store = createStore({
104
- maxObjectSize: 10 * 1024 * 1024, // 10MB limit
105
- onError: (error, context) => {
106
- if (context.operation === 'set') {
107
- console.warn(`Large object detected in key: ${context.key}`)
108
- }
109
- }
110
- })
111
-
112
- // This will trigger a warning if > 10MB
113
- store.set('bigData', hugeArray)
114
- ```
115
-
116
- #### Disable Size Checking
117
-
118
- ```typescript
119
- const store = createStore({
120
- maxObjectSize: 0 // Disable size warnings
121
- })
122
- ```
123
-
124
- ---
125
-
126
- ## Upgrading to Latest Version (The Enterprise Update)
127
-
128
- **IMPORTANT:** This release introduces a fundamental shift towards **Multi-Store Isolation**. Security rules and GDPR consents are now instance-bound rather than global.
129
-
130
- ### 1. Security: Global → Instance-Specific
131
-
132
- In previous versions, security rules were shared globally. Now, each store instance maintains its own rules for better isolation in micro-frontend environments.
133
-
134
- #### ❌ Deprecated Global Methods
135
-
136
- ```typescript
137
- import { addAccessRule, recordConsent } from '@biglogic/rgs'
138
-
139
- // ⚠️ DEPRECATED: These affect the 'default' store only and are less isolated
140
- addAccessRule('user_*', ['read', 'write'])
141
- ```
142
-
143
- #### ✅ Recommended Instance Methods
144
-
145
- ```typescript
146
- const store = createStore({ namespace: 'my-isolated-app' })
147
-
148
- // ✅ Use the instance methods
149
- store.addAccessRule('user_*', ['read', 'write'])
150
- store.recordConsent('user123', 'marketing', true)
151
- ```
152
-
153
- ### 2. Operational Resilience: Ghost Stores
154
-
155
- **NEW:** If you access a store that hasn't finished its initialization (e.g., during slow hydration), RGS now returns a **Ghost Store Proxy**.
156
-
157
- - **Behavior:** It prevents application crashes by providing a safe fallback.
158
- - **Developer Warning:** It logs a detailed warning in the console so you can fix the initialization sequence.
159
-
160
- ### 3. Performance: Regex Caching
161
-
162
- **NEW:** Permission checks now use an internal **Regex Cache** per instance.
163
-
164
- - **Why?** Avoids the overhead of re-compiling regex strings on every `.get()` or `.set()` call.
165
- - **Impact:** Significant performance boost for applications with high-frequency state updates and complex RBAC rules.
166
-
167
- ### 4. Advanced Plugin Typing
168
-
169
- **NEW:** Introducing `GStatePlugins` for Module Augmentation.
170
-
171
- - You can now define types for your custom plugins to get full IDE autocomplete.
172
-
173
- ---
174
-
175
- ## v2.9.5: The Architecture & Safety Update (2026-02-16)
176
-
177
- This release focuses on improving developer ergonomics, security visibility, and complex dependency handling.
178
-
179
- ### 1. Nested Computed Dependencies
180
- **NEW:** Computed values can now re-trigger based on other computed values.
181
-
182
- ```typescript
183
- store.compute('tax', (get) => (get<number>('subtotal') || 0) * 0.2)
184
- store.compute('total', (get) => (get<number>('subtotal') || 0) + (get<number>('tax') || 0))
185
- ```
186
-
187
- ### 2. Direct Store Access: `getStore()`
188
- **NEW:** A top-level utility to retrieve the default store without React hooks.
189
-
190
- ```typescript
191
- import { getStore } from '@biglogic/rgs'
192
-
193
- export const toggleTheme = () => {
194
- const store = getStore()
195
- if (store) store.set('mode', 'dark')
196
- }
197
- ```
198
-
199
- ### 3. Exposed Metadata: `namespace` and `userId`
200
- **NEW:** Store instances now expose their identifying properties as read-only getters.
201
-
202
- ```typescript
203
- const store = createStore({ namespace: 'auth-vault', userId: 'user-001' })
204
- console.log(store.namespace) // 'auth-vault'
205
- console.log(store.userId) // 'user-001'
206
- ```
207
-
208
- ### 4. High-Volume & Hybrid Sync (Plugins)
209
- **NEW:** Support for GB-scale storage and Remote Cloud Backups.
210
-
211
- - **IndexedDB Plugin**: Replaces localStorage for massive browser datasets.
212
- - **Cloud Sync Plugin**: Differential synchronization to MongoDB, Firebase, or any SQL backend.
213
-
214
- ```typescript
215
- // Example: Manual Cloud Sync
216
- const result = await store.plugins.cloudSync.sync()
217
- console.log('Stats:', store.plugins.cloudSync.getStats())
218
- ```
219
-
220
- ---
221
-
222
- ## Breaking Changes
223
-
224
- ### 🔒 Security Isolation
225
-
226
- If you relied on `addAccessRule()` from the global export to affect a `createStore()` instance, you must now call `store.addAccessRule()` on that specific instance.
227
-
228
- ---
229
-
230
- ## Recommended Actions
231
-
232
- ### 1. Migrate Security Calls to Store Instances
233
-
234
- **Priority:** High (if using multiple stores)
235
-
236
- **Effort:** Low
237
-
238
- ### 2. Implement `GStatePlugins` for Custom Plugins
239
-
240
- **Priority:** Medium (Developer Experience)
241
-
242
- **Effort:** Low
243
-
244
- ---
245
-
246
- ## Need Help?
247
-
248
- - **Issues:** [GitHub Issues](https://github.com/dpassariello/rgs/issues)
249
- - **Docs:** [Galaxy Documentation](../SUMMARY.md)
250
-
251
- ---
252
-
253
- ## Last updated: 2026-02-16
@@ -1,40 +0,0 @@
1
- # Security Architecture & Hardening
2
-
3
- ## Overview
4
- React Globo State (RGS) is designed with a "Security-First" philosophy. Our architecture ensures that global state is not only reactive but protected against common web vulnerabilities and unauthorized access.
5
-
6
- ## 1. Data Sanitization (XSS Defense)
7
- The `sanitizeValue` utility provides a robust baseline defense by stripping malicious content from strings and objects before they enter the store.
8
-
9
- - **Scheme Blocking**: Specifically blocks `javascript:`, `vbscript:`, and `data:text/html` schemes.
10
- - **Tag Removal**: Automatically removes dangerous HTML tags such as `<script>`, `<iframe>`, `<form>`, and `<meta>`.
11
- - **Entity Removal**: Strips HTML entities (`&#...;`) to prevent obfuscation-based bypasses.
12
-
13
- ## 2. Advanced Deep Cloning
14
- To ensure state immutability and prevent unintended side effects, RGS uses an intelligent cloning engine:
15
- - **Native structuredClone**: Leverages the browser's native API for maximum performance.
16
- - **Support for Collections**: Extends cloning capabilities to `Map` and `Set` objects.
17
- - **Circular Reference Protection**: Uses `WeakMap` to handle complex nested structures safely.
18
-
19
- ## 3. Cryptography (AES-256-GCM)
20
- The security module uses the Web Crypto API to provide high-performance, authenticated encryption:
21
- - **AES-GCM**: Provides both confidentiality and integrity verification.
22
- - **GCM (Galois/Counter Mode)**: Ensures that data has not been tampered with during storage.
23
-
24
- ## 4. RBAC (Role-Based Access Control)
25
- RGS supports fine-grained access rules:
26
- - **Fail-Closed Design**: Access is denied by default if any rules are defined.
27
- - **Regex Caching**: Store instances cache compiled regular expressions for ultra-fast permission checks.
28
-
29
- ## 5. Security Best Practices
30
- For real-world implementations, refer to the `examples/security-best-practices` directory, which covers:
31
- - **Encryption Key Management**: Using `generateEncryptionKey()` for secure key generation.
32
- - **Audit Logging**: Tracking all store modifications for compliance.
33
- - **GDPR Compliance**: Managing user consent and data export/deletion.
34
-
35
- ## Summary of 2.9.5 Enhancements
36
- - Robust regex patterns for `sanitizeValue`.
37
- - Recursive sanitization for plain objects.
38
- - `Map` and `Set` support in `deepClone`.
39
- - **Exposed Metadata**: Store instances now expose read-only `namespace` and `userId`.
40
- - **Direct Store Access**: Added `getStore()` utility for non-React contexts.
package/plugins/index.js DELETED
@@ -1,34 +0,0 @@
1
- export { immerPlugin } from "./official/immer.plugin";
2
- export { undoRedoPlugin } from "./official/undo-redo.plugin";
3
- export { schemaPlugin } from "./official/schema.plugin";
4
- export { devToolsPlugin } from "./official/devtools.plugin";
5
- export { snapshotPlugin } from "./official/snapshot.plugin";
6
- export { guardPlugin } from "./official/guard.plugin";
7
- export { analyticsPlugin } from "./official/analytics.plugin";
8
- export { syncPlugin } from "./official/sync.plugin";
9
- export { debugPlugin } from "./official/debug.plugin";
10
- export { indexedDBPlugin } from "./official/indexeddb.plugin";
11
- export { cloudSyncPlugin, createMongoAdapter, createFirestoreAdapter, createSqlRestAdapter } from "./official/cloud-sync.plugin";
12
- export const loggerPlugin = (options) => ({
13
- name: 'gstate-logger',
14
- hooks: {
15
- onSet: ({ key, value, version }) => {
16
- const time = new Date().toLocaleTimeString(), groupLabel = `[gState] SET: ${key} (v${version}) @ ${time}`;
17
- if (options?.collapsed)
18
- console.groupCollapsed(groupLabel);
19
- else
20
- console.group(groupLabel);
21
- console.info('%c Value:', 'color: #4CAF50; font-weight: bold;', value);
22
- console.groupEnd();
23
- },
24
- onRemove: ({ key }) => {
25
- console.warn(`[gState] REMOVED: ${key}`);
26
- },
27
- onTransaction: ({ key }) => {
28
- if (key === 'START')
29
- console.group('── TRANSACTION START ──');
30
- else
31
- console.groupEnd();
32
- }
33
- }
34
- });
@@ -1,19 +0,0 @@
1
- export const analyticsPlugin = (options) => ({
2
- name: 'gstate-analytics',
3
- hooks: {
4
- onSet: ({ key, value }) => {
5
- if (!key)
6
- return;
7
- if (!options.keys || options.keys.includes(key)) {
8
- options.provider({ key, value, action: 'SET' });
9
- }
10
- },
11
- onRemove: ({ key }) => {
12
- if (!key)
13
- return;
14
- if (!options.keys || options.keys.includes(key)) {
15
- options.provider({ key, value: null, action: 'REMOVE' });
16
- }
17
- }
18
- }
19
- });
@@ -1,117 +0,0 @@
1
- export const cloudSyncPlugin = (options) => {
2
- const { adapter, autoSyncInterval } = options;
3
- const lastSyncedVersions = new Map();
4
- const stats = {
5
- lastSyncTimestamp: null,
6
- totalKeysSynced: 0,
7
- totalBytesSynced: 0,
8
- syncCount: 0,
9
- lastDuration: 0,
10
- errors: 0
11
- };
12
- let timer = null;
13
- return {
14
- name: 'cloudSync',
15
- hooks: {
16
- onInstall: ({ store }) => {
17
- store._registerMethod('cloudSync', 'sync', async () => {
18
- const startTime = performance.now();
19
- const dirtyData = {};
20
- let bytesCount = 0;
21
- try {
22
- const allData = store.list();
23
- const keys = Object.keys(allData);
24
- for (const key of keys) {
25
- const currentVersion = store._getVersion?.(key) || 0;
26
- const lastVersion = lastSyncedVersions.get(key) || 0;
27
- if (currentVersion > lastVersion) {
28
- const val = allData[key];
29
- dirtyData[key] = val;
30
- bytesCount += JSON.stringify(val).length;
31
- lastSyncedVersions.set(key, currentVersion);
32
- }
33
- }
34
- if (Object.keys(dirtyData).length === 0)
35
- return { status: 'no-change', stats };
36
- const success = await adapter.save(dirtyData);
37
- if (success) {
38
- stats.lastSyncTimestamp = Date.now();
39
- stats.totalKeysSynced += Object.keys(dirtyData).length;
40
- stats.totalBytesSynced += bytesCount;
41
- stats.syncCount++;
42
- stats.lastDuration = performance.now() - startTime;
43
- if (options.onSync)
44
- options.onSync(stats);
45
- return { status: 'success', stats };
46
- }
47
- else {
48
- throw new Error(`Adapter ${adapter.name} failed to save.`);
49
- }
50
- }
51
- catch (err) {
52
- stats.errors++;
53
- console.error(`[gState] Cloud Sync Failed (${adapter.name}):`, err);
54
- return { status: 'error', error: String(err), stats };
55
- }
56
- });
57
- store._registerMethod('cloudSync', 'getStats', () => stats);
58
- if (autoSyncInterval && autoSyncInterval > 0) {
59
- timer = setInterval(() => {
60
- const plugins = store.plugins;
61
- const cs = plugins.cloudSync;
62
- if (cs)
63
- cs.sync();
64
- }, autoSyncInterval);
65
- }
66
- },
67
- onDestroy: () => {
68
- if (timer)
69
- clearInterval(timer);
70
- }
71
- }
72
- };
73
- };
74
- export const createMongoAdapter = (apiUrl, apiKey) => ({
75
- name: 'MongoDB-Atlas',
76
- save: async (data) => {
77
- const response = await fetch(`${apiUrl}/action/updateOne`, {
78
- method: 'POST',
79
- headers: { 'Content-Type': 'application/json', 'api-key': apiKey },
80
- body: JSON.stringify({
81
- dataSource: 'Cluster0',
82
- database: 'rgs_cloud',
83
- collection: 'user_states',
84
- filter: { id: 'global_state' },
85
- update: { $set: { data, updatedAt: Date.now() } },
86
- upsert: true
87
- })
88
- });
89
- return response.ok;
90
- }
91
- });
92
- export const createFirestoreAdapter = (db, docPath) => ({
93
- name: 'Firebase-Firestore',
94
- save: async (data) => {
95
- try {
96
- console.log('[Mock] Firestore Syncing:', data);
97
- return true;
98
- }
99
- catch (e) {
100
- return false;
101
- }
102
- }
103
- });
104
- export const createSqlRestAdapter = (endpoint, authToken) => ({
105
- name: 'SQL-REST-API',
106
- save: async (data) => {
107
- const response = await fetch(endpoint, {
108
- method: 'PATCH',
109
- headers: {
110
- 'Content-Type': 'application/json',
111
- 'Authorization': `Bearer ${authToken}`
112
- },
113
- body: JSON.stringify(data)
114
- });
115
- return response.ok;
116
- }
117
- });
@@ -1,62 +0,0 @@
1
- export const debugPlugin = () => {
2
- if (process.env.NODE_ENV === 'production') {
3
- return { name: 'gstate-debug-noop', hooks: {} };
4
- }
5
- return {
6
- name: 'gstate-debug',
7
- hooks: {
8
- onInstall: ({ store }) => {
9
- if (typeof window !== 'undefined') {
10
- window.gstate = {
11
- list: () => {
12
- console.log('[gState] Current state:', store.list());
13
- return store.list();
14
- },
15
- get: (key) => {
16
- const val = store.get(key);
17
- console.log(`[gState] get('${key}'):`, val);
18
- return val;
19
- },
20
- set: (key, value) => {
21
- const result = store.set(key, value);
22
- console.log(`[gState] set('${key}', ${JSON.stringify(value)}):`, result);
23
- return result;
24
- },
25
- watch: (key, callback) => {
26
- const unwatch = store.watch(key, callback);
27
- console.log(`[gState] watching '${key}'`);
28
- return unwatch;
29
- },
30
- info: () => {
31
- const info = {
32
- namespace: store.namespace,
33
- isReady: store.isReady,
34
- keys: Object.keys(store.list()),
35
- size: Object.keys(store.list()).length
36
- };
37
- console.log('[gState] Store Info:', info);
38
- return info;
39
- },
40
- banner: () => {
41
- console.log(`
42
- ╔═══════════════════════════════════════╗
43
- ║ 🧲 gState Debug ║
44
- ║ Type: gstate.list() ║
45
- ║ gstate.get(key) ║
46
- ║ gstate.set(key, value) ║
47
- ║ gstate.info() ║
48
- ╚═══════════════════════════════════════╝
49
- `);
50
- }
51
- };
52
- console.log('[gState] Debug plugin installed. Type gstate.banner() for help.');
53
- }
54
- },
55
- onDestroy: () => {
56
- if (typeof window !== 'undefined') {
57
- delete window.gstate;
58
- }
59
- }
60
- }
61
- };
62
- };
@@ -1,28 +0,0 @@
1
- export const devToolsPlugin = (options) => {
2
- const ext = globalThis;
3
- const global = ext;
4
- const extension = global.__REDUX_DEVTOOLS_EXTENSION__;
5
- if (!extension?.connect) {
6
- return { name: 'gstate-devtools-noop', hooks: {} };
7
- }
8
- let _devTools = null;
9
- return {
10
- name: 'gstate-devtools',
11
- hooks: {
12
- onInstall: ({ store }) => {
13
- _devTools = extension.connect({ name: options?.name || 'Magnetar Store' });
14
- _devTools.init(store.list());
15
- },
16
- onSet: ({ key, store }) => {
17
- if (!key || !_devTools)
18
- return;
19
- _devTools.send(`SET_${key.toUpperCase()}`, store.list());
20
- },
21
- onRemove: ({ key, store }) => {
22
- if (!key || !_devTools)
23
- return;
24
- _devTools.send(`REMOVE_${key.toUpperCase()}`, store.list());
25
- }
26
- }
27
- };
28
- };
@@ -1,15 +0,0 @@
1
- export const guardPlugin = (guards) => ({
2
- name: 'gstate-guard',
3
- hooks: {
4
- onBeforeSet: ({ key, value, store: _store }) => {
5
- if (!key)
6
- return;
7
- const guard = guards[key];
8
- if (guard) {
9
- const transformed = guard(value);
10
- if (transformed !== value) {
11
- }
12
- }
13
- }
14
- }
15
- });
@@ -1,10 +0,0 @@
1
- export const immerPlugin = () => ({
2
- name: 'gstate-immer',
3
- hooks: {
4
- onInstall: ({ store }) => {
5
- store._registerMethod('immer', 'setWithProduce', ((key, updater) => {
6
- return store.set(key, updater);
7
- }));
8
- }
9
- }
10
- });
@@ -1,102 +0,0 @@
1
- export const indexedDBPlugin = (options = {}) => {
2
- const dbName = options.dbName || 'rgs-db';
3
- const storeName = options.storeName || 'states';
4
- const dbVersion = options.version || 1;
5
- let db = null;
6
- const getDB = () => {
7
- return new Promise((resolve, reject) => {
8
- if (db)
9
- return resolve(db);
10
- const request = indexedDB.open(dbName, dbVersion);
11
- request.onerror = () => reject(request.error);
12
- request.onsuccess = () => {
13
- db = request.result;
14
- resolve(db);
15
- };
16
- request.onupgradeneeded = (event) => {
17
- const database = event.target.result;
18
- if (!database.objectStoreNames.contains(storeName)) {
19
- database.createObjectStore(storeName);
20
- }
21
- };
22
- });
23
- };
24
- const save = async (key, value) => {
25
- const database = await getDB();
26
- return new Promise((resolve, reject) => {
27
- const tx = database.transaction(storeName, 'readwrite');
28
- const store = tx.objectStore(storeName);
29
- const request = store.put(value, key);
30
- request.onsuccess = () => resolve();
31
- request.onerror = () => reject(request.error);
32
- });
33
- };
34
- const load = async (key) => {
35
- const database = await getDB();
36
- return new Promise((resolve, reject) => {
37
- const tx = database.transaction(storeName, 'readonly');
38
- const store = tx.objectStore(storeName);
39
- const request = store.get(key);
40
- request.onsuccess = () => resolve(request.result);
41
- request.onerror = () => reject(request.error);
42
- });
43
- };
44
- const remove = async (key) => {
45
- const database = await getDB();
46
- return new Promise((resolve, reject) => {
47
- const tx = database.transaction(storeName, 'readwrite');
48
- const store = tx.objectStore(storeName);
49
- const request = store.delete(key);
50
- request.onsuccess = () => resolve();
51
- request.onerror = () => reject(request.error);
52
- });
53
- };
54
- return {
55
- name: 'indexedDB',
56
- hooks: {
57
- onInstall: ({ store }) => {
58
- store._registerMethod('indexedDB', 'clear', async () => {
59
- const database = await getDB();
60
- const tx = database.transaction(storeName, 'readwrite');
61
- tx.objectStore(storeName).clear();
62
- });
63
- },
64
- onInit: async ({ store }) => {
65
- const database = await getDB();
66
- const tx = database.transaction(storeName, 'readonly');
67
- const objectStore = tx.objectStore(storeName);
68
- const request = objectStore.getAllKeys();
69
- request.onsuccess = async () => {
70
- const keys = request.result;
71
- const prefix = store.namespace + '_';
72
- for (const key of keys) {
73
- if (key.startsWith(prefix)) {
74
- const val = await load(key);
75
- if (val) {
76
- const storeKey = key.substring(prefix.length);
77
- store._setSilently(storeKey, val.d);
78
- }
79
- }
80
- }
81
- };
82
- },
83
- onSet: async ({ key, value, store }) => {
84
- if (!key)
85
- return;
86
- const prefix = store.namespace + '_';
87
- const data = {
88
- d: value,
89
- t: Date.now(),
90
- v: store._getVersion?.(key) || 1
91
- };
92
- await save(`${prefix}${key}`, data);
93
- },
94
- onRemove: async ({ key, store }) => {
95
- if (!key)
96
- return;
97
- const prefix = store.namespace + '_';
98
- await remove(`${prefix}${key}`);
99
- }
100
- }
101
- };
102
- };