scope-state 0.1.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/README.md ADDED
@@ -0,0 +1,372 @@
1
+ # Scope State
2
+ _The simplest global state system for React._
3
+
4
+ A tiny reactive state manager with global reach and local clarity. Built for modern React. No stale bugs. No mental gymnastics. Full support for storage persistence, auto-optimization, and more.
5
+
6
+ Built for developers who hate Redux and love clarity.
7
+
8
+
9
+
10
+ ## Why Scope State?
11
+
12
+ Scope State gives you **global reactive state** with:
13
+ - **Zero reducers, zero contexts**
14
+ - **Zero boilerplate**
15
+ - **Zero spreads, zero selectors**
16
+ - **No need for `setState`**
17
+ - And most importantly — **no stale value bugs**
18
+
19
+ Just write:
20
+
21
+ ```ts
22
+ import { $, useScope } from 'scope-state';
23
+
24
+ export const ProfilePage = () => {
25
+ const name = useScope(() => $.user.name);
26
+ return <h1>{name}</h1>
27
+ }
28
+ ```
29
+
30
+ That's it. It tracks dependencies automatically and re-renders only what changed.
31
+
32
+
33
+
34
+ ## What Makes It Different?
35
+
36
+ - **Fully reactive** — inspired by proxies, not reducers
37
+ - **Intuitive reads and writes** — no `.get()` or `.set()` syntax hell
38
+ - **Mutate like it's a regular object or ref** — works with objects, arrays, numbers, everything
39
+ - **Fine-grained tracking** — no wasted renders
40
+ - **Built-in debug tools**
41
+ - **Feels like magic**
42
+ - **Read and set states *independently* —** outside of functional components or custom hooks
43
+
44
+
45
+
46
+ ## Getting Started
47
+
48
+ ### Installation
49
+
50
+ ```bash
51
+ npm install scope-state
52
+ ```
53
+
54
+ ### Quick Start
55
+
56
+ ```tsx
57
+ // store.ts
58
+ import { configure } from 'scope-state';
59
+ export const $ = configure({
60
+ initialState: {
61
+ user: { name: 'John', age: 30 }
62
+ }
63
+ });
64
+ ```
65
+
66
+ ```tsx
67
+ // UserProfile.tsx
68
+ import { useScope } from 'scope-state';
69
+ import { $ } from './store';
70
+
71
+ export const UserProfile = () => {
72
+ const name = useScope(() => $.user.name);
73
+ return <h1>{name}</h1>;
74
+ }
75
+ ```
76
+
77
+ ### Basic Usage
78
+
79
+ ```tsx
80
+ import { useScope, configure } from 'scope-state';
81
+
82
+ // RECOMMENDED: Configure with your initial state
83
+ // It's best to configure your initial store in a separate file.
84
+ // The usage of the dollar sign ($) is optional; just a way to keep it brief
85
+ // and easy to identify.
86
+
87
+ export const $ = configure({
88
+ initialState: {
89
+ user: { name: 'John', age: 30 },
90
+ todos: [],
91
+ theme: 'dark'
92
+ }
93
+ });
94
+
95
+ // Use in components
96
+ import { useScope } from 'scope-state';
97
+
98
+ export const UserProfileComponent = () => {
99
+
100
+ const user = useScope(() => $.user);
101
+
102
+ return (
103
+ <div>
104
+ <h1>{user.name}</h1>
105
+ <button
106
+ onClick={() => {
107
+ user.age += 1
108
+ }}
109
+ >
110
+ Age: {user.age}
111
+ </button>
112
+ </div>
113
+ );
114
+ }
115
+
116
+ const TodoList = () => {
117
+ const todos = useScope(() => $.todos);
118
+
119
+ const addTodo = () => {
120
+ $.todos.push({ id: Date.now(), text: 'New todo', done: false });
121
+ };
122
+
123
+ return (
124
+ <div>
125
+ {todos.map(todo => (
126
+ <div key={todo.id}>{todo.text}</div>
127
+ ))}
128
+ <button onClick={addTodo}>Add Todo</button>
129
+ </div>
130
+ );
131
+ }
132
+ ```
133
+
134
+ ## 📚 API Reference
135
+
136
+ ### Core Functions
137
+
138
+ #### `useScope(selector)`
139
+ Subscribe to reactive state changes.
140
+
141
+ ```tsx
142
+ // Subscribe to entire object
143
+ const user = useScope(() => $.user);
144
+
145
+ // Subscribe to specific property
146
+ const userName = useScope(() => $.user.name);
147
+
148
+ // Subscribe to computed value
149
+ const isAdmin = useScope(() => $.user.role === 'admin');
150
+
151
+ // Subscribe to array
152
+ const todos = useScope(() => $.todos);
153
+ ```
154
+
155
+ #### `configure(options)`
156
+ Configure Scope State with custom settings.
157
+
158
+ ```tsx
159
+ import { configure, presets } from 'scope-state';
160
+
161
+ // Use a preset
162
+ configure(presets.production());
163
+
164
+ // Custom configuration
165
+ const $ = configure({
166
+ initialState: { /* your state */ },
167
+ monitoring: { enabled: true },
168
+ proxy: { maxDepth: 3 }
169
+ });
170
+
171
+ // Then access any item in your state by scoping it using the main hook:
172
+ const restaurants = useScope(() => $.restaurants || []) // optional fallback
173
+ ```
174
+
175
+ ---
176
+
177
+ ### Object Methods
178
+
179
+ All objects in the store have these reactive methods:
180
+
181
+ #### `$merge(newProps)`
182
+ Merge new properties without removing existing ones.
183
+
184
+ ```tsx
185
+ $.user.$merge({ name: 'John' }); // Updates only name
186
+ ```
187
+
188
+ #### `$set(newProps)`
189
+ Replace object with new properties.
190
+
191
+ ```tsx
192
+ $.user.$set({ name: 'John', age: 25 }); // Replaces entire user object
193
+ ```
194
+
195
+ #### `raw()`
196
+ Get plain, serializable JavaScript object without any function references (the reactivity methods removed).
197
+
198
+ ```tsx
199
+ const plainUser = $.user.raw();
200
+ ```
201
+ _This is helpful when you need to serialize the state for storage, API calls, or debugging. Otherwise, it's not necessary._
202
+
203
+ ---
204
+
205
+ ### Array Methods
206
+
207
+ Arrays have enhanced methods that trigger reactivity:
208
+
209
+ ```tsx
210
+ todos.push({ id: 1, text: 'Buy milk' }); // This will trigger a re-render
211
+
212
+ todos.splice(0, 1); // This will trigger a re-render
213
+
214
+ $.todos = [/* new array */] // You can also directly assign a new array to the property in the global state itself ($).
215
+ ```
216
+
217
+ ### Utility Functions
218
+
219
+ Create reactive local state (not global).
220
+
221
+ ```tsx
222
+ import { useLocal } from 'scope-state';
223
+
224
+ function MyComponent() {
225
+ const localState = useLocal({ count: 0 });
226
+
227
+ return (
228
+ <button onClick={() => localState.count + 1}>
229
+ Count: {localState.count}
230
+ </button>
231
+ );
232
+ }
233
+ ```
234
+
235
+
236
+
237
+ ## Configuration
238
+
239
+
240
+
241
+ ### Presets
242
+
243
+ ```tsx
244
+ import { configure, presets } from 'scope-state';
245
+
246
+ // Development: Enhanced debugging
247
+ configure(presets.development());
248
+
249
+ // Production: Optimized performance
250
+ configure(presets.production());
251
+
252
+ // Minimal: Memory-constrained environments
253
+ configure(presets.minimal());
254
+
255
+ // Full-featured: All features enabled
256
+ configure(presets.full());
257
+ ```
258
+
259
+
260
+
261
+ ### Custom Configuration
262
+
263
+ ```tsx
264
+ configure({
265
+ initialState: {
266
+ // Your app's initial state
267
+ },
268
+ proxy: {
269
+ maxDepth: 5, // How deep to proxy objects
270
+ smartArrayTracking: true, // Optimize array operations
271
+ },
272
+ monitoring: {
273
+ enabled: true, // Enable debug logging
274
+ verboseLogging: false, // Detailed logs
275
+ autoLeakDetection: true, // Detect memory leaks
276
+ },
277
+ persistence: {
278
+ enabled: true, // Enable state persistence
279
+ paths: ['user', 'settings.theme'], // Which paths to persist (leave as undefined to persist all paths)
280
+ }
281
+ });
282
+ ```
283
+
284
+
285
+
286
+ ## Philosophy
287
+
288
+ React's core primitives like `useState`, `useReducer`, and `useContext` work well for many use cases.
289
+
290
+ But when your app grows in complexity…
291
+ - deeply nested objects,
292
+ - shared state across pages,
293
+ - state persistence,
294
+ - or fine-grained reactivity,
295
+
296
+ suddenly you're spending time wiring reducers, spreading props, memoizing selectors, and debugging re-renders.
297
+
298
+ Scope State simplifies that.
299
+
300
+ You write and read state _directly_, just like a `ref` or a signal, but with full reactivity, automatic tracking, and global accessibility.
301
+
302
+
303
+ This library was built out of frustration with every other state system:
304
+ - **Redux** is too bloated
305
+ - **Recoil** is too verbose
306
+ - **Zustand** still forces manual updates
307
+ - **Legend State** is performant (and deserves significant respect) but has a higher learning curve and confusing API. It's simply ahead of its time.
308
+
309
+ The mental model is simple: write and read directly. Like `useRef`, but global, reactive, and tracked.
310
+
311
+ ## Best Practices
312
+
313
+ ### 1. Keep Selectors Simple
314
+
315
+ ```tsx
316
+ // ✅ Good
317
+ const name = useScope(() => $.user.name);
318
+
319
+ // ❌ Avoid complex computations in selectors
320
+ const expensiveData = useScope(() => $.data.map(/* heavy computation */));
321
+
322
+ // Instead...
323
+ const data = useScope(() => $.data);
324
+ const expensiveCalculation = useMemo(() => data.map(/* heavy computation */), [data])
325
+ ```
326
+
327
+
328
+
329
+ ### 2. Use Direct Assignment & Flexible Methods for Updates
330
+
331
+ ```tsx
332
+ // Option 1: Direct assignment
333
+ $.user.name = "John";
334
+
335
+ $.todos.push({ title: "Do Laundry", date: new Date().toISOString() })
336
+
337
+ // Option 2: Shallow merge a new value without changing existing properties
338
+ $.user.$merge({ name: 'John' });
339
+
340
+ // Option 3: Use the updater function [NEW!]
341
+ $.user.$update("age", (age) => age + 1);
342
+
343
+ ```
344
+
345
+
346
+
347
+ ### 3. Configure Persistence (Optional)
348
+
349
+ ```tsx
350
+ // ✅ Configure before your app starts
351
+ configure(presets.production());
352
+
353
+ function App() {
354
+ // Your app components
355
+ }
356
+ ```
357
+
358
+
359
+ ## Created by Dalton Letorney
360
+
361
+ If you like this, feel free to star the repo! If you love it, use it in production. If it breaks, open a PR so we can make this even more epic.
362
+
363
+
364
+ ---
365
+
366
+
367
+ **Scope State is minimal by design** — the goal is not to reinvent React, but to make it finally feel clean again.
368
+
369
+
370
+ ## License
371
+
372
+ MIT © Dalton Letorney
@@ -0,0 +1,33 @@
1
+ export { useScope } from './hooks/useScope';
2
+ export { useLocal } from './hooks/useLocal';
3
+ export { getConfig, resetConfig, presets } from './config';
4
+ export { initializeStore, getStore, resetStore } from './core/store';
5
+ export { monitorAPI } from './core/monitoring';
6
+ export { persistenceAPI } from './persistence/advanced';
7
+ export { setInitialStoreState, createAdvancedProxy, pathUsageStats, selectorPaths, clearProxyCache, getProxyCacheStats, optimizeMemoryUsage, proxyPathMap } from './core/proxy';
8
+ export type { ScopeConfig, ProxyConfig, MonitoringConfig, PersistenceConfig, CustomMethods, CustomArrayMethods, StoreType, MonitoringStats, ProxyCacheStats, PathUsageStats } from './types';
9
+ import type { StoreType, CustomMethods, CustomArrayMethods, ScopeConfig } from './types';
10
+ /**
11
+ * Configure Scope with custom settings and return a properly typed store
12
+ * This is the main way to set up Scope with TypeScript support
13
+ */
14
+ export declare function configure<T extends Record<string, any>>(config: ScopeConfig<T>): StoreType<T>;
15
+ export declare function trackDependenciesAdvanced<T>(selector: () => T): {
16
+ value: T;
17
+ paths: string[];
18
+ };
19
+ export declare const $: StoreType<any>;
20
+ export declare function createReactive<T extends object>(obj: T): T & (T extends any[] ? CustomArrayMethods<T[0]> : CustomMethods<T>);
21
+ export declare const $local: typeof createReactive;
22
+ export declare function isReactive(obj: any): boolean;
23
+ export declare function activate<T>(obj: T): T;
24
+ export declare function $activate<T>(obj: T): T;
25
+ export declare function getProxy<T = any>(path: string | string[] | any): T;
26
+ export declare function $get<T = any>(path: string | string[] | T): T;
27
+ export declare const debugInfo: {
28
+ getListenerCount: () => any;
29
+ getPathCount: () => any;
30
+ getActivePaths: () => any;
31
+ };
32
+ export declare const rawStore: any;
33
+ //# sourceMappingURL=index.d.ts.map