tabus-js 0.1.0 → 0.1.3

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 (2) hide show
  1. package/README.md +190 -8
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -1,19 +1,19 @@
1
+ # tabus-js
2
+
1
3
  <div align="center">
2
- <img src="./public/tabus-logo.png" alt="tabus" width="200" />
4
+ <img src="./public/tabus-logo.png" alt="tabus-js" width="250" />
3
5
  </div>
4
6
 
5
- # tabus
6
-
7
7
  > Type-safe cross-tab message bus for the browser, built on the native `BroadcastChannel` API.
8
8
 
9
- [![npm](https://img.shields.io/npm/v/tabus)](https://www.npmjs.com/package/tabus)
10
- [![license](https://img.shields.io/npm/l/tabus)](./LICENSE)
11
- [![types](https://img.shields.io/npm/types/tabus)](./src/core/types.ts)
9
+ [![npm](https://img.shields.io/npm/v/tabus-js)](https://www.npmjs.com/package/tabus-js)
10
+ [![license](https://img.shields.io/npm/l/tabus-js)](./LICENSE)
11
+ [![types](https://img.shields.io/npm/types/tabus-js)](./src/core/types.ts)
12
12
 
13
13
  ## Why
14
14
 
15
15
  When a user signs out in one tab, other open tabs keep showing sensitive data.
16
- `tabus` solves this by letting tabs broadcast events to each other instantly — no server, no WebSockets, no polling.
16
+ `tabus-js` solves this by letting tabs broadcast events to each other instantly — no server, no WebSockets, no polling.
17
17
 
18
18
  ## Install
19
19
 
@@ -23,6 +23,23 @@ npm install tabus-js
23
23
  pnpm add tabus-js
24
24
  ```
25
25
 
26
+ ## Framework compatibility
27
+
28
+ `tabus-js` is framework-agnostic. It works in any environment that runs JavaScript in the browser.
29
+
30
+ | Environment | Supported |
31
+ | --------------------- | ----------- |
32
+ | Vanilla JS | ✅ |
33
+ | React | ✅ |
34
+ | Vue | ✅ |
35
+ | Angular | ✅ |
36
+ | Svelte | ✅ |
37
+ | Next.js (client only) | ✅ |
38
+ | Nuxt (client only) | ✅ |
39
+ | Node.js / SSR | ⚠️ fallback |
40
+
41
+ > **SSR note:** `BroadcastChannel` is a browser API — it does not exist on the server. In SSR environments (Next.js, Nuxt, SvelteKit), create the `Tabus` instance only on the client side. If `BroadcastChannel` is unavailable, `tabus-js` falls back to an in-memory bus automatically and emits a `console.warn`.
42
+
26
43
  ## Quick start
27
44
 
28
45
  ```ts
@@ -48,6 +65,171 @@ bus.emit("logout", { userId: 42 });
48
65
  bus.destroy();
49
66
  ```
50
67
 
68
+ ## Examples
69
+
70
+ ### Vanilla JS
71
+
72
+ ```js
73
+ import { Tabus } from "tabus-js";
74
+
75
+ const bus = new Tabus("my-app");
76
+
77
+ bus.on("notification", ({ message }) => {
78
+ alert(message);
79
+ });
80
+
81
+ document.querySelector("#btn").addEventListener("click", () => {
82
+ bus.emit("notification", { message: "Hello from this tab!" });
83
+ });
84
+ ```
85
+
86
+ ### React
87
+
88
+ ```tsx
89
+ import { useEffect } from "react";
90
+ import { Tabus } from "tabus-js";
91
+
92
+ type AppEvents = {
93
+ logout: { userId: number };
94
+ };
95
+
96
+ const bus = new Tabus<AppEvents>("my-app");
97
+
98
+ function App() {
99
+ useEffect(() => {
100
+ const handler = ({ userId }: { userId: number }) => {
101
+ console.log("Logged out:", userId);
102
+ redirectToLogin();
103
+ };
104
+
105
+ bus.on("logout", handler);
106
+ return () => bus.off("logout", handler);
107
+ }, []);
108
+
109
+ const handleLogout = () => {
110
+ bus.emit("logout", { userId: 42 });
111
+ };
112
+
113
+ return <button onClick={handleLogout}>Logout</button>;
114
+ }
115
+ ```
116
+
117
+ ### Vue
118
+
119
+ ```vue
120
+ <script setup lang="ts">
121
+ import { onMounted, onUnmounted } from "vue";
122
+ import { Tabus } from "tabus-js";
123
+
124
+ type AppEvents = {
125
+ logout: { userId: number };
126
+ };
127
+
128
+ const bus = new Tabus<AppEvents>("my-app");
129
+
130
+ const handler = ({ userId }: { userId: number }) => {
131
+ console.log("Logged out:", userId);
132
+ redirectToLogin();
133
+ };
134
+
135
+ onMounted(() => bus.on("logout", handler));
136
+ onUnmounted(() => bus.off("logout", handler));
137
+
138
+ const handleLogout = () => bus.emit("logout", { userId: 42 });
139
+ </script>
140
+
141
+ <template>
142
+ <button @click="handleLogout">Logout</button>
143
+ </template>
144
+ ```
145
+
146
+ ### Next.js (client only)
147
+
148
+ ```tsx
149
+ "use client";
150
+
151
+ import { useEffect } from "react";
152
+ import { Tabus } from "tabus-js";
153
+
154
+ type AppEvents = {
155
+ logout: { userId: number };
156
+ };
157
+
158
+ // Create outside the component to share across renders
159
+ const bus = new Tabus<AppEvents>("my-app");
160
+
161
+ export function LogoutButton() {
162
+ useEffect(() => {
163
+ const handler = ({ userId }: { userId: number }) => {
164
+ redirectToLogin();
165
+ };
166
+
167
+ bus.on("logout", handler);
168
+ return () => bus.off("logout", handler);
169
+ }, []);
170
+
171
+ return (
172
+ <button onClick={() => bus.emit("logout", { userId: 42 })}>Logout</button>
173
+ );
174
+ }
175
+ ```
176
+
177
+ ### Real-world: sync logout across tabs
178
+
179
+ ```ts
180
+ import { Tabus } from "tabus-js";
181
+
182
+ type AuthEvents = {
183
+ logout: { userId: number };
184
+ sessionExpired: { reason: string };
185
+ };
186
+
187
+ const auth = new Tabus<AuthEvents>("auth");
188
+
189
+ // In your auth service
190
+ auth.on("logout", () => {
191
+ clearLocalStorage();
192
+ redirectToLogin();
193
+ });
194
+
195
+ auth.on("sessionExpired", ({ reason }) => {
196
+ showToast(`Session expired: ${reason}`);
197
+ redirectToLogin();
198
+ });
199
+
200
+ // When the user clicks logout
201
+ function logout(userId: number) {
202
+ auth.emit("logout", { userId });
203
+ }
204
+ ```
205
+
206
+ ### Real-world: sync cart across tabs
207
+
208
+ ```ts
209
+ import { Tabus } from "tabus-js";
210
+
211
+ type CartEvents = {
212
+ itemAdded: { productId: string; qty: number };
213
+ itemRemoved: { productId: string };
214
+ cleared: Record<string, never>;
215
+ };
216
+
217
+ const cart = new Tabus<CartEvents>("cart");
218
+
219
+ cart.on("itemAdded", ({ productId, qty }) => {
220
+ updateCartUI(productId, qty);
221
+ });
222
+
223
+ cart.on("cleared", () => {
224
+ resetCartUI();
225
+ });
226
+
227
+ // When user adds an item
228
+ function addToCart(productId: string, qty: number) {
229
+ cart.emit("itemAdded", { productId, qty });
230
+ }
231
+ ```
232
+
51
233
  ## API
52
234
 
53
235
  ### `new Tabus<Events>(channelName?)`
@@ -88,7 +270,7 @@ These are emitted automatically — you cannot emit them manually.
88
270
 
89
271
  ## Fallback
90
272
 
91
- If `BroadcastChannel` is not available (old browsers, some WebViews), `tabus` falls back to an in-memory bus automatically. Events will still work within the same tab. A `console.warn` is emitted once per channel to notify you.
273
+ If `BroadcastChannel` is not available (old browsers, some WebViews, SSR), `tabus-js` falls back to an in-memory bus automatically. Events will still work within the same tab. A `console.warn` is emitted once per channel to notify you.
92
274
 
93
275
  ## Browser support
94
276
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tabus-js",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Type-safe cross-tab message bus using BroadcastChannel, with in-memory fallback.",
5
5
  "author": "Rody Huancas <rodyhuancas.04@gmail.com>",
6
6
  "license": "MIT",