react-broadcast-sync 1.6.0 → 1.8.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.
Files changed (52) hide show
  1. package/README.md +59 -11
  2. package/dist/index.cjs.js +1 -1
  3. package/dist/index.cjs.js.map +1 -1
  4. package/dist/index.esm.js +1 -1
  5. package/dist/index.esm.js.map +1 -1
  6. package/dist/types/types.d.ts +17 -0
  7. package/package.json +6 -1
  8. package/.eslintignore +0 -5
  9. package/.github/workflows/ci.yml +0 -61
  10. package/.github/workflows/release.yml +0 -39
  11. package/.husky/pre-commit +0 -5
  12. package/.nvmrc +0 -1
  13. package/.prettierignore +0 -4
  14. package/.prettierrc +0 -9
  15. package/.releaserc.json +0 -22
  16. package/CHANGELOG.md +0 -97
  17. package/assets/react-broadcast-sync-logo.png +0 -0
  18. package/commitlint.config.js +0 -3
  19. package/demo/react-broadcast-sync-demo/README.md +0 -130
  20. package/demo/react-broadcast-sync-demo/index.html +0 -14
  21. package/demo/react-broadcast-sync-demo/package-lock.json +0 -6379
  22. package/demo/react-broadcast-sync-demo/package.json +0 -42
  23. package/demo/react-broadcast-sync-demo/public/assets/react-broadcast-sync-demo-video.gif +0 -0
  24. package/demo/react-broadcast-sync-demo/public/assets/react-broadcast-sync-logo.png +0 -0
  25. package/demo/react-broadcast-sync-demo/src/App.tsx +0 -277
  26. package/demo/react-broadcast-sync-demo/src/components/Counter.tsx +0 -63
  27. package/demo/react-broadcast-sync-demo/src/components/TextSync.tsx +0 -61
  28. package/demo/react-broadcast-sync-demo/src/components/TodoList.tsx +0 -186
  29. package/demo/react-broadcast-sync-demo/src/hooks/useCounterBroadcast.ts +0 -96
  30. package/demo/react-broadcast-sync-demo/src/hooks/useTextBroadcast.ts +0 -37
  31. package/demo/react-broadcast-sync-demo/src/hooks/useTodoBroadcast.ts +0 -249
  32. package/demo/react-broadcast-sync-demo/src/main.tsx +0 -11
  33. package/demo/react-broadcast-sync-demo/src/theme.ts +0 -62
  34. package/demo/react-broadcast-sync-demo/tsconfig.json +0 -26
  35. package/demo/react-broadcast-sync-demo/tsconfig.node.json +0 -10
  36. package/demo/react-broadcast-sync-demo/vite.config.ts +0 -12
  37. package/eslint.config.js +0 -38
  38. package/jest.config.cjs +0 -9
  39. package/jest.setup.ts +0 -1
  40. package/rollup.config.mjs +0 -98
  41. package/src/BroadcastProvider.tsx +0 -28
  42. package/src/hooks/useBroadcastChannel.ts +0 -513
  43. package/src/index.ts +0 -9
  44. package/src/tests/BroadcastProvider.test.tsx +0 -157
  45. package/src/tests/debug.test.ts +0 -273
  46. package/src/tests/messageUtils.test.ts +0 -252
  47. package/src/tests/useBroadcastChannel.same-tab.test.tsx +0 -992
  48. package/src/tests/useBroadcastChannel.test.tsx +0 -1243
  49. package/src/types/types.ts +0 -124
  50. package/src/utils/debug.ts +0 -80
  51. package/src/utils/messageUtils.ts +0 -122
  52. package/tsconfig.json +0 -36
@@ -1,124 +0,0 @@
1
- /**
2
- * Options for configuring the broadcast channel
3
- */
4
- export interface BroadcastOptions {
5
- /** Custom source name for this instance */
6
- sourceName?: string;
7
-
8
- /** Interval in milliseconds for cleaning expired messages (default: 1000) */
9
- cleaningInterval?: number;
10
-
11
- /** If true, only the latest message is kept */
12
- keepLatestMessage?: boolean;
13
-
14
- /** Array of allowed message types */
15
- registeredTypes?: string[];
16
-
17
- /** Channel namespace for isolation */
18
- namespace?: string;
19
-
20
- /** TTL for deduplication in milliseconds (default: 5 minutes) */
21
- deduplicationTTL?: number;
22
-
23
- /** Debounce time for messages cleanup in milliseconds (default: 1000) */
24
- cleanupDebounceMs?: number;
25
-
26
- /** Delay in milliseconds for batching messages (default: 20) */
27
- batchingDelayMs?: number;
28
-
29
- /** Array of message types that should not be batched and sent immediately (default: []) */
30
- excludedBatchMessageTypes?: string[];
31
- }
32
-
33
- /**
34
- * Options for sending messages
35
- */
36
- export interface SendMessageOptions {
37
- /** Message expiration duration in milliseconds */
38
- expirationDuration?: number;
39
-
40
- /** Specific expiration timestamp */
41
- expirationDate?: number;
42
- }
43
-
44
- /**
45
- * Structure of a broadcast message
46
- */
47
- export interface BroadcastMessage {
48
- /** Unique message identifier */
49
- id: string;
50
-
51
- /** Message type for categorization */
52
- type: string;
53
-
54
- /** Message content */
55
- message: any;
56
-
57
- /** Creation timestamp */
58
- timestamp: number;
59
-
60
- /** Source identifier */
61
- source: string;
62
-
63
- /** Optional expiration timestamp */
64
- expirationDate?: number;
65
- }
66
- export interface ClearOptions {
67
- ids?: string[];
68
- types?: string[];
69
- }
70
-
71
- export interface ClearReceivedMessagesOptions extends ClearOptions {
72
- sources?: string[];
73
- }
74
-
75
- export interface ClearSentMessagesOptions extends ClearOptions {
76
- sync?: boolean;
77
- }
78
-
79
- export interface GetLatestMessageOptions {
80
- source?: string;
81
- type?: string;
82
- }
83
-
84
- /**
85
- * Actions and state provided by the broadcast channel hook
86
- */
87
- export interface BroadcastActions {
88
- /** The resolved channel name including namespace */
89
- channelName: string;
90
-
91
- /** Array of received messages */
92
- messages: BroadcastMessage[];
93
-
94
- /** Array of sent messages */
95
- sentMessages: BroadcastMessage[];
96
-
97
- /** Whether a ping operation is currently in progress */
98
- isPingInProgress: boolean;
99
-
100
- /** Function to ping the channel and get active sources */
101
- ping: (timeoutMs?: number) => Promise<string[]>;
102
-
103
- /** Function to send a message */
104
- postMessage: (messageType: string, messageContent: any, options?: SendMessageOptions) => void;
105
-
106
- /** Function to clear received messages */
107
- clearReceivedMessages: (options?: ClearReceivedMessagesOptions) => void;
108
-
109
- /** Function to clear sent messages */
110
- clearSentMessages: (options?: ClearSentMessagesOptions) => void;
111
-
112
- /** Function to get the latest message by source and type */
113
- getLatestMessage: (options?: GetLatestMessageOptions) => BroadcastMessage | null;
114
-
115
- /** Explicitly close the broadcast channel and remove listeners */
116
- closeChannel: () => void;
117
-
118
- /** Current error state */
119
- error: string | null;
120
- }
121
-
122
- export type ClearMessage = 'CLEAR_SENT_MESSAGES';
123
-
124
- export type InternalMessage = ClearMessage | 'PING' | 'PONG';
@@ -1,80 +0,0 @@
1
- declare const process: { env: { [key: string]: string | undefined } };
2
-
3
- const isDebugMode = () => {
4
- if (typeof process !== 'undefined' && process.env) {
5
- return process.env.REACT_APP_DEBUG_BROADCAST === 'true';
6
- }
7
- return false;
8
- };
9
-
10
- type LogLevel = 'info' | 'warn' | 'error';
11
-
12
- const log = (level: LogLevel, message: string, data?: any) => {
13
- if (!isDebugMode()) return;
14
-
15
- const timestamp = new Date().toISOString();
16
- const prefix = `[${timestamp}] [react-broadcast-sync]`;
17
- switch (level) {
18
- case 'info':
19
- console.log(prefix, message, data || '');
20
- break;
21
- case 'warn':
22
- console.warn(prefix, message, data || '');
23
- break;
24
- case 'error':
25
- console.error(prefix, message, data || '');
26
- break;
27
- }
28
- };
29
-
30
- export const debug = {
31
- channel: {
32
- created: (name: string) => log('info', 'Channel created:', name),
33
- closed: (name: string) => log('info', 'Channel closed:', name),
34
- },
35
- message: {
36
- sent: (message: any) => log('info', 'Message sent:', message),
37
- received: (message: any) => log('info', 'Message received:', message),
38
- cleared: (messageId: string) => log('info', 'Message cleared:', messageId),
39
- expired: (messageId: string) => log('info', 'Message expired:', messageId),
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'),
43
- ignored: (messageType: string) =>
44
- log('info', 'Message ignored due to type filter:', messageType),
45
- },
46
- ping: {
47
- inProgress: () => log('warn', 'Ping already in progress. Skipping call.'),
48
- },
49
- cleanup: {
50
- started: () => log('info', 'Cleanup started'),
51
- completed: (removedCount: number) => log('info', 'Cleanup completed:', removedCount),
52
- },
53
- error: (context: {
54
- action: string;
55
- channelName?: string;
56
- type?: string;
57
- source?: string;
58
- originalError?: unknown;
59
- }) => {
60
- const { action, channelName, type, source, originalError } = context;
61
-
62
- let message = `${action}`;
63
- if (channelName) message += ` | channel: ${channelName}`;
64
- if (type) message += ` | type: ${type}`;
65
- if (source) message += ` | source: ${source}`;
66
- if (originalError instanceof Error) {
67
- message += ` | cause: ${originalError.message}`;
68
- } else if (typeof originalError === 'string') {
69
- message += ` | cause: ${originalError}`;
70
- } else if (originalError != null) {
71
- try {
72
- message += ` | cause: ${JSON.stringify(originalError)}`;
73
- } catch {
74
- message += ' | cause: [unserializable]';
75
- }
76
- }
77
-
78
- log('error', 'Error:', message);
79
- },
80
- };
@@ -1,122 +0,0 @@
1
- import { InternalMessage } from '../types/types';
2
-
3
- const INTERNAL_PREFIX = '__INTERNAL__';
4
- const SECRET = 'react-broadcast-sync';
5
-
6
- // Generate a random 9-character alphanumeric string
7
- export const generateRandomPart = () => Math.random().toString(36).substr(2, 9);
8
-
9
- // Generate a unique source name for a tab
10
- export const generateSourceName = () => `tab-${generateRandomPart()}`;
11
-
12
- // Generate a unique message ID
13
- export const generateMessageId = (source: string, timestamp: number) => {
14
- const raw = `${generateRandomPart()}-${source}-${timestamp}`;
15
- return btoa(raw).replace(/=+$/, '');
16
- };
17
-
18
- // Check if a message is valid
19
- export const isValidMessage = (message: any): boolean => {
20
- return Boolean(message && typeof message === 'object' && 'id' in message);
21
- };
22
-
23
- // Check if a message has expired
24
- export const isMessageExpired = (message: { expirationDate?: number }): boolean => {
25
- return message.expirationDate ? message.expirationDate < Date.now() : false;
26
- };
27
-
28
- // Create a new message object
29
- export const createMessage = (
30
- type: string,
31
- content: any,
32
- source: string,
33
- options: { expirationDate?: number; expirationDuration?: number } = {}
34
- ): any => {
35
- const timestamp = Date.now();
36
- return {
37
- id: generateMessageId(source, timestamp),
38
- type,
39
- message: content,
40
- timestamp,
41
- source,
42
- expirationDate:
43
- options.expirationDate ??
44
- (options.expirationDuration ? timestamp + options.expirationDuration : undefined),
45
- };
46
- };
47
-
48
- export const getInternalMessageType = (
49
- baseType: InternalMessage,
50
- channelName: string,
51
- namespace = ''
52
- ): string => {
53
- const fullChannel = `${channelName}-${namespace}`;
54
- const input = `${SECRET}:${baseType}:${fullChannel}`;
55
- const hash = btoa(input); // optional: use sha256 if added
56
- return `${INTERNAL_PREFIX}:${baseType}:${hash}`;
57
- };
58
-
59
- export const isInternalType = (type: string): boolean => {
60
- return type.startsWith(`${INTERNAL_PREFIX}:`);
61
- };
62
-
63
- export const isValidInternalClearMessage = (message: any): boolean => {
64
- return (
65
- message &&
66
- typeof message === 'object' &&
67
- typeof message.type === 'string' &&
68
- isInternalType(message.type) &&
69
- typeof message.source === 'string'
70
- );
71
- };
72
-
73
- export const debounce = <T extends (...args: any[]) => any>(
74
- fn: T,
75
- wait: number
76
- ): ((...args: Parameters<T>) => void) & {
77
- cancel: () => void;
78
- flush: () => ReturnType<T> | undefined;
79
- } => {
80
- let timeoutId: ReturnType<typeof setTimeout> | null = null;
81
- let lastArgs: Parameters<T> | null = null;
82
- let result: ReturnType<T> | undefined;
83
-
84
- const debounced = (...args: Parameters<T>) => {
85
- lastArgs = args;
86
- if (timeoutId !== null) {
87
- clearTimeout(timeoutId);
88
- }
89
- timeoutId = setTimeout(() => {
90
- timeoutId = null;
91
- if (lastArgs !== null) {
92
- result = fn(...lastArgs);
93
- lastArgs = null;
94
- }
95
- }, wait);
96
- };
97
-
98
- debounced.cancel = () => {
99
- if (timeoutId !== null) {
100
- clearTimeout(timeoutId);
101
- timeoutId = null;
102
- lastArgs = null;
103
- }
104
- };
105
-
106
- debounced.flush = () => {
107
- if (timeoutId !== null) {
108
- clearTimeout(timeoutId);
109
- timeoutId = null;
110
- if (lastArgs !== null) {
111
- result = fn(...lastArgs);
112
- lastArgs = null;
113
- }
114
- }
115
- return result;
116
- };
117
-
118
- return debounced as typeof debounced & {
119
- cancel: () => void;
120
- flush: () => ReturnType<T> | undefined;
121
- };
122
- };
package/tsconfig.json DELETED
@@ -1,36 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES6",
4
- "lib": ["DOM", "ES2017"],
5
- "module": "ESNext",
6
- "moduleResolution": "node",
7
- "strict": true,
8
- "esModuleInterop": true,
9
- "skipLibCheck": true,
10
- "forceConsistentCasingInFileNames": true,
11
- "jsx": "react-jsx",
12
- "outDir": "./dist",
13
- "declaration": true,
14
- "declarationDir": "./dist",
15
- "emitDeclarationOnly": false,
16
- "isolatedModules": true,
17
- "resolveJsonModule": true,
18
- "allowSyntheticDefaultImports": true,
19
- "noImplicitAny": true,
20
- "strictNullChecks": true,
21
- "strictFunctionTypes": true,
22
- "strictBindCallApply": true,
23
- "strictPropertyInitialization": true,
24
- "noImplicitThis": true,
25
- "alwaysStrict": true,
26
- "noUnusedLocals": true,
27
- "noUnusedParameters": true,
28
- "noImplicitReturns": true,
29
- "noFallthroughCasesInSwitch": true,
30
- "allowJs": true,
31
- "checkJs": false,
32
- "types": ["jest", "node", "react"]
33
- },
34
- "include": ["src/**/*"],
35
- "exclude": ["node_modules", "dist"]
36
- }