cogsbox-state 0.5.467 → 0.5.469

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 CHANGED
@@ -46,9 +46,9 @@ function UserComponent() {
46
46
 
47
47
  return (
48
48
  <div>
49
- <p>Name: {user.name.get()}</p>
50
- <p>Counter: {user.stats.counter.get()}</p>
51
- <button onClick={() => user.stats.counter.update(prev => prev + 1)}>
49
+ <p>Name: {user.name.$get()}</p>
50
+ <p>Counter: {user.stats.counter.$get()}</p>
51
+ <button onClick={() => user.stats.counter.$update(prev => prev + 1)}>
52
52
  Increment Counter
53
53
  </button>
54
54
  </div>
@@ -60,7 +60,7 @@ function TodoComponent() {
60
60
 
61
61
  return (
62
62
  <div>
63
- <p>Todo count: {todos.get().length}</p>
63
+ <p>Todo count: {todos.$get().length}</p>
64
64
  <button onClick={() => todos.insert({ id: Date.now(), text: 'New todo', done: false })}>
65
65
  Add Todo
66
66
  </button>
@@ -77,16 +77,16 @@ Every state property gets these core methods:
77
77
 
78
78
  #### Primitives (strings, numbers, booleans)
79
79
 
80
- - `.get()` - read values reactively
81
- - `.update()` - set values
82
- - `.toggle()` - flip booleans
80
+ - `.$get()` - read values reactively
81
+ - `.$update()` - set values
82
+ - `.$toggle()` - flip booleans
83
83
  - `.$get()` - non-reactive read (signals)
84
84
  - `.$derive()` - computed signals
85
85
 
86
86
  #### Objects
87
87
 
88
88
  - All primitive methods plus access to nested properties
89
- - `.update()` can do partial updates
89
+ - `.$update()` can do partial updates
90
90
 
91
91
  #### Arrays
92
92
 
@@ -101,19 +101,19 @@ const todos = useCogsState('todos');
101
101
  const settings = useCogsState('settings');
102
102
 
103
103
  // Reactive reads (triggers re-renders)
104
- const userName = user.name.get();
105
- const allTodos = todos.get();
106
- const isDarkMode = settings.darkMode.get();
104
+ const userName = user.name.$get();
105
+ const allTodos = todos.$get();
106
+ const isDarkMode = settings.darkMode.$get();
107
107
 
108
108
  // Access nested properties
109
- const counterValue = user.stats.counter.get();
110
- const firstTodo = todos.index(0)?.get();
109
+ const counterValue = user.stats.counter.$get();
110
+ const firstTodo = todos.$index(0)?.$get();
111
111
 
112
112
  // Non-reactive reads (no re-renders, for signals)
113
- const userNameStatic = user.name.$get();
113
+ const userNameStatic = user.name.$$get();
114
114
 
115
115
  // Computed signals (transforms value without re-renders)
116
- const todoCount = todos.$derive((todos) => todos.length);
116
+ const todoCount = todos.$$derive((todos) => todos.length);
117
117
  ```
118
118
 
119
119
  ### Updating State
@@ -124,17 +124,17 @@ const settings = useCogsState('settings');
124
124
  const todos = useCogsState('todos');
125
125
 
126
126
  // Direct updates
127
- user.name.update('Jane');
128
- settings.darkMode.toggle();
127
+ user.name.$update('Jane');
128
+ settings.darkMode.$toggle();
129
129
 
130
130
  // Functional updates
131
- user.stats.counter.update((prev) => prev + 1);
131
+ user.stats.counter.$update((prev) => prev + 1);
132
132
 
133
133
  // Object updates
134
- user.update((prev) => ({ ...prev, name: 'Jane', age: 30 }));
134
+ user.$update((prev) => ({ ...prev, name: 'Jane', age: 30 }));
135
135
 
136
136
  // Deep nested updates
137
- todos.index(0).text.update('Updated todo text');
137
+ todos.$index(0).text.$update('Updated todo text');
138
138
  ```
139
139
 
140
140
  ## Working with Arrays
@@ -147,20 +147,20 @@ Arrays are first-class citizens with powerful built-in operations:
147
147
  const todos = useCogsState('todos');
148
148
 
149
149
  // Add items
150
- todos.insert({ id: 'uuid', text: 'New todo', done: false });
151
- todos.insert(({ uuid }) => ({
150
+ todos.$insert({ id: 'uuid', text: 'New todo', done: false });
151
+ todos.$insert(({ uuid }) => ({
152
152
  id: uuid,
153
153
  text: 'Auto-generated ID',
154
154
  done: false,
155
155
  }));
156
156
 
157
157
  // Remove items
158
- todos.cut(2); // Remove at index 2
159
- todos.cutSelected(); // Remove currently selected item
158
+ todos.$cut(2); // Remove at index 2
159
+ todos.$cutSelected(); // Remove currently selected item
160
160
 
161
161
  // Access items
162
- const firstTodo = todos.index(0);
163
- const lastTodo = todos.last();
162
+ const firstTodo = todos.$index(0);
163
+ const lastTodo = todos.$last();
164
164
  ```
165
165
 
166
166
  ### Array Iteration and Rendering
@@ -171,12 +171,12 @@ const lastTodo = todos.last();
171
171
  const todos = useCogsState('todos');
172
172
 
173
173
  // Returns transformed array, each item is a full state object
174
- const todoElements = todos.stateMap((todoState, index, arrayState) => (
174
+ const todoElements = todos.$stateMap((todoState, index, arrayState) => (
175
175
  <TodoItem
176
- key={todoState.id.get()}
176
+ key={todoState.id.$get()}
177
177
  todo={todoState}
178
- onToggle={() => todoState.done.toggle()}
179
- onDelete={() => arrayState.cut(index)}
178
+ onToggle={() => todoState.done.$toggle()}
179
+ onDelete={() => arrayState.$cut(index)}
180
180
  />
181
181
  ));
182
182
  ```
@@ -187,10 +187,10 @@ const todoElements = todos.stateMap((todoState, index, arrayState) => (
187
187
  const todos = useCogsState('todos');
188
188
 
189
189
  // Renders directly in place with automatic key management
190
- {todos.stateList((todoState, index, arrayState) => (
191
- <div key={todoState.id.get()}>
192
- <span>{todoState.text.get()}</span>
193
- <button onClick={() => todoState.done.toggle()}>Toggle</button>
190
+ {todos.$stateList((todoState, index, arrayState) => (
191
+ <div key={todoState.id.$get()}>
192
+ <span>{todoState.text.$get()}</span>
193
+ <button onClick={() => todoState.done.$toggle()}>Toggle</button>
194
194
  <button onClick={() => arrayState.cut(index)}>Delete</button>
195
195
  </div>
196
196
  ))}
@@ -215,16 +215,16 @@ const todos = useCogsState('todos');
215
215
  const todos = useCogsState('todos');
216
216
 
217
217
  // Filter items (returns new state object with filtered view)
218
- const completedTodos = todos.stateFilter((todo) => todo.done);
219
- const incompleteTodos = todos.stateFilter((todo) => !todo.done);
218
+ const completedTodos = todos.$stateFilter((todo) => todo.done);
219
+ const incompleteTodos = todos.$stateFilter((todo) => !todo.done);
220
220
 
221
221
  // Sort items (returns new state object with sorted view)
222
- const sortedTodos = todos.stateSort((a, b) => a.text.localeCompare(b.text));
222
+ const sortedTodos = todos.$stateSort((a, b) => a.text.localeCompare(b.text));
223
223
 
224
224
  // Chain operations
225
225
  const sortedCompletedTodos = todos
226
- .stateFilter((todo) => todo.done)
227
- .stateSort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
226
+ .$stateFilter((todo) => todo.done)
227
+ .$stateSort((a, b) => new Date(a.createdAt) - new Date(b.createdAt));
228
228
  ```
229
229
 
230
230
  #### Finding and Searching
@@ -233,13 +233,13 @@ const sortedCompletedTodos = todos
233
233
  const todos = useCogsState('todos');
234
234
 
235
235
  // Find by property value
236
- const todoById = todos.findWith('id', 'some-id');
236
+ const todoById = todos.$findWith('id', 'some-id');
237
237
  if (todoById) {
238
- todoById.text.update('Updated text');
238
+ todoById.text.$update('Updated text');
239
239
  }
240
240
 
241
241
  // Find with custom function
242
- const firstIncompleteTodo = todos.stateFind((todo) => !todo.done);
242
+ const firstIncompleteTodo = todos.$stateFind((todo) => !todo.done);
243
243
  ```
244
244
 
245
245
  #### Unique Operations
@@ -248,7 +248,7 @@ const firstIncompleteTodo = todos.stateFind((todo) => !todo.done);
248
248
  const todos = useCogsState('todos');
249
249
 
250
250
  // Insert only if unique (prevents duplicates)
251
- todos.uniqueInsert(
251
+ todos.$uniqueInsert(
252
252
  { id: 'new-id', text: 'New todo', done: false },
253
253
  ['id'], // Fields to check for uniqueness
254
254
  (existingItem) => {
@@ -258,7 +258,7 @@ todos.uniqueInsert(
258
258
  );
259
259
 
260
260
  // Toggle presence (insert if missing, remove if present)
261
- todos.toggleByValue('some-id');
261
+ todos.$toggleByValue('some-id');
262
262
  ```
263
263
 
264
264
  #### Selection Management
@@ -267,18 +267,18 @@ todos.toggleByValue('some-id');
267
267
  const todos = useCogsState('todos');
268
268
 
269
269
  // Built-in selection tracking
270
- const selectedTodo = todos.getSelected();
271
- const selectedIndex = todos.getSelectedIndex();
270
+ const selectedTodo = todos.$getSelected();
271
+ const selectedIndex = todos.$getSelectedIndex();
272
272
 
273
273
  // Set selection on individual items
274
- todos.index(0).setSelected(true);
275
- todos.index(0).toggleSelected();
274
+ todos.$index(0).$setSelected(true);
275
+ todos.$index(0).$toggleSelected();
276
276
 
277
277
  // Clear all selections
278
- todos.clearSelected();
278
+ todos.$clearSelected();
279
279
 
280
280
  // Check if item is selected
281
- const isSelected = todos.index(0).isSelected;
281
+ const isSelected = todos.$index(0).isSelected;
282
282
  ```
283
283
 
284
284
  <!-- ### Virtualization for Large Lists
@@ -302,7 +302,7 @@ function MessageList() {
302
302
  <div style={virtualizerProps.inner.style}>
303
303
  <div style={virtualizerProps.list.style}>
304
304
  {virtualState.stateList((messageState, index) => (
305
- <MessageItem key={messageState.id.get()} message={messageState} />
305
+ <MessageItem key={messageState.id.$get()} message={messageState} />
306
306
  ))}
307
307
  </div>
308
308
  </div>
@@ -311,37 +311,6 @@ function MessageList() {
311
311
  }
312
312
  ``` -->
313
313
 
314
- ### Streaming for Real-time Data
315
-
316
- ```typescript
317
- const messages = useCogsState('messages');
318
-
319
- // Create a stream for efficient batch operations
320
- const messageStream = messages.stream({
321
- bufferSize: 100, // Buffer size before auto-flush
322
- flushInterval: 100, // Auto-flush interval (ms)
323
- bufferStrategy: 'sliding', // 'sliding' | 'dropping' | 'accumulate'
324
- store: (buffer) => buffer, // Transform buffered items before insertion
325
- onFlush: (buffer) => console.log('Flushed', buffer.length, 'items'),
326
- });
327
-
328
- // Write individual items
329
- messageStream.write(newMessage);
330
-
331
- // Write multiple items
332
- messageStream.writeMany([msg1, msg2, msg3]);
333
-
334
- // Manual flush
335
- messageStream.flush();
336
-
337
- // Pause/resume
338
- messageStream.pause();
339
- messageStream.resume();
340
-
341
- // Close stream
342
- messageStream.close();
343
- ```
344
-
345
314
  ## Reactivity Control
346
315
 
347
316
  Cogsbox offers different reactivity modes for performance optimization:
@@ -409,14 +378,14 @@ function PerformantComponent() {
409
378
  {todos.$stateMap((todo, index) => (
410
379
  <div key={todo.id.$get()}>
411
380
  <span>{todo.text.$get()}</span>
412
- <button onClick={() => todo.done.toggle()}>Toggle</button>
381
+ <button onClick={() => todo.done.$toggle()}>Toggle</button>
413
382
  </div>
414
383
  ))}
415
384
 
416
385
  {/* Wrap with formElement for isolated reactivity */}
417
386
  {user.stats.counter.formElement((obj) => (
418
- <button onClick={() => obj.update(prev => prev + 1)}>
419
- Increment: {obj.get()}
387
+ <button onClick={() => obj.$update(prev => prev + 1)}>
388
+ Increment: {obj.$get()}
420
389
  </button>
421
390
  ))}
422
391
  </div>
@@ -474,246 +443,28 @@ function UserForm() {
474
443
  ))}
475
444
 
476
445
  {/* Custom debounce time */}
477
- {userForm.email.formElement(({ inputProps, get, update }) => (
446
+ {userForm.email.formElement(({ inputProps, $get, $update }) => (
478
447
  <>
479
448
  <label>Email</label>
480
449
  <input {...inputProps} />
481
- <small>Current: {get()}</small>
450
+ <small>Current: {$get()}</small>
482
451
  </>
483
452
  ), { debounceTime: 500 })}
484
453
 
485
454
  {/* Custom form control */}
486
- {userForm.age.formElement(({ get, update }) => (
455
+ {userForm.age.formElement(({ $get, $update }) => (
487
456
  <>
488
457
  <label>Age</label>
489
458
  <input
490
459
  type="number"
491
- value={get()}
492
- onChange={e => update(parseInt(e.target.value))}
460
+ value={$get()}
461
+ onChange={e => $update(parseInt(e.target.value))}
493
462
  />
494
463
  </>
495
464
  ))}
496
465
 
497
- <button onClick={() => {
498
- if (userForm.validateZodSchema()) {
499
- console.log('Valid!', userForm.get());
500
- }
501
- }}>
502
- Submit
503
- </button>
504
- </form>
505
- );
506
- }
507
- ```
508
-
509
- ## Advanced Features
510
-
511
- ### Server Synchronization
512
-
513
- ```typescript
514
- const { useCogsState } = createCogsState({
515
- userProfile: {
516
- initialState: { name: "", email: "" },
517
- sync: {
518
- action: async (state) => {
519
- const response = await fetch('/api/user', {
520
- method: 'PUT',
521
- body: JSON.stringify(state)
522
- });
523
- return response.ok
524
- ? { success: true, data: await response.json() }
525
- : { success: false, error: 'Failed to save' };
526
- },
527
- onSuccess: (data) => console.log('Saved!', data),
528
- onError: (error) => console.error('Save failed:', error)
529
- }
530
- }
531
- });
532
-
533
- function UserProfile() {
534
- const userProfile = useCogsState('userProfile');
535
-
536
- return (
537
- <div>
538
- <div>Status: {userProfile.getStatus()}</div> {/* 'fresh' | 'dirty' | 'synced' | 'restored' */}
539
- <input
540
- value={userProfile.name.get()}
541
- onChange={e => userProfile.name.update(e.target.value)}
542
- />
543
- <button onClick={() => userProfile.sync()}>Save to Server</button>
544
- </div>
545
- );
546
- }
547
- ```
548
-
549
- ### Local Storage Integration
550
-
551
- ```typescript
552
- const { useCogsState } = createCogsState({
553
- userPrefs: {
554
- initialState: { theme: 'dark', language: 'en' },
555
- localStorage: {
556
- key: 'user-preferences',
557
- onChange: (state) => console.log('Saved to localStorage:', state),
558
- },
559
- },
560
- });
561
-
562
- function PreferencesComponent() {
563
- const userPrefs = useCogsState('userPrefs');
564
466
 
565
- return (
566
- <div>
567
- <select
568
- value={userPrefs.theme.get()}
569
- onChange={e => userPrefs.theme.update(e.target.value)}
570
- >
571
- <option value="dark">Dark</option>
572
- <option value="light">Light</option>
573
- </select>
574
- </div>
575
- );
576
- }
577
- ```
578
-
579
- ### State Status and History
580
-
581
- ```typescript
582
- const user = useCogsState('user');
583
-
584
- // Check what changed from initial state
585
- const differences = user.getDifferences();
586
-
587
- // Get current status
588
- const status = user.getStatus(); // 'fresh' | 'dirty' | 'synced' | 'restored'
589
-
590
- // Revert to initial state
591
- user.revertToInitialState();
592
-
593
- // Update initial state (useful for findign diffs and server-synced data)
594
- const newServerData = {
595
- name: 'Jane Doe',
596
- age: 31,
597
- stats: { counter: 100, lastUpdated: new Date() },
598
- };
599
- user.updateInitialState(newServerData);
600
- ```
601
-
602
- ### Component Isolation
603
-
604
- ```typescript
605
- // Each component can have its own reactive settings
606
- function ComponentA() {
607
- const user = useCogsState('user', {
608
- reactiveType: 'deps',
609
- reactiveDeps: (state) => [state.name],
610
- });
611
- // Only re-renders when user.name changes
612
- }
613
-
614
- function ComponentB() {
615
- const user = useCogsState('user', {
616
- reactiveType: 'all',
617
- });
618
- // Re-renders on any change to 'user' state
619
- }
620
- ```
621
-
622
- ## Performance Tips
623
-
624
- 1. **Use signals for high-frequency updates**: `.$get()` and `.$derive()` don't trigger React re-renders
625
- 2. **Use `reactiveType: 'none'` with signals**: Maximum performance for signal-heavy components
626
- 3. **Use virtualization for large lists**: `useVirtualView()` handles thousands of items efficiently
627
- 4. **Use streaming for real-time data**: Batch operations with `stream()` for better performance
628
- 5. **Chain filter/sort operations**: `stateFilter().stateSort()` creates efficient views
629
- 6. **Use `formElement` for forms**: Automatic debouncing and validation handling
630
-
631
- ## Common Patterns
632
-
633
- ### Master-Detail Interface
634
-
635
- ```typescript
636
- function TodoApp() {
637
- const todos = useCogsState('todos');
638
- const selectedTodo = todos.getSelected();
639
-
640
- return (
641
- <div className="flex">
642
- <div className="list">
643
- {todos.stateList((todo, index) => (
644
- <div
645
- key={todo.id.get()}
646
- className={todo.isSelected ? 'selected' : ''}
647
- onClick={() => todo.toggleSelected()}
648
- >
649
- {todo.text.get()}
650
- </div>
651
- ))}
652
- </div>
653
-
654
- <div className="detail">
655
- {selectedTodo ? (
656
- <TodoDetail todo={selectedTodo} />
657
- ) : (
658
- <p>Select a todo</p>
659
- )}
660
- </div>
661
- </div>
662
- );
663
- }
664
- ```
665
-
666
- ### Real-time Chat with Virtualization
667
-
668
- ```typescript
669
- function ChatRoom() {
670
- const messages = useCogsState('messages', { reactiveType: 'none' });
671
-
672
- const { virtualState, virtualizerProps, scrollToBottom } =
673
- messages.useVirtualView({
674
- itemHeight: 65,
675
- overscan: 10,
676
- stickToBottom: true,
677
- });
678
-
679
- return (
680
- <div {...virtualizerProps.outer} className="chat-container">
681
- <div style={virtualizerProps.inner.style}>
682
- <div style={virtualizerProps.list.style}>
683
- {virtualState.stateList((message) => (
684
- <MessageItem key={message.id.$get()} message={message} />
685
- ))}
686
- </div>
687
- </div>
688
- </div>
689
- );
690
- }
691
- ```
692
-
693
- ### Multiple State Slices in One Component
694
-
695
- ```typescript
696
- function Dashboard() {
697
- const user = useCogsState('user');
698
- const todos = useCogsState('todos');
699
- const settings = useCogsState('settings');
700
-
701
- return (
702
- <div>
703
- <header>
704
- <h1>Welcome, {user.name.get()}</h1>
705
- <button onClick={() => settings.darkMode.toggle()}>
706
- Toggle Theme
707
- </button>
708
- </header>
709
-
710
- <main>
711
- <p>You have {todos.get().length} todos</p>
712
- <p>Counter: {user.stats.counter.get()}</p>
713
- </main>
714
- </div>
467
+ </form>
715
468
  );
716
469
  }
717
470
  ```
718
-
719
- This library provides a unique approach to React state management by creating a proxy that mirrors your data structure while adding powerful methods for manipulation, rendering, and performance optimization.