@asteby/metacore-websocket 0.4.0 → 0.5.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.
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Multi-channel WebSocket client.
3
+ *
4
+ * Complements the single-socket `WebSocketProvider`: some backends (hubs
5
+ * that route by logical channel — notifications, chat, live TV) expect ONE
6
+ * connection PER channel, addressed by query params:
7
+ *
8
+ * wss://api.example.com/ws?channel=chat&token=<jwt>&with=42
9
+ *
10
+ * This client manages that pool imperatively and framework-agnostically:
11
+ * auto-reconnect with exponential backoff, React StrictMode-safe
12
+ * subscription (100ms close debounce so double-mount doesn't drop the
13
+ * socket), and lazy connection per channel.
14
+ *
15
+ * Origin: battle-tested client contributed upstream from doctores.lat.
16
+ */
17
+ /** Handler for incoming messages on a channel. */
18
+ export type ChannelMessageHandler = (event: MessageEvent) => void;
19
+ export interface ChannelClientOptions {
20
+ /** WS base URL, e.g. `wss://api.example.com/ws`. */
21
+ baseUrl: string;
22
+ /** Returns the current JWT; `null` suppresses the connection. */
23
+ getToken: () => string | null;
24
+ /** Max reconnection attempts per channel (default: 5). */
25
+ maxReconnectAttempts?: number;
26
+ /** Base reconnect delay in ms, doubled per attempt (default: 1000). */
27
+ reconnectBaseDelay?: number;
28
+ }
29
+ export type ChannelConnectionState = 'connected' | 'connecting' | 'disconnected' | 'reconnecting';
30
+ export interface ChannelClient {
31
+ /**
32
+ * Subscribe a handler to a logical channel. Returns `unsubscribe`.
33
+ * StrictMode-safe: unsubscribing debounces the actual close by 100ms so a
34
+ * double-mount resubscription reuses the live socket.
35
+ */
36
+ subscribe: (channel: string, handler: ChannelMessageHandler, extraParams?: Record<string, string | number>) => () => void;
37
+ /** Send a string or JSON-serializable payload over a channel. */
38
+ send: (channel: string, payload: unknown, extraParams?: Record<string, string | number>) => void;
39
+ /** Close every open connection (e.g. on app teardown). */
40
+ disconnectAll: () => void;
41
+ /** Connection state for a channel. */
42
+ getConnectionState: (channel: string) => ChannelConnectionState;
43
+ }
44
+ export declare function createChannelClient(options: ChannelClientOptions): ChannelClient;
45
+ //# sourceMappingURL=channels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.d.ts","sourceRoot":"","sources":["../src/channels.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,kDAAkD;AAClD,MAAM,MAAM,qBAAqB,GAAG,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;AAEjE,MAAM,WAAW,oBAAoB;IACnC,oDAAoD;IACpD,OAAO,EAAE,MAAM,CAAA;IACf,iEAAiE;IACjE,QAAQ,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IAC7B,0DAA0D;IAC1D,oBAAoB,CAAC,EAAE,MAAM,CAAA;IAC7B,uEAAuE;IACvE,kBAAkB,CAAC,EAAE,MAAM,CAAA;CAC5B;AAED,MAAM,MAAM,sBAAsB,GAC9B,WAAW,GACX,YAAY,GACZ,cAAc,GACd,cAAc,CAAA;AAYlB,MAAM,WAAW,aAAa;IAC5B;;;;OAIG;IACH,SAAS,EAAE,CACT,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,qBAAqB,EAC9B,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAC1C,MAAM,IAAI,CAAA;IACf,iEAAiE;IACjE,IAAI,EAAE,CACJ,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,OAAO,EAChB,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,KAC1C,IAAI,CAAA;IACT,0DAA0D;IAC1D,aAAa,EAAE,MAAM,IAAI,CAAA;IACzB,sCAAsC;IACtC,kBAAkB,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,sBAAsB,CAAA;CAChE;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CA+MhF"}
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Multi-channel WebSocket client.
3
+ *
4
+ * Complements the single-socket `WebSocketProvider`: some backends (hubs
5
+ * that route by logical channel — notifications, chat, live TV) expect ONE
6
+ * connection PER channel, addressed by query params:
7
+ *
8
+ * wss://api.example.com/ws?channel=chat&token=<jwt>&with=42
9
+ *
10
+ * This client manages that pool imperatively and framework-agnostically:
11
+ * auto-reconnect with exponential backoff, React StrictMode-safe
12
+ * subscription (100ms close debounce so double-mount doesn't drop the
13
+ * socket), and lazy connection per channel.
14
+ *
15
+ * Origin: battle-tested client contributed upstream from doctores.lat.
16
+ */
17
+ export function createChannelClient(options) {
18
+ const connections = new Map();
19
+ const maxReconnectAttempts = options.maxReconnectAttempts ?? 5;
20
+ const reconnectBaseDelay = options.reconnectBaseDelay ?? 1000;
21
+ function buildUrl(channel, token, extraParams) {
22
+ const url = new URL(options.baseUrl);
23
+ url.searchParams.set('channel', channel);
24
+ url.searchParams.set('token', token);
25
+ if (extraParams) {
26
+ Object.entries(extraParams).forEach(([key, value]) => {
27
+ url.searchParams.set(key, String(value));
28
+ });
29
+ }
30
+ return url.toString();
31
+ }
32
+ function createConnection(channel, existingHandlers, extraParams) {
33
+ const token = options.getToken();
34
+ if (!token) {
35
+ console.warn('[ws:channels] No token available for channel', channel);
36
+ return null;
37
+ }
38
+ const socket = new WebSocket(buildUrl(channel, token, extraParams));
39
+ const handlers = existingHandlers ?? new Set();
40
+ const connection = {
41
+ socket,
42
+ handlers,
43
+ reconnectAttempts: 0,
44
+ reconnectTimeout: null,
45
+ closeTimeout: null,
46
+ isClosed: false,
47
+ extraParams,
48
+ };
49
+ socket.onopen = () => {
50
+ connection.reconnectAttempts = 0;
51
+ };
52
+ socket.onmessage = (event) => {
53
+ handlers.forEach((handler) => handler(event));
54
+ };
55
+ socket.onclose = (event) => {
56
+ // Reconnect only when: not intentionally closed, someone is still
57
+ // listening, attempts remain, and the closure wasn't clean (1000).
58
+ if (!connection.isClosed &&
59
+ handlers.size > 0 &&
60
+ connection.reconnectAttempts < maxReconnectAttempts &&
61
+ event.code !== 1000) {
62
+ const delay = reconnectBaseDelay * Math.pow(2, connection.reconnectAttempts);
63
+ connection.reconnectTimeout = setTimeout(() => {
64
+ connection.reconnectAttempts++;
65
+ const newConn = createConnection(channel, handlers, extraParams);
66
+ if (newConn) {
67
+ // Preserve the attempt counter across the replacement socket so
68
+ // the backoff caps at maxReconnectAttempts overall.
69
+ newConn.reconnectAttempts = connection.reconnectAttempts;
70
+ connections.set(channel, newConn);
71
+ }
72
+ else {
73
+ connections.delete(channel);
74
+ }
75
+ }, delay);
76
+ }
77
+ else {
78
+ connections.delete(channel);
79
+ }
80
+ };
81
+ socket.onerror = (event) => {
82
+ console.error('[ws:channels] Error on channel', channel, event);
83
+ };
84
+ connections.set(channel, connection);
85
+ return connection;
86
+ }
87
+ function ensureConnection(channel, extraParams) {
88
+ const existing = connections.get(channel);
89
+ if (existing &&
90
+ existing.socket.readyState !== WebSocket.CLOSED &&
91
+ existing.socket.readyState !== WebSocket.CLOSING) {
92
+ return existing;
93
+ }
94
+ if (existing) {
95
+ if (existing.reconnectTimeout)
96
+ clearTimeout(existing.reconnectTimeout);
97
+ connections.delete(channel);
98
+ }
99
+ return createConnection(channel, undefined, extraParams);
100
+ }
101
+ function subscribe(channel, handler, extraParams) {
102
+ const conn = ensureConnection(channel, extraParams);
103
+ if (!conn)
104
+ return () => { };
105
+ // Cancel a pending close (StrictMode resubscription).
106
+ if (conn.closeTimeout) {
107
+ clearTimeout(conn.closeTimeout);
108
+ conn.closeTimeout = null;
109
+ conn.isClosed = false;
110
+ }
111
+ conn.handlers.add(handler);
112
+ return () => {
113
+ const current = connections.get(channel);
114
+ if (!current)
115
+ return;
116
+ current.handlers.delete(handler);
117
+ if (current.handlers.size > 0)
118
+ return;
119
+ if (current.closeTimeout)
120
+ clearTimeout(current.closeTimeout);
121
+ // 100ms debounce lets StrictMode resubscribe before the real close.
122
+ current.closeTimeout = setTimeout(() => {
123
+ if (current.handlers.size > 0)
124
+ return;
125
+ current.isClosed = true;
126
+ if (current.reconnectTimeout) {
127
+ clearTimeout(current.reconnectTimeout);
128
+ current.reconnectTimeout = null;
129
+ }
130
+ if (current.socket.readyState === WebSocket.OPEN) {
131
+ current.socket.close(1000, 'No more handlers');
132
+ }
133
+ else if (current.socket.readyState === WebSocket.CONNECTING) {
134
+ current.socket.addEventListener('open', () => {
135
+ if (current.handlers.size === 0) {
136
+ current.socket.close(1000, 'No more handlers');
137
+ }
138
+ }, { once: true });
139
+ }
140
+ connections.delete(channel);
141
+ }, 100);
142
+ };
143
+ }
144
+ function send(channel, payload, extraParams) {
145
+ const conn = ensureConnection(channel, extraParams);
146
+ if (!conn)
147
+ return;
148
+ const data = typeof payload === 'string' ? payload : JSON.stringify(payload);
149
+ if (conn.socket.readyState === WebSocket.OPEN) {
150
+ conn.socket.send(data);
151
+ }
152
+ else if (conn.socket.readyState === WebSocket.CONNECTING) {
153
+ conn.socket.addEventListener('open', () => conn.socket.send(data), {
154
+ once: true,
155
+ });
156
+ }
157
+ else {
158
+ console.warn('[ws:channels] Cannot send, socket not open for channel', channel);
159
+ }
160
+ }
161
+ function disconnectAll() {
162
+ connections.forEach((conn) => {
163
+ conn.isClosed = true;
164
+ if (conn.reconnectTimeout)
165
+ clearTimeout(conn.reconnectTimeout);
166
+ if (conn.closeTimeout)
167
+ clearTimeout(conn.closeTimeout);
168
+ if (conn.socket.readyState === WebSocket.OPEN ||
169
+ conn.socket.readyState === WebSocket.CONNECTING) {
170
+ conn.socket.close(1000, 'Disconnect all');
171
+ }
172
+ });
173
+ connections.clear();
174
+ }
175
+ function getConnectionState(channel) {
176
+ const conn = connections.get(channel);
177
+ if (!conn)
178
+ return 'disconnected';
179
+ if (conn.reconnectTimeout)
180
+ return 'reconnecting';
181
+ switch (conn.socket.readyState) {
182
+ case WebSocket.CONNECTING:
183
+ return 'connecting';
184
+ case WebSocket.OPEN:
185
+ return 'connected';
186
+ default:
187
+ return 'disconnected';
188
+ }
189
+ }
190
+ return { subscribe, send, disconnectAll, getConnectionState };
191
+ }
192
+ //# sourceMappingURL=channels.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"channels.js","sourceRoot":"","sources":["../src/channels.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,4 +2,6 @@ export { WebSocketProvider, WebSocketContext } from './provider';
2
2
  export { useWebSocket, useWebSocketMessage } from './hooks';
3
3
  export { WebSocketStatus } from './types';
4
4
  export type { WebSocketMessage, WebSocketContextValue, WebSocketProviderProps, SendPayload, TokenGetter, } from './types';
5
+ export { createChannelClient } from './channels';
6
+ export type { ChannelClient, ChannelClientOptions, ChannelMessageHandler, ChannelConnectionState, } from './channels';
5
7
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,WAAW,EACX,WAAW,GACZ,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,YAAY,EACV,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,WAAW,EACX,WAAW,GACZ,MAAM,SAAS,CAAA;AAIhB,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA;AAChD,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,GACvB,MAAM,YAAY,CAAA"}
package/dist/index.js CHANGED
@@ -1,4 +1,7 @@
1
1
  export { WebSocketProvider, WebSocketContext } from './provider';
2
2
  export { useWebSocket, useWebSocketMessage } from './hooks';
3
3
  export { WebSocketStatus } from './types';
4
+ // Multi-channel imperative client (one socket per logical channel, routed
5
+ // by query params). Complements the single-socket WebSocketProvider.
6
+ export { createChannelClient } from './channels';
4
7
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAChE,OAAO,EAAE,YAAY,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAA;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AASzC,0EAA0E;AAC1E,qEAAqE;AACrE,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAA"}
@@ -5,5 +5,5 @@ export declare const WebSocketContext: import("react").Context<WebSocketContextV
5
5
  * auto-reconnect (exponential backoff), optional heartbeat, typed message
6
6
  * dispatch, and a pub/sub registry consumed by `useWebSocketMessage`.
7
7
  */
8
- export declare function WebSocketProvider<TMessage extends WebSocketMessage = WebSocketMessage>(props: WebSocketProviderProps<TMessage>): import("react/jsx-runtime").JSX.Element;
8
+ export declare function WebSocketProvider<TMessage extends WebSocketMessage = WebSocketMessage>(props: WebSocketProviderProps<TMessage>): import("react").JSX.Element;
9
9
  //# sourceMappingURL=provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAEV,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,SAAS,CAAA;AAIhB,eAAO,MAAM,gBAAgB,iEAE5B,CAAA;AAwBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,SAAS,gBAAgB,GAAG,gBAAgB,EACpF,KAAK,EAAE,sBAAsB,CAAC,QAAQ,CAAC,2CA+RxC"}
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAEV,qBAAqB,EACrB,gBAAgB,EAChB,sBAAsB,EACvB,MAAM,SAAS,CAAA;AAIhB,eAAO,MAAM,gBAAgB,iEAE5B,CAAA;AAwBD;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,SAAS,gBAAgB,GAAG,gBAAgB,EACpF,KAAK,EAAE,sBAAsB,CAAC,QAAQ,CAAC,+BA+RxC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@asteby/metacore-websocket",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Metacore WebSocket provider — auto-reconnect, typed messages, channel subscriptions",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {