react-broadcast-sync 1.1.3 → 1.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/CHANGELOG.md +14 -0
- package/README.md +56 -48
- package/demo/react-broadcast-sync-demo/package.json +1 -1
- package/dist/index.cjs.js +1 -1
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.esm.js.map +1 -1
- package/dist/types/types.d.ts +15 -6
- package/dist/utils/debug.d.ts +4 -2
- package/dist/utils/messageUtils.d.ts +3 -6
- package/package.json +1 -1
- package/src/hooks/useBroadcastChannel.ts +75 -55
- package/src/tests/messageUtils.test.ts +6 -6
- package/src/tests/useBroadcastChannel.test.tsx +14 -14
- package/src/types/types.ts +18 -7
- package/src/utils/debug.ts +4 -2
- package/src/utils/messageUtils.ts +6 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,17 @@
|
|
|
1
|
+
# [1.2.0](https://github.com/IdanShalem/react-broadcast-sync/compare/v1.1.4...v1.2.0) (2025-06-08)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* clearReceivedMessages and clearSentMessages improved logic ([92916d8](https://github.com/IdanShalem/react-broadcast-sync/commit/92916d80f3a79cadb2f34c11f3f9759f6d74f8b2))
|
|
7
|
+
|
|
8
|
+
## [1.1.4](https://github.com/IdanShalem/react-broadcast-sync/compare/v1.1.3...v1.1.4) (2025-06-03)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* removed icon ([3488a8d](https://github.com/IdanShalem/react-broadcast-sync/commit/3488a8d9526e5561f5ffdd14dfcce5ebb20c6838))
|
|
14
|
+
|
|
1
15
|
## [1.1.3](https://github.com/IdanShalem/react-broadcast-sync/compare/v1.1.2...v1.1.3) (2025-06-03)
|
|
2
16
|
|
|
3
17
|
|
package/README.md
CHANGED
|
@@ -40,25 +40,25 @@ Easily sync UI state or user events across browser tabs in React apps — notifi
|
|
|
40
40
|
|
|
41
41
|
## Table of Contents
|
|
42
42
|
|
|
43
|
-
- [Features](
|
|
44
|
-
- [Demo App](
|
|
45
|
-
- [Installation](
|
|
46
|
-
- [Quick Start](
|
|
47
|
-
- [Advanced Usage](
|
|
48
|
-
- [BroadcastProvider](
|
|
49
|
-
- [API Reference](
|
|
50
|
-
- [Best Practices](
|
|
51
|
-
- [Common Use Cases](
|
|
52
|
-
- [Performance Considerations](
|
|
53
|
-
- [Troubleshooting](#-
|
|
54
|
-
- [Testing](
|
|
55
|
-
- [Browser Support](
|
|
56
|
-
- [Coming Soon](
|
|
57
|
-
- [Versioning & Releases](
|
|
58
|
-
- [Contributing](
|
|
59
|
-
- [License](
|
|
60
|
-
|
|
61
|
-
##
|
|
43
|
+
- [Features](#features)
|
|
44
|
+
- [Demo App](#demo-app)
|
|
45
|
+
- [Installation](#installation)
|
|
46
|
+
- [Quick Start](#quick-start)
|
|
47
|
+
- [Advanced Usage](#️advanced-usage)
|
|
48
|
+
- [BroadcastProvider](#using-broadcastprovider)
|
|
49
|
+
- [API Reference](#api-reference)
|
|
50
|
+
- [Best Practices](#best-practices)
|
|
51
|
+
- [Common Use Cases](#common-use-cases)
|
|
52
|
+
- [Performance Considerations](#performance-considerations)
|
|
53
|
+
- [Troubleshooting](#-roubleshooting)
|
|
54
|
+
- [Testing](#testing)
|
|
55
|
+
- [Browser Support](#browser-support)
|
|
56
|
+
- [Coming Soon](#coming-soon)
|
|
57
|
+
- [Versioning & Releases](#versioning--releases)
|
|
58
|
+
- [Contributing](#contributing)
|
|
59
|
+
- [License](#license)
|
|
60
|
+
|
|
61
|
+
## Features
|
|
62
62
|
|
|
63
63
|
- Simple and intuitive API
|
|
64
64
|
- Real-time synchronization across tabs
|
|
@@ -118,7 +118,7 @@ pnpm add react-broadcast-sync
|
|
|
118
118
|
import { useBroadcastChannel } from 'react-broadcast-sync';
|
|
119
119
|
|
|
120
120
|
function MyComponent() {
|
|
121
|
-
const { messages, postMessage,
|
|
121
|
+
const { messages, postMessage, clearReceivedMessages } = useBroadcastChannel('my-channel');
|
|
122
122
|
|
|
123
123
|
const handleSend = () => {
|
|
124
124
|
postMessage('greeting', { text: 'Hello from another tab!' });
|
|
@@ -130,7 +130,7 @@ function MyComponent() {
|
|
|
130
130
|
{messages.map(msg => (
|
|
131
131
|
<div key={msg.id}>
|
|
132
132
|
{msg.message.text}
|
|
133
|
-
<button onClick={() =>
|
|
133
|
+
<button onClick={() => clearReceivedMessages({ ids: [msg.id] })}>Clear</button>
|
|
134
134
|
</div>
|
|
135
135
|
))}
|
|
136
136
|
</>
|
|
@@ -148,9 +148,8 @@ const {
|
|
|
148
148
|
messages,
|
|
149
149
|
sentMessages,
|
|
150
150
|
postMessage,
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
clearSentMessage,
|
|
151
|
+
clearReceivedMessages,
|
|
152
|
+
clearSentMessages,
|
|
154
153
|
error,
|
|
155
154
|
} = useBroadcastChannel('my-channel', {
|
|
156
155
|
sourceName: 'my-tab',
|
|
@@ -211,9 +210,8 @@ const {
|
|
|
211
210
|
messages,
|
|
212
211
|
sentMessages,
|
|
213
212
|
postMessage,
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
clearSentMessage,
|
|
213
|
+
clearReceivedMessages,
|
|
214
|
+
clearSentMessages,
|
|
217
215
|
error,
|
|
218
216
|
} = useBroadcastChannel(channelName, options);
|
|
219
217
|
```
|
|
@@ -252,9 +250,8 @@ interface BroadcastActions {
|
|
|
252
250
|
messages: BroadcastMessage[]; // Received messages
|
|
253
251
|
sentMessages: BroadcastMessage[]; // Messages sent by this instance
|
|
254
252
|
postMessage: (type: string, content: any, options?: SendMessageOptions) => void;
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
clearSentMessage: (id: string) => void;
|
|
253
|
+
clearReceivedMessages: (opts?: { ids?: string[]; types?: string[]; sources?: string[] }) => void;
|
|
254
|
+
clearSentMessages: (opts?: { ids?: string[]; types?: string[]; sync?: boolean }) => void;
|
|
258
255
|
error: string | null; // Current error state
|
|
259
256
|
}
|
|
260
257
|
```
|
|
@@ -263,16 +260,33 @@ interface BroadcastActions {
|
|
|
263
260
|
|
|
264
261
|
Returns an object with:
|
|
265
262
|
|
|
266
|
-
| Property
|
|
267
|
-
|
|
|
268
|
-
| `channelName`
|
|
269
|
-
| `messages`
|
|
270
|
-
| `sentMessages`
|
|
271
|
-
| `postMessage()`
|
|
272
|
-
| `
|
|
273
|
-
| `
|
|
274
|
-
| `
|
|
275
|
-
|
|
263
|
+
| Property | Type | Description |
|
|
264
|
+
| ------------------------- | -------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
265
|
+
| `channelName` | `string` | The resolved channel name (includes namespace) |
|
|
266
|
+
| `messages` | `BroadcastMessage[]` | All received messages from other tabs |
|
|
267
|
+
| `sentMessages` | `BroadcastMessage[]` | Messages sent from this tab |
|
|
268
|
+
| `postMessage()` | `function` | Send a message to all tabs |
|
|
269
|
+
| `clearReceivedMessages()` | `function` | Clear received messages. No filters ⇒ clear all. With filters, a message is deleted only if it matches **every** provided filter (`ids`, `types`, `sources`). Empty arrays act as wildcards. |
|
|
270
|
+
| `clearSentMessages()` | `function` | Clear messages this tab sent (same matching rules). Pass `sync: true` to broadcast the clear to other tabs. |
|
|
271
|
+
| `error` | `string \| null` | Any runtime error from the channel |
|
|
272
|
+
|
|
273
|
+
#### Clearing examples
|
|
274
|
+
|
|
275
|
+
```tsx
|
|
276
|
+
// Clear everything we've received
|
|
277
|
+
clearReceivedMessages();
|
|
278
|
+
|
|
279
|
+
// Clear all messages we sent and broadcast the clear to other tabs
|
|
280
|
+
clearSentMessages({ sync: true });
|
|
281
|
+
|
|
282
|
+
// Clear by id or by type (OR inside each array)
|
|
283
|
+
clearReceivedMessages({ ids: ['123'] }); // id match
|
|
284
|
+
clearReceivedMessages({ types: ['alert', 'chat'] }); // type match
|
|
285
|
+
|
|
286
|
+
// Combine filters (logical AND between filters)
|
|
287
|
+
// Removes messages whose id is '123' AND type is 'alert'
|
|
288
|
+
clearSentMessages({ ids: ['123'], types: ['alert'] });
|
|
289
|
+
```
|
|
276
290
|
|
|
277
291
|
#### Send Options:
|
|
278
292
|
|
|
@@ -378,9 +392,9 @@ function TabStatus() {
|
|
|
378
392
|
- Implement debouncing for rapid state changes
|
|
379
393
|
- Consider using `expirationDuration` for temporary messages
|
|
380
394
|
|
|
381
|
-
### Memory Management
|
|
395
|
+
### Memory Management
|
|
382
396
|
|
|
383
|
-
- Clear messages when they're no longer needed
|
|
397
|
+
- Clear messages when they're no longer needed using `clearReceivedMessages` / `clearSentMessages`
|
|
384
398
|
- Use `cleaningInterval` to automatically remove expired messages
|
|
385
399
|
- Implement proper cleanup in component unmount
|
|
386
400
|
|
|
@@ -421,12 +435,6 @@ function ChatComponent() {
|
|
|
421
435
|
- For infrequent updates: 0ms (no debounce)
|
|
422
436
|
- Adjust `cleaningInterval` based on your message expiration needs
|
|
423
437
|
|
|
424
|
-
### Memory Management
|
|
425
|
-
|
|
426
|
-
- Clear messages when they're no longer needed using `clearMessage` or `clearAllMessages`
|
|
427
|
-
- Use message expiration for temporary data
|
|
428
|
-
- Consider using `keepLatestMessage: true` for status updates to prevent memory buildup
|
|
429
|
-
|
|
430
438
|
## Troubleshooting
|
|
431
439
|
|
|
432
440
|
### Common Issues
|
package/dist/index.cjs.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var e=require("react"),r=require("react/jsx-runtime");const t="__INTERNAL__",s=()=>Math.random().toString(36).substr(2,9),n=(e,r)=>{const t=`${s()}-${e}-${r}`;return btoa(t).replace(/=+$/,"")},
|
|
1
|
+
"use strict";var e=require("react"),r=require("react/jsx-runtime");const t="__INTERNAL__",s=()=>Math.random().toString(36).substr(2,9),n=(e,r)=>{const t=`${s()}-${e}-${r}`;return btoa(t).replace(/=+$/,"")},o=e=>!!e.expirationDate&&e.expirationDate<Date.now(),a=(e,r,t,s={})=>{var o;const a=Date.now();return{id:n(t,a),type:e,message:r,timestamp:a,source:t,expirationDate:null!==(o=s.expirationDate)&&void 0!==o?o:s.expirationDuration?a+s.expirationDuration:void 0}},c=(e,r,s="")=>{const n=btoa(`react-broadcast-sync:${e}:${`${r}-${s}`}`);return`${t}:${e}:${n}`},l=e=>e.startsWith(`${t}:`),i=(e,r,t)=>{if("undefined"==typeof process||!process.env||"true"!==process.env.REACT_APP_DEBUG_BROADCAST)return;const s=`[${(new Date).toISOString()}] [react-broadcast-sync]`;switch(e){case"info":console.log(s,r,t||"");break;case"warn":console.warn(s,r,t||"");break;case"error":console.error(s,r,t||"")}},u={created:e=>i("info","Channel created:",e),closed:e=>i("info","Channel closed:",e)},d={sent:e=>i("info","Message sent:",e),received:e=>i("info","Message received:",e),cleared:e=>i("info","Message cleared:",e),expired:e=>i("info","Message expired:",e),duplicate:e=>i("warn","Duplicate message ignored:",e),allSentCleared:()=>i("info","All sent messages cleared"),allReceivedCleared:()=>i("info","All received messages cleared")},p={started:()=>i("info","Cleanup started"),completed:e=>i("info","Cleanup completed:",{removedCount:e})},f=e=>i("error","Error:",e),h="CLEAR_SENT_MESSAGES",g=(r,t={})=>{const{sourceName:n,cleaningInterval:i=1e3,keepLatestMessage:g=!1,registeredTypes:v=[],namespace:m="",deduplicationTTL:E=3e5,cleanupDebounceMs:y=0}=t,[C,b]=e.useState([]),[S,M]=e.useState([]),[w,x]=e.useState(null),B=e.useRef(null),D=e.useRef(new Map),$=e.useRef(n||`tab-${s()}`).current,A=e.useRef({CLEAR_SENT_MESSAGES:c(h,r,m)}).current,R=e.useMemo((()=>`${r}-${m}`),[r,m]),T=e.useCallback((()=>{b((e=>e.filter((e=>!o(e)))))}),[]),_=e.useRef(y>0?((e,r)=>{let t,s=null,n=null;const o=(...o)=>{n=o,null!==s&&clearTimeout(s),s=setTimeout((()=>{s=null,null!==n&&(t=e(...n),n=null)}),r)};return o.cancel=()=>{null!==s&&(clearTimeout(s),s=null,n=null)},o.flush=()=>(null!==s&&(clearTimeout(s),s=null,null!==n&&(t=e(...n),n=null)),t),o})(T,y):Object.assign(T,{cancel:()=>{}})).current,k=e.useCallback((e=>{x(e),setTimeout((()=>x(null)),3e3)}),[]),P=e.useCallback(((e,r,t={})=>{const s=B.current;if(!s){const e="Channel not available";return f(e),void k(e)}try{const n=a(e,r,$,t);s.postMessage(n),d.sent(n),M((e=>[...e,n]))}catch(e){const r="Failed to send message";f(r),k(r)}}),[$,k]),L=e.useCallback(((e={})=>{const r=Boolean(e.ids&&e.ids.length||e.types&&e.types.length||e.sources&&e.sources.length);b((t=>r?t.filter((r=>!(e.ids&&e.ids.includes(r.id)||e.types&&e.types.includes(r.type)||e.sources&&e.sources.includes(r.source)))):[]))}),[]),N=e.useCallback(((e={})=>{var r,t,s;const{ids:n=[],types:o=[],sync:c=!1}=null!=e?e:{};M((e=>n.length>0||o.length>0?e.filter((e=>{if(e.source!==$)return!0;const r=0===n.length||n.includes(e.id),t=0===o.length||o.includes(e.type);return!(r&&t)})):[])),c&&(null===(r=B.current)||void 0===r||r.postMessage(a(A.CLEAR_SENT_MESSAGES,{ids:null!==(t=e.ids)&&void 0!==t?t:[],types:null!==(s=e.types)&&void 0!==s?s:[]},$)))}),[]),j=e.useCallback((e=>{try{const r=e.data;if(d.received(r),!(e=>Boolean(e&&"object"==typeof e&&"id"in e))(r))return;if(r.source===$)return;if(!l(r.type)&&v.length>0&&!v.includes(r.type))return;if(o(r))return void d.expired(r.id);if((e=>e&&"object"==typeof e&&"string"==typeof e.type&&l(e.type)&&"string"==typeof e.source)(r)&&r.type===A.CLEAR_SENT_MESSAGES){const{ids:e=[],types:t=[]}=r.message||{};return void b((s=>s.filter((s=>{if(s.source!==r.source)return!0;const n=0===e.length||e.includes(s.id),o=0===t.length||t.includes(s.type);return!(n&&o)}))))}const t=Date.now(),s=D.current.get(r.id);if(s&&t-s<E)return void d.duplicate(r.id);D.current.set(r.id,t),b((e=>g?[r]:[...e,r]))}catch(e){const r="Error processing broadcast message";f(r),k(r)}}),[$,v,g,k,A,E]);return e.useEffect((()=>{if("undefined"==typeof BroadcastChannel){const e="BroadcastChannel is not supported in this browser. Please check browser compatibility.";return f(e),void k(e)}const e=new BroadcastChannel(R);return B.current=e,u.created(R),e.addEventListener("message",j),()=>{e.removeEventListener("message",j),e.close(),u.closed(R)}}),[R,j,k]),e.useEffect((()=>{if(i<=0)return;const e=setInterval((()=>{p.started();const e=C.length;_(),p.completed(e-C.length)}),i);return()=>{var r;clearInterval(e),y>0&&(null===(r=_.cancel)||void 0===r||r.call(_))}}),[i,_,y]),e.useEffect((()=>{const e=setInterval((()=>{const e=Date.now();for(const[r,t]of D.current.entries())e-t>=E&&D.current.delete(r)}),6e4);return()=>clearInterval(e)}),[E]),{channelName:R,messages:C,sentMessages:S,postMessage:P,clearReceivedMessages:L,clearSentMessages:N,error:w}},v=e.createContext(void 0);class m extends e.Component{constructor(){super(...arguments),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e){console.error("BroadcastChannel error:",e)}render(){return this.state.hasError?r.jsx("div",{children:"Error occurred in BroadcastProvider"}):this.props.children}}exports.BroadcastProvider=({children:t,channelName:s})=>{const{messages:n,error:o}=g(s),[a,c]=e.useState(null),l=e.useRef(null);e.useEffect((()=>{if("undefined"==typeof BroadcastChannel)return void c("BroadcastChannel is not supported in this browser. Please check browser compatibility.");const e=new BroadcastChannel(s);return l.current=e,()=>{e.close()}}),[s]);const i=e.useMemo((()=>({messages:n,error:a||o,channel:l.current})),[n,a,o]);return r.jsx(m,{children:r.jsx(v.Provider,{value:i,children:t})})},exports.useBroadcastChannel=g,exports.useBroadcastProvider=()=>{const r=e.useContext(v);if(!r)throw new Error("useBroadcastProvider must be used within a BroadcastProvider");return r};
|
|
2
2
|
//# sourceMappingURL=index.cjs.js.map
|
package/dist/index.cjs.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs.js","sources":["../src/utils/messageUtils.ts","../src/utils/debug.ts","../src/hooks/useBroadcastChannel.ts","../src/BroadcastProvider.tsx"],"sourcesContent":[null,null,null,null],"names":["INTERNAL_PREFIX","generateRandomPart","Math","random","toString","substr","generateMessageId","source","timestamp","raw","btoa","replace","isMessageExpired","message","expirationDate","Date","now","getInternalMessageType","baseType","channelName","namespace","hash","isInternalType","
|
|
1
|
+
{"version":3,"file":"index.cjs.js","sources":["../src/utils/messageUtils.ts","../src/utils/debug.ts","../src/hooks/useBroadcastChannel.ts","../src/BroadcastProvider.tsx"],"sourcesContent":[null,null,null,null],"names":["INTERNAL_PREFIX","generateRandomPart","Math","random","toString","substr","generateMessageId","source","timestamp","raw","btoa","replace","isMessageExpired","message","expirationDate","Date","now","createMessage","type","content","options","id","_a","expirationDuration","undefined","getInternalMessageType","baseType","channelName","namespace","hash","isInternalType","startsWith","log","level","data","process","env","REACT_APP_DEBUG_BROADCAST","prefix","toISOString","console","warn","error","debug","created","name","closed","sent","received","cleared","messageId","expired","duplicate","allSentCleared","allReceivedCleared","started","completed","removedCount","INTERNAL_MESSAGE_TYPES","useBroadcastChannel","sourceName","cleaningInterval","keepLatestMessage","registeredTypes","deduplicationTTL","cleanupDebounceMs","messages","setMessages","useState","sentMessages","setSentMessages","setError","channel","useRef","receivedMessageIds","Map","current","internalTypes","CLEAR_SENT_MESSAGES","resolvedChannelName","useMemo","performCleanup","useCallback","prev","filter","msg","debouncedCleanup","debounce","fn","wait","result","timeoutId","lastArgs","debounced","args","clearTimeout","setTimeout","cancel","flush","Object","assign","setErrorMessage","postMessage","messageType","messageContent","channelCurrent","e","clearReceivedMessages","hasFilters","Boolean","ids","length","types","sources","includes","clearSentMessages","sync","idMatches","typeMatches","_b","_c","handleMessage","event","isValidMessage","isValidInternalClearMessage","receivedAt","get","set","useEffect","BroadcastChannel","bc","addEventListener","removeEventListener","close","interval","setInterval","before","clearInterval","call","entries","delete","BroadcastChannelContext","createContext","ErrorBoundary","React","Component","constructor","this","state","hasError","getDerivedStateFromError","componentDidCatch","render","_jsx","props","children","BroadcastProvider","hookError","value","Provider","useBroadcastProvider","context","useContext","Error"],"mappings":"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"}
|
package/dist/index.esm.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e,{useState as r,useRef as t,useMemo as n,useCallback as s,useEffect as o,createContext as a,useContext as c}from"react";import{jsx as
|
|
1
|
+
import e,{useState as r,useRef as t,useMemo as n,useCallback as s,useEffect as o,createContext as a,useContext as c}from"react";import{jsx as l}from"react/jsx-runtime";const i="__INTERNAL__",u=()=>Math.random().toString(36).substr(2,9),d=(e,r)=>{const t=`${u()}-${e}-${r}`;return btoa(t).replace(/=+$/,"")},p=e=>!!e.expirationDate&&e.expirationDate<Date.now(),h=(e,r,t,n={})=>{var s;const o=Date.now();return{id:d(t,o),type:e,message:r,timestamp:o,source:t,expirationDate:null!==(s=n.expirationDate)&&void 0!==s?s:n.expirationDuration?o+n.expirationDuration:void 0}},g=(e,r,t="")=>{const n=btoa(`react-broadcast-sync:${e}:${`${r}-${t}`}`);return`${i}:${e}:${n}`},f=e=>e.startsWith(`${i}:`),v=(e,r,t)=>{if("undefined"==typeof process||!process.env||"true"!==process.env.REACT_APP_DEBUG_BROADCAST)return;const n=`[${(new Date).toISOString()}] [react-broadcast-sync]`;switch(e){case"info":console.log(n,r,t||"");break;case"warn":console.warn(n,r,t||"");break;case"error":console.error(n,r,t||"")}},m={created:e=>v("info","Channel created:",e),closed:e=>v("info","Channel closed:",e)},y={sent:e=>v("info","Message sent:",e),received:e=>v("info","Message received:",e),cleared:e=>v("info","Message cleared:",e),expired:e=>v("info","Message expired:",e),duplicate:e=>v("warn","Duplicate message ignored:",e),allSentCleared:()=>v("info","All sent messages cleared"),allReceivedCleared:()=>v("info","All received messages cleared")},E={started:()=>v("info","Cleanup started"),completed:e=>v("info","Cleanup completed:",{removedCount:e})},S=e=>v("error","Error:",e),C="CLEAR_SENT_MESSAGES",b=(e,a={})=>{const{sourceName:c,cleaningInterval:l=1e3,keepLatestMessage:i=!1,registeredTypes:d=[],namespace:v="",deduplicationTTL:b=3e5,cleanupDebounceMs:w=0}=a,[M,D]=r([]),[$,A]=r([]),[T,_]=r(null),B=t(null),x=t(new Map),L=t(c||`tab-${u()}`).current,N=t({CLEAR_SENT_MESSAGES:g(C,e,v)}).current,R=n((()=>`${e}-${v}`),[e,v]),P=s((()=>{D((e=>e.filter((e=>!p(e)))))}),[]),I=t(w>0?((e,r)=>{let t,n=null,s=null;const o=(...o)=>{s=o,null!==n&&clearTimeout(n),n=setTimeout((()=>{n=null,null!==s&&(t=e(...s),s=null)}),r)};return o.cancel=()=>{null!==n&&(clearTimeout(n),n=null,s=null)},o.flush=()=>(null!==n&&(clearTimeout(n),n=null,null!==s&&(t=e(...s),s=null)),t),o})(P,w):Object.assign(P,{cancel:()=>{}})).current,k=s((e=>{_(e),setTimeout((()=>_(null)),3e3)}),[]),G=s(((e,r,t={})=>{const n=B.current;if(!n){const e="Channel not available";return S(e),void k(e)}try{const s=h(e,r,L,t);n.postMessage(s),y.sent(s),A((e=>[...e,s]))}catch(e){const r="Failed to send message";S(r),k(r)}}),[L,k]),j=s(((e={})=>{const r=Boolean(e.ids&&e.ids.length||e.types&&e.types.length||e.sources&&e.sources.length);D((t=>r?t.filter((r=>!(e.ids&&e.ids.includes(r.id)||e.types&&e.types.includes(r.type)||e.sources&&e.sources.includes(r.source)))):[]))}),[]),O=s(((e={})=>{var r,t,n;const{ids:s=[],types:o=[],sync:a=!1}=null!=e?e:{};A((e=>s.length>0||o.length>0?e.filter((e=>{if(e.source!==L)return!0;const r=0===s.length||s.includes(e.id),t=0===o.length||o.includes(e.type);return!(r&&t)})):[])),a&&(null===(r=B.current)||void 0===r||r.postMessage(h(N.CLEAR_SENT_MESSAGES,{ids:null!==(t=e.ids)&&void 0!==t?t:[],types:null!==(n=e.types)&&void 0!==n?n:[]},L)))}),[]),F=s((e=>{try{const r=e.data;if(y.received(r),!(e=>Boolean(e&&"object"==typeof e&&"id"in e))(r))return;if(r.source===L)return;if(!f(r.type)&&d.length>0&&!d.includes(r.type))return;if(p(r))return void y.expired(r.id);if((e=>e&&"object"==typeof e&&"string"==typeof e.type&&f(e.type)&&"string"==typeof e.source)(r)&&r.type===N.CLEAR_SENT_MESSAGES){const{ids:e=[],types:t=[]}=r.message||{};return void D((n=>n.filter((n=>{if(n.source!==r.source)return!0;const s=0===e.length||e.includes(n.id),o=0===t.length||t.includes(n.type);return!(s&&o)}))))}const t=Date.now(),n=x.current.get(r.id);if(n&&t-n<b)return void y.duplicate(r.id);x.current.set(r.id,t),D((e=>i?[r]:[...e,r]))}catch(e){const r="Error processing broadcast message";S(r),k(r)}}),[L,d,i,k,N,b]);return o((()=>{if("undefined"==typeof BroadcastChannel){const e="BroadcastChannel is not supported in this browser. Please check browser compatibility.";return S(e),void k(e)}const e=new BroadcastChannel(R);return B.current=e,m.created(R),e.addEventListener("message",F),()=>{e.removeEventListener("message",F),e.close(),m.closed(R)}}),[R,F,k]),o((()=>{if(l<=0)return;const e=setInterval((()=>{E.started();const e=M.length;I(),E.completed(e-M.length)}),l);return()=>{var r;clearInterval(e),w>0&&(null===(r=I.cancel)||void 0===r||r.call(I))}}),[l,I,w]),o((()=>{const e=setInterval((()=>{const e=Date.now();for(const[r,t]of x.current.entries())e-t>=b&&x.current.delete(r)}),6e4);return()=>clearInterval(e)}),[b]),{channelName:R,messages:M,sentMessages:$,postMessage:G,clearReceivedMessages:j,clearSentMessages:O,error:T}},w=a(void 0);class M extends e.Component{constructor(){super(...arguments),this.state={hasError:!1}}static getDerivedStateFromError(){return{hasError:!0}}componentDidCatch(e){console.error("BroadcastChannel error:",e)}render(){return this.state.hasError?l("div",{children:"Error occurred in BroadcastProvider"}):this.props.children}}const D=({children:r,channelName:s})=>{const{messages:a,error:c}=b(s),[i,u]=e.useState(null),d=t(null);o((()=>{if("undefined"==typeof BroadcastChannel)return void u("BroadcastChannel is not supported in this browser. Please check browser compatibility.");const e=new BroadcastChannel(s);return d.current=e,()=>{e.close()}}),[s]);const p=n((()=>({messages:a,error:i||c,channel:d.current})),[a,i,c]);return l(M,{children:l(w.Provider,{value:p,children:r})})},$=()=>{const e=c(w);if(!e)throw new Error("useBroadcastProvider must be used within a BroadcastProvider");return e};export{D as BroadcastProvider,b as useBroadcastChannel,$ as useBroadcastProvider};
|
|
2
2
|
//# sourceMappingURL=index.esm.js.map
|
package/dist/index.esm.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.esm.js","sources":["../src/utils/messageUtils.ts","../src/utils/debug.ts","../src/hooks/useBroadcastChannel.ts","../src/BroadcastProvider.tsx"],"sourcesContent":[null,null,null,null],"names":["INTERNAL_PREFIX","generateRandomPart","Math","random","toString","substr","generateMessageId","source","timestamp","raw","btoa","replace","isMessageExpired","message","expirationDate","Date","now","getInternalMessageType","baseType","channelName","namespace","hash","isInternalType","
|
|
1
|
+
{"version":3,"file":"index.esm.js","sources":["../src/utils/messageUtils.ts","../src/utils/debug.ts","../src/hooks/useBroadcastChannel.ts","../src/BroadcastProvider.tsx"],"sourcesContent":[null,null,null,null],"names":["INTERNAL_PREFIX","generateRandomPart","Math","random","toString","substr","generateMessageId","source","timestamp","raw","btoa","replace","isMessageExpired","message","expirationDate","Date","now","createMessage","type","content","options","id","_a","expirationDuration","undefined","getInternalMessageType","baseType","channelName","namespace","hash","isInternalType","startsWith","log","level","data","process","env","REACT_APP_DEBUG_BROADCAST","prefix","toISOString","console","warn","error","debug","created","name","closed","sent","received","cleared","messageId","expired","duplicate","allSentCleared","allReceivedCleared","started","completed","removedCount","INTERNAL_MESSAGE_TYPES","useBroadcastChannel","sourceName","cleaningInterval","keepLatestMessage","registeredTypes","deduplicationTTL","cleanupDebounceMs","messages","setMessages","useState","sentMessages","setSentMessages","setError","channel","useRef","receivedMessageIds","Map","current","internalTypes","CLEAR_SENT_MESSAGES","resolvedChannelName","useMemo","performCleanup","useCallback","prev","filter","msg","debouncedCleanup","debounce","fn","wait","result","timeoutId","lastArgs","debounced","args","clearTimeout","setTimeout","cancel","flush","Object","assign","setErrorMessage","postMessage","messageType","messageContent","channelCurrent","e","clearReceivedMessages","hasFilters","Boolean","ids","length","types","sources","includes","clearSentMessages","sync","idMatches","typeMatches","_b","_c","handleMessage","event","isValidMessage","isValidInternalClearMessage","receivedAt","get","set","useEffect","BroadcastChannel","bc","addEventListener","removeEventListener","close","interval","setInterval","before","clearInterval","call","entries","delete","BroadcastChannelContext","createContext","ErrorBoundary","React","Component","constructor","this","state","hasError","getDerivedStateFromError","componentDidCatch","render","_jsx","props","children","BroadcastProvider","hookError","value","Provider","useBroadcastProvider","context","useContext","Error"],"mappings":"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"}
|
package/dist/types/types.d.ts
CHANGED
|
@@ -43,6 +43,16 @@ export interface BroadcastMessage {
|
|
|
43
43
|
/** Optional expiration timestamp */
|
|
44
44
|
expirationDate?: number;
|
|
45
45
|
}
|
|
46
|
+
export interface ClearOptions {
|
|
47
|
+
ids?: string[];
|
|
48
|
+
types?: string[];
|
|
49
|
+
}
|
|
50
|
+
export interface ClearReceivedMessagesOptions extends ClearOptions {
|
|
51
|
+
sources?: string[];
|
|
52
|
+
}
|
|
53
|
+
export interface ClearSentMessagesOptions extends ClearOptions {
|
|
54
|
+
sync?: boolean;
|
|
55
|
+
}
|
|
46
56
|
/**
|
|
47
57
|
* Actions and state provided by the broadcast channel hook
|
|
48
58
|
*/
|
|
@@ -55,12 +65,11 @@ export interface BroadcastActions {
|
|
|
55
65
|
sentMessages: BroadcastMessage[];
|
|
56
66
|
/** Function to send a message */
|
|
57
67
|
postMessage: (messageType: string, messageContent: any, options?: SendMessageOptions) => void;
|
|
58
|
-
/** Function to clear
|
|
59
|
-
|
|
60
|
-
/** Function to clear
|
|
61
|
-
|
|
62
|
-
/** Function to clear a specific sent message */
|
|
63
|
-
clearSentMessage: (id: string) => void;
|
|
68
|
+
/** Function to clear received messages */
|
|
69
|
+
clearReceivedMessages: (options?: ClearReceivedMessagesOptions) => void;
|
|
70
|
+
/** Function to clear sent messages */
|
|
71
|
+
clearSentMessages: (options?: ClearSentMessagesOptions) => void;
|
|
64
72
|
/** Current error state */
|
|
65
73
|
error: string | null;
|
|
66
74
|
}
|
|
75
|
+
export type ClearMessage = 'CLEAR_SENT_MESSAGES';
|
package/dist/utils/debug.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export declare const debug: {
|
|
2
2
|
channel: {
|
|
3
|
-
created: (
|
|
4
|
-
closed: (
|
|
3
|
+
created: (name: string) => void;
|
|
4
|
+
closed: (name: string) => void;
|
|
5
5
|
};
|
|
6
6
|
message: {
|
|
7
7
|
sent: (message: any) => void;
|
|
@@ -9,6 +9,8 @@ export declare const debug: {
|
|
|
9
9
|
cleared: (messageId: string) => void;
|
|
10
10
|
expired: (messageId: string) => void;
|
|
11
11
|
duplicate: (messageId: string) => void;
|
|
12
|
+
allSentCleared: () => void;
|
|
13
|
+
allReceivedCleared: () => void;
|
|
12
14
|
};
|
|
13
15
|
cleanup: {
|
|
14
16
|
started: () => void;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ClearMessage } from '../types/types';
|
|
1
2
|
export declare const generateRandomPart: () => string;
|
|
2
3
|
export declare const generateSourceName: () => string;
|
|
3
4
|
export declare const generateMessageId: (source: string, timestamp: number) => string;
|
|
@@ -9,13 +10,9 @@ export declare const createMessage: (type: string, content: any, source: string,
|
|
|
9
10
|
expirationDate?: number;
|
|
10
11
|
expirationDuration?: number;
|
|
11
12
|
}) => any;
|
|
12
|
-
export declare const getInternalMessageType: (baseType:
|
|
13
|
+
export declare const getInternalMessageType: (baseType: ClearMessage, channelName: string, namespace?: string) => string;
|
|
13
14
|
export declare const isInternalType: (type: string) => boolean;
|
|
14
|
-
export declare const isValidInternalClearMessage: (message: any) =>
|
|
15
|
-
id: string;
|
|
16
|
-
type: string;
|
|
17
|
-
source: string;
|
|
18
|
-
};
|
|
15
|
+
export declare const isValidInternalClearMessage: (message: any) => boolean;
|
|
19
16
|
export declare const debounce: <T extends (...args: any[]) => any>(fn: T, wait: number) => ((...args: Parameters<T>) => void) & {
|
|
20
17
|
cancel: () => void;
|
|
21
18
|
flush: () => ReturnType<T> | undefined;
|
package/package.json
CHANGED
|
@@ -3,6 +3,9 @@ import {
|
|
|
3
3
|
BroadcastActions,
|
|
4
4
|
BroadcastMessage,
|
|
5
5
|
BroadcastOptions,
|
|
6
|
+
ClearMessage,
|
|
7
|
+
ClearReceivedMessagesOptions,
|
|
8
|
+
ClearSentMessagesOptions,
|
|
6
9
|
SendMessageOptions,
|
|
7
10
|
} from '../types/types';
|
|
8
11
|
import {
|
|
@@ -17,9 +20,8 @@ import {
|
|
|
17
20
|
} from '../utils/messageUtils';
|
|
18
21
|
import { debug } from '../utils/debug';
|
|
19
22
|
|
|
20
|
-
const INTERNAL_MESSAGE_TYPES = {
|
|
21
|
-
|
|
22
|
-
CLEAR_ALL_MESSAGES: 'CLEAR_ALL_MESSAGES',
|
|
23
|
+
const INTERNAL_MESSAGE_TYPES: Record<string, ClearMessage> = {
|
|
24
|
+
CLEAR_SENT_MESSAGES: 'CLEAR_SENT_MESSAGES',
|
|
23
25
|
} as const;
|
|
24
26
|
|
|
25
27
|
export const useBroadcastChannel = (
|
|
@@ -46,13 +48,8 @@ export const useBroadcastChannel = (
|
|
|
46
48
|
const receivedMessageIds = useRef(new Map<string, number>());
|
|
47
49
|
const source = useRef(sourceName || generateSourceName()).current;
|
|
48
50
|
const internalTypes = useRef({
|
|
49
|
-
|
|
50
|
-
INTERNAL_MESSAGE_TYPES.
|
|
51
|
-
channelName,
|
|
52
|
-
namespace
|
|
53
|
-
),
|
|
54
|
-
CLEAR_ALL_MESSAGES: getInternalMessageType(
|
|
55
|
-
INTERNAL_MESSAGE_TYPES.CLEAR_ALL_MESSAGES,
|
|
51
|
+
CLEAR_SENT_MESSAGES: getInternalMessageType(
|
|
52
|
+
INTERNAL_MESSAGE_TYPES.CLEAR_SENT_MESSAGES,
|
|
56
53
|
channelName,
|
|
57
54
|
namespace
|
|
58
55
|
),
|
|
@@ -80,42 +77,6 @@ export const useBroadcastChannel = (
|
|
|
80
77
|
setTimeout(() => setError(null), 3000);
|
|
81
78
|
}, []);
|
|
82
79
|
|
|
83
|
-
const clearMessage = useCallback(
|
|
84
|
-
(id: string) => {
|
|
85
|
-
setMessages(prev => prev.filter(msg => msg.id !== id));
|
|
86
|
-
setSentMessages(prev => prev.filter(msg => msg.id !== id));
|
|
87
|
-
channel.current?.postMessage({
|
|
88
|
-
id,
|
|
89
|
-
type: internalTypes.CLEAR_MESSAGE,
|
|
90
|
-
source,
|
|
91
|
-
});
|
|
92
|
-
debug.message.cleared(id);
|
|
93
|
-
},
|
|
94
|
-
[source, internalTypes.CLEAR_MESSAGE]
|
|
95
|
-
);
|
|
96
|
-
|
|
97
|
-
const clearAllMessages = useCallback(() => {
|
|
98
|
-
setMessages([]);
|
|
99
|
-
setSentMessages([]);
|
|
100
|
-
channel.current?.postMessage({
|
|
101
|
-
id: internalTypes.CLEAR_ALL_MESSAGES,
|
|
102
|
-
type: internalTypes.CLEAR_ALL_MESSAGES,
|
|
103
|
-
source,
|
|
104
|
-
});
|
|
105
|
-
}, [source, internalTypes.CLEAR_ALL_MESSAGES]);
|
|
106
|
-
|
|
107
|
-
const clearSentMessage = useCallback(
|
|
108
|
-
(id: string) => {
|
|
109
|
-
setSentMessages(prev => prev.filter(msg => msg.id !== id));
|
|
110
|
-
channel.current?.postMessage({
|
|
111
|
-
id,
|
|
112
|
-
type: internalTypes.CLEAR_MESSAGE,
|
|
113
|
-
source,
|
|
114
|
-
});
|
|
115
|
-
},
|
|
116
|
-
[source, internalTypes.CLEAR_MESSAGE]
|
|
117
|
-
);
|
|
118
|
-
|
|
119
80
|
const postMessage = useCallback(
|
|
120
81
|
(messageType: string, messageContent: any, options: SendMessageOptions = {}) => {
|
|
121
82
|
const channelCurrent = channel.current;
|
|
@@ -140,6 +101,55 @@ export const useBroadcastChannel = (
|
|
|
140
101
|
[source, setErrorMessage]
|
|
141
102
|
);
|
|
142
103
|
|
|
104
|
+
const clearReceivedMessages = useCallback((options: ClearReceivedMessagesOptions = {}) => {
|
|
105
|
+
const hasFilters = Boolean(
|
|
106
|
+
(options.ids && options.ids.length) ||
|
|
107
|
+
(options.types && options.types.length) ||
|
|
108
|
+
(options.sources && options.sources.length)
|
|
109
|
+
);
|
|
110
|
+
|
|
111
|
+
setMessages(prev =>
|
|
112
|
+
hasFilters
|
|
113
|
+
? prev.filter(
|
|
114
|
+
msg =>
|
|
115
|
+
!(options.ids && options.ids.includes(msg.id)) &&
|
|
116
|
+
!(options.types && options.types.includes(msg.type)) &&
|
|
117
|
+
!(options.sources && options.sources.includes(msg.source))
|
|
118
|
+
)
|
|
119
|
+
: []
|
|
120
|
+
);
|
|
121
|
+
}, []);
|
|
122
|
+
|
|
123
|
+
const clearSentMessages = useCallback((options: ClearSentMessagesOptions = {}) => {
|
|
124
|
+
const { ids = [], types = [], sync = false } = options ?? {};
|
|
125
|
+
setSentMessages(prev =>
|
|
126
|
+
ids.length > 0 || types.length > 0
|
|
127
|
+
? prev.filter(msg => {
|
|
128
|
+
// Only consider messages from the same sender
|
|
129
|
+
if (msg.source !== source) return true;
|
|
130
|
+
|
|
131
|
+
// Decide whether this message should be cleared:
|
|
132
|
+
// - If the ids array is empty, treat as wildcard (match all ids)
|
|
133
|
+
// - If the types array is empty, treat as wildcard (match all types)
|
|
134
|
+
const idMatches = ids.length === 0 || ids.includes(msg.id);
|
|
135
|
+
const typeMatches = types.length === 0 || types.includes(msg.type);
|
|
136
|
+
|
|
137
|
+
// Remove when BOTH criteria match (wildcards included)
|
|
138
|
+
return !(idMatches && typeMatches);
|
|
139
|
+
})
|
|
140
|
+
: []
|
|
141
|
+
);
|
|
142
|
+
if (sync) {
|
|
143
|
+
channel.current?.postMessage(
|
|
144
|
+
createMessage(
|
|
145
|
+
internalTypes.CLEAR_SENT_MESSAGES,
|
|
146
|
+
{ ids: options.ids ?? [], types: options.types ?? [] },
|
|
147
|
+
source
|
|
148
|
+
)
|
|
149
|
+
);
|
|
150
|
+
}
|
|
151
|
+
}, []);
|
|
152
|
+
|
|
143
153
|
const handleMessage = useCallback(
|
|
144
154
|
(event: MessageEvent) => {
|
|
145
155
|
try {
|
|
@@ -160,15 +170,26 @@ export const useBroadcastChannel = (
|
|
|
160
170
|
}
|
|
161
171
|
|
|
162
172
|
if (isValidInternalClearMessage(message)) {
|
|
163
|
-
if (message.type === internalTypes.
|
|
173
|
+
if (message.type === internalTypes.CLEAR_SENT_MESSAGES) {
|
|
174
|
+
const { ids = [], types = [] } = (message as any).message || {};
|
|
175
|
+
|
|
164
176
|
setMessages(prev =>
|
|
165
|
-
prev.filter(msg =>
|
|
177
|
+
prev.filter(msg => {
|
|
178
|
+
// Only consider messages from the same sender
|
|
179
|
+
if (msg.source !== message.source) return true;
|
|
180
|
+
|
|
181
|
+
// Decide whether this message should be cleared:
|
|
182
|
+
// - If the ids array is empty, treat as wildcard (match all ids)
|
|
183
|
+
// - If the types array is empty, treat as wildcard (match all types)
|
|
184
|
+
const idMatches = ids.length === 0 || ids.includes(msg.id);
|
|
185
|
+
const typeMatches = types.length === 0 || types.includes(msg.type);
|
|
186
|
+
|
|
187
|
+
// Remove when BOTH criteria match (wildcards included)
|
|
188
|
+
return !(idMatches && typeMatches);
|
|
189
|
+
})
|
|
166
190
|
);
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
191
|
|
|
170
|
-
|
|
171
|
-
setMessages(prev => prev.filter(msg => msg.source !== message.source));
|
|
192
|
+
// After handling we don't want to process this internal control packet further
|
|
172
193
|
return;
|
|
173
194
|
}
|
|
174
195
|
}
|
|
@@ -249,9 +270,8 @@ export const useBroadcastChannel = (
|
|
|
249
270
|
messages,
|
|
250
271
|
sentMessages,
|
|
251
272
|
postMessage,
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
clearSentMessage,
|
|
273
|
+
clearReceivedMessages,
|
|
274
|
+
clearSentMessages,
|
|
255
275
|
error,
|
|
256
276
|
};
|
|
257
277
|
};
|
|
@@ -65,19 +65,19 @@ describe('messageUtils', () => {
|
|
|
65
65
|
});
|
|
66
66
|
|
|
67
67
|
it('getInternalMessageType produces unique hashes for different channels', () => {
|
|
68
|
-
const one = getInternalMessageType('
|
|
69
|
-
const two = getInternalMessageType('
|
|
68
|
+
const one = getInternalMessageType('CLEAR_SENT_MESSAGES', 'chanA');
|
|
69
|
+
const two = getInternalMessageType('CLEAR_SENT_MESSAGES', 'chanB');
|
|
70
70
|
expect(one).not.toBe(two);
|
|
71
71
|
});
|
|
72
72
|
|
|
73
73
|
it('getInternalMessageType generates consistent hash string', () => {
|
|
74
|
-
const type = getInternalMessageType('
|
|
74
|
+
const type = getInternalMessageType('CLEAR_SENT_MESSAGES', 'my-channel', 'ns');
|
|
75
75
|
expect(type).toContain('__INTERNAL__');
|
|
76
|
-
expect(type).toContain('
|
|
76
|
+
expect(type).toContain('CLEAR_SENT_MESSAGES');
|
|
77
77
|
});
|
|
78
78
|
|
|
79
79
|
it('isInternalType detects internal message types', () => {
|
|
80
|
-
const type = getInternalMessageType('
|
|
80
|
+
const type = getInternalMessageType('CLEAR_SENT_MESSAGES', 'chan');
|
|
81
81
|
expect(isInternalType(type)).toBe(true);
|
|
82
82
|
expect(isInternalType('normal-type')).toBe(false);
|
|
83
83
|
});
|
|
@@ -85,7 +85,7 @@ describe('messageUtils', () => {
|
|
|
85
85
|
it('isValidInternalClearMessage detects valid structure', () => {
|
|
86
86
|
const valid = {
|
|
87
87
|
id: 'abc',
|
|
88
|
-
type: getInternalMessageType('
|
|
88
|
+
type: getInternalMessageType('CLEAR_SENT_MESSAGES', 'ch'),
|
|
89
89
|
source: 'tab-1',
|
|
90
90
|
};
|
|
91
91
|
const invalid = { id: 123, type: 'bad', source: 'tab-1' };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { renderHook, act } from '@testing-library/react';
|
|
2
2
|
import { useBroadcastChannel } from '../hooks/useBroadcastChannel';
|
|
3
|
+
import { getInternalMessageType } from '../utils/messageUtils';
|
|
3
4
|
|
|
4
5
|
let mockChannels: any[] = [];
|
|
5
6
|
|
|
@@ -118,7 +119,7 @@ describe('useBroadcastChannel', () => {
|
|
|
118
119
|
expect(result.current.messages.length).toBe(1);
|
|
119
120
|
|
|
120
121
|
act(() => {
|
|
121
|
-
result.current.
|
|
122
|
+
result.current.clearReceivedMessages({ ids: ['123'] });
|
|
122
123
|
});
|
|
123
124
|
|
|
124
125
|
expect(result.current.messages.length).toBe(0);
|
|
@@ -149,7 +150,7 @@ describe('useBroadcastChannel', () => {
|
|
|
149
150
|
expect(result.current.messages.length).toBe(2);
|
|
150
151
|
|
|
151
152
|
act(() => {
|
|
152
|
-
result.current.
|
|
153
|
+
result.current.clearReceivedMessages();
|
|
153
154
|
});
|
|
154
155
|
|
|
155
156
|
expect(result.current.messages.length).toBe(0);
|
|
@@ -167,7 +168,7 @@ describe('useBroadcastChannel', () => {
|
|
|
167
168
|
expect(result.current.sentMessages.length).toBe(1);
|
|
168
169
|
|
|
169
170
|
act(() => {
|
|
170
|
-
result.current.
|
|
171
|
+
result.current.clearSentMessages({ ids: [sentId] });
|
|
171
172
|
});
|
|
172
173
|
|
|
173
174
|
expect(result.current.sentMessages.length).toBe(0);
|
|
@@ -262,7 +263,7 @@ describe('useBroadcastChannel', () => {
|
|
|
262
263
|
expect(result.current.messages.length).toBe(1);
|
|
263
264
|
});
|
|
264
265
|
|
|
265
|
-
it('handles internal clear message from other source', async () => {
|
|
266
|
+
it('handles internal clear message (single id) from other source', async () => {
|
|
266
267
|
const { result } = renderHook(() => useBroadcastChannel('test-channel'));
|
|
267
268
|
await waitForChannel();
|
|
268
269
|
const channel = mockChannels[0];
|
|
@@ -279,11 +280,12 @@ describe('useBroadcastChannel', () => {
|
|
|
279
280
|
|
|
280
281
|
expect(result.current.messages.length).toBe(1);
|
|
281
282
|
|
|
283
|
+
const internalType = getInternalMessageType('CLEAR_SENT_MESSAGES', 'test-channel');
|
|
282
284
|
act(() => {
|
|
283
285
|
channel.simulateMessage({
|
|
284
|
-
id: '
|
|
285
|
-
type:
|
|
286
|
-
|
|
286
|
+
id: 'internal-1',
|
|
287
|
+
type: internalType,
|
|
288
|
+
message: { ids: ['to-clear'], types: [] },
|
|
287
289
|
source: 'another-tab',
|
|
288
290
|
timestamp: Date.now(),
|
|
289
291
|
});
|
|
@@ -292,7 +294,7 @@ describe('useBroadcastChannel', () => {
|
|
|
292
294
|
expect(result.current.messages.length).toBe(0);
|
|
293
295
|
});
|
|
294
296
|
|
|
295
|
-
it('handles internal clear all messages from other source', async () => {
|
|
297
|
+
it('handles internal clear all messages (wildcard) from other source', async () => {
|
|
296
298
|
const { result } = renderHook(() => useBroadcastChannel('test-channel'));
|
|
297
299
|
await waitForChannel();
|
|
298
300
|
const channel = mockChannels[0];
|
|
@@ -316,14 +318,12 @@ describe('useBroadcastChannel', () => {
|
|
|
316
318
|
|
|
317
319
|
expect(result.current.messages.length).toBe(2);
|
|
318
320
|
|
|
321
|
+
const internalTypeAll = getInternalMessageType('CLEAR_SENT_MESSAGES', 'test-channel');
|
|
319
322
|
act(() => {
|
|
320
323
|
channel.simulateMessage({
|
|
321
|
-
id:
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
type:
|
|
325
|
-
'__INTERNAL__:CLEAR_ALL_MESSAGES:' +
|
|
326
|
-
btoa('react-broadcast-sync:CLEAR_ALL_MESSAGES:test-channel-'),
|
|
324
|
+
id: 'internal-2',
|
|
325
|
+
type: internalTypeAll,
|
|
326
|
+
message: { ids: [], types: [] },
|
|
327
327
|
source: 'another-tab',
|
|
328
328
|
timestamp: Date.now(),
|
|
329
329
|
});
|
package/src/types/types.ts
CHANGED
|
@@ -57,6 +57,18 @@ export interface BroadcastMessage {
|
|
|
57
57
|
/** Optional expiration timestamp */
|
|
58
58
|
expirationDate?: number;
|
|
59
59
|
}
|
|
60
|
+
export interface ClearOptions {
|
|
61
|
+
ids?: string[];
|
|
62
|
+
types?: string[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ClearReceivedMessagesOptions extends ClearOptions {
|
|
66
|
+
sources?: string[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface ClearSentMessagesOptions extends ClearOptions {
|
|
70
|
+
sync?: boolean;
|
|
71
|
+
}
|
|
60
72
|
|
|
61
73
|
/**
|
|
62
74
|
* Actions and state provided by the broadcast channel hook
|
|
@@ -74,15 +86,14 @@ export interface BroadcastActions {
|
|
|
74
86
|
/** Function to send a message */
|
|
75
87
|
postMessage: (messageType: string, messageContent: any, options?: SendMessageOptions) => void;
|
|
76
88
|
|
|
77
|
-
/** Function to clear
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
/** Function to clear all messages */
|
|
81
|
-
clearAllMessages: () => void;
|
|
89
|
+
/** Function to clear received messages */
|
|
90
|
+
clearReceivedMessages: (options?: ClearReceivedMessagesOptions) => void;
|
|
82
91
|
|
|
83
|
-
/** Function to clear
|
|
84
|
-
|
|
92
|
+
/** Function to clear sent messages */
|
|
93
|
+
clearSentMessages: (options?: ClearSentMessagesOptions) => void;
|
|
85
94
|
|
|
86
95
|
/** Current error state */
|
|
87
96
|
error: string | null;
|
|
88
97
|
}
|
|
98
|
+
|
|
99
|
+
export type ClearMessage = 'CLEAR_SENT_MESSAGES';
|
package/src/utils/debug.ts
CHANGED
|
@@ -29,8 +29,8 @@ const log = (level: LogLevel, message: string, data?: any) => {
|
|
|
29
29
|
|
|
30
30
|
export const debug = {
|
|
31
31
|
channel: {
|
|
32
|
-
created: (
|
|
33
|
-
closed: (
|
|
32
|
+
created: (name: string) => log('info', 'Channel created:', name),
|
|
33
|
+
closed: (name: string) => log('info', 'Channel closed:', name),
|
|
34
34
|
},
|
|
35
35
|
message: {
|
|
36
36
|
sent: (message: any) => log('info', 'Message sent:', message),
|
|
@@ -38,6 +38,8 @@ export const debug = {
|
|
|
38
38
|
cleared: (messageId: string) => log('info', 'Message cleared:', messageId),
|
|
39
39
|
expired: (messageId: string) => log('info', 'Message expired:', messageId),
|
|
40
40
|
duplicate: (messageId: string) => log('warn', 'Duplicate message ignored:', messageId),
|
|
41
|
+
allSentCleared: () => log('info', 'All sent messages cleared'),
|
|
42
|
+
allReceivedCleared: () => log('info', 'All received messages cleared'),
|
|
41
43
|
},
|
|
42
44
|
cleanup: {
|
|
43
45
|
started: () => log('info', 'Cleanup started'),
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { ClearMessage } from '../types/types';
|
|
2
|
+
|
|
1
3
|
const INTERNAL_PREFIX = '__INTERNAL__';
|
|
2
4
|
const SECRET = 'react-broadcast-sync';
|
|
3
5
|
|
|
@@ -44,7 +46,7 @@ export const createMessage = (
|
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
export const getInternalMessageType = (
|
|
47
|
-
baseType:
|
|
49
|
+
baseType: ClearMessage,
|
|
48
50
|
channelName: string,
|
|
49
51
|
namespace = ''
|
|
50
52
|
): string => {
|
|
@@ -58,16 +60,13 @@ export const isInternalType = (type: string): boolean => {
|
|
|
58
60
|
return type.startsWith(`${INTERNAL_PREFIX}:`);
|
|
59
61
|
};
|
|
60
62
|
|
|
61
|
-
export const isValidInternalClearMessage = (
|
|
62
|
-
message: any
|
|
63
|
-
): message is { id: string; type: string; source: string } => {
|
|
63
|
+
export const isValidInternalClearMessage = (message: any): boolean => {
|
|
64
64
|
return (
|
|
65
65
|
message &&
|
|
66
66
|
typeof message === 'object' &&
|
|
67
|
-
typeof message.id === 'string' &&
|
|
68
|
-
typeof message.source === 'string' &&
|
|
69
67
|
typeof message.type === 'string' &&
|
|
70
|
-
isInternalType(message.type)
|
|
68
|
+
isInternalType(message.type) &&
|
|
69
|
+
typeof message.source === 'string'
|
|
71
70
|
);
|
|
72
71
|
};
|
|
73
72
|
|