tab-bridge 0.1.1 → 0.2.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 CHANGED
@@ -17,8 +17,8 @@
17
17
  [![npm version](https://img.shields.io/npm/v/tab-bridge?style=for-the-badge&color=cb3837&label=npm&logo=npm&logoColor=white)](https://www.npmjs.com/package/tab-bridge)
18
18
  [![bundle size](https://img.shields.io/bundlephobia/minzip/tab-bridge?style=for-the-badge&color=6ead0a&label=size&logo=webpack&logoColor=white)](https://bundlephobia.com/package/tab-bridge)
19
19
  [![TypeScript](https://img.shields.io/badge/TypeScript-first-3178c6?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org)
20
- [![license](https://img.shields.io/github/license/serbi2012/tab-sync?style=for-the-badge&color=blue&logo=open-source-initiative&logoColor=white)](./LICENSE)
21
- [![GitHub stars](https://img.shields.io/github/stars/serbi2012/tab-sync?style=for-the-badge&color=yellow&logo=github&logoColor=white)](https://github.com/serbi2012/tab-sync)
20
+ [![license](https://img.shields.io/github/license/serbi2012/tab-bridge?style=for-the-badge&color=blue&logo=open-source-initiative&logoColor=white)](./LICENSE)
21
+ [![GitHub stars](https://img.shields.io/github/stars/serbi2012/tab-bridge?style=for-the-badge&color=yellow&logo=github&logoColor=white)](https://github.com/serbi2012/tab-bridge)
22
22
 
23
23
  <br />
24
24
 
@@ -26,7 +26,7 @@
26
26
 
27
27
  <br />
28
28
 
29
- [**Getting Started**](#-getting-started) · [**API**](#-api-reference) · [**React**](#%EF%B8%8F-react) · [**Architecture**](#-architecture) · [**Examples**](#-examples)
29
+ [**Getting Started**](#-getting-started) · [**API**](#-api-reference) · [**React**](#%EF%B8%8F-react) · [**Architecture**](#-architecture) · [**Examples**](#-examples) · [**Live Demo**](https://serbi2012.github.io/tab-bridge/)
30
30
 
31
31
  </div>
32
32
 
@@ -59,23 +59,23 @@ LWW conflict resolution with batched broadcasts and custom merge strategies
59
59
  Bully algorithm with heartbeat monitoring and automatic failover
60
60
 
61
61
  #### 📡 Cross-Tab RPC
62
- Fully typed arguments, Promise-based calls with timeout handling
62
+ Fully typed arguments, Promise-based calls with `callAll` broadcast support
63
63
 
64
- #### ⚛️ React Hooks
65
- Built on `useSyncExternalStore` for zero-tear concurrent rendering
64
+ #### 🔄 Atomic Transactions
65
+ `transaction()` for safe multi-key updates with abort support
66
66
 
67
67
  </td>
68
68
  <td width="50%" valign="top">
69
69
 
70
+ #### ⚛️ React Hooks
71
+ 7 hooks built on `useSyncExternalStore` — zero-tear concurrent rendering
72
+
70
73
  #### 🛡️ Middleware Pipeline
71
74
  Intercept, validate, and transform state changes before they're applied
72
75
 
73
76
  #### 💾 State Persistence
74
77
  Survive page reloads with key whitelisting and custom storage backends
75
78
 
76
- #### 🔒 End-to-End Type Safety
77
- Discriminated unions, full type inference, and generic constraints
78
-
79
79
  #### 📦 Zero Dependencies
80
80
  Native browser APIs only, ~4KB gzipped, fully tree-shakable
81
81
 
@@ -175,6 +175,12 @@ sync.get('theme') // Read single key
175
175
  sync.getAll() // Read full state (stable reference)
176
176
  sync.set('theme', 'dark') // Write single key → broadcasts to all tabs
177
177
  sync.patch({ theme: 'dark', count: 5 }) // Write multiple keys in one broadcast
178
+
179
+ // Atomic multi-key update — return null to abort
180
+ sync.transaction((state) => {
181
+ if (state.count >= 100) return null; // abort
182
+ return { count: state.count + 1, lastUpdated: Date.now() };
183
+ });
178
184
  ```
179
185
 
180
186
  </details>
@@ -196,6 +202,13 @@ sync.select(
196
202
  (state) => state.items.filter(i => i.done).length,
197
203
  (doneCount) => updateBadge(doneCount),
198
204
  );
205
+
206
+ // Debounced derived state — callback fires at most once per 200ms
207
+ sync.select(
208
+ (state) => state.items.length,
209
+ (count) => analytics.track('item_count', count),
210
+ { debounce: 200 },
211
+ );
199
212
  ```
200
213
 
201
214
  </details>
@@ -248,6 +261,10 @@ sync.handle('getServerTime', () => ({
248
261
 
249
262
  const { iso } = await sync.call('leader', 'getServerTime');
250
263
  const result = await sync.call(tabId, 'compute', payload, 10_000);
264
+
265
+ // Broadcast RPC to ALL other tabs and collect responses
266
+ const results = await sync.callAll('getStatus');
267
+ // results: Array<{ tabId: string; result?: T; error?: string }>
251
268
  ```
252
269
 
253
270
  </details>
@@ -359,7 +376,8 @@ First-class React integration built on `useSyncExternalStore` for **zero-tear co
359
376
 
360
377
  ```tsx
361
378
  import {
362
- TabSyncProvider, useTabSync, useTabSyncValue, useTabSyncSelector, useIsLeader,
379
+ TabSyncProvider, useTabSync, useTabSyncValue, useTabSyncSelector,
380
+ useIsLeader, useTabs, useLeaderInfo, useTabSyncActions,
363
381
  } from 'tab-bridge/react';
364
382
  ```
365
383
 
@@ -444,6 +462,51 @@ function LeaderIndicator() {
444
462
 
445
463
  </details>
446
464
 
465
+ <details open>
466
+ <summary><b><code>useTabs()</code> — Active tab list</b></summary>
467
+
468
+ <br />
469
+
470
+ ```tsx
471
+ function TabList() {
472
+ const tabs = useTabs();
473
+ return <p>{tabs.length} tab(s) open</p>;
474
+ }
475
+ ```
476
+
477
+ </details>
478
+
479
+ <details open>
480
+ <summary><b><code>useLeaderInfo()</code> — Leader tab info</b></summary>
481
+
482
+ <br />
483
+
484
+ ```tsx
485
+ function LeaderDisplay() {
486
+ const leader = useLeaderInfo();
487
+ if (!leader) return <p>No leader yet</p>;
488
+ return <p>Leader: {leader.id}</p>;
489
+ }
490
+ ```
491
+
492
+ </details>
493
+
494
+ <details open>
495
+ <summary><b><code>useTabSyncActions()</code> — Write-only (no re-renders)</b></summary>
496
+
497
+ <br />
498
+
499
+ ```tsx
500
+ function IncrementButton() {
501
+ const { set, patch, transaction } = useTabSyncActions<MyState>();
502
+ return <button onClick={() => set('count', prev => prev + 1)}>+1</button>;
503
+ }
504
+ ```
505
+
506
+ Components using only `useTabSyncActions` **never re-render** due to state changes — perfect for write-only controls.
507
+
508
+ </details>
509
+
447
510
  <br />
448
511
 
449
512
  ---
@@ -725,7 +788,7 @@ MIT © [serbi2012](https://github.com/serbi2012)
725
788
 
726
789
  <br />
727
790
 
728
- <a href="https://github.com/serbi2012/tab-sync">
791
+ <a href="https://github.com/serbi2012/tab-bridge">
729
792
  <img src="https://img.shields.io/badge/GitHub-tab--bridge-4f46e5?style=for-the-badge&logo=github&logoColor=white" alt="GitHub" />
730
793
  </a>
731
794
  &nbsp;