evstream 1.0.3 → 1.0.4

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.
@@ -1,14 +1,40 @@
1
1
  import { RedisOptions } from 'ioredis';
2
- interface EvRedisPubSubOptions {
3
- subject?: string;
2
+ /**
3
+ * Configuration options for EvRedisPubSub
4
+ */
5
+ interface EvRedisPubSubOptions<T> {
6
+ /** Redis Pub/Sub channel name */
7
+ subject: string;
8
+ /** Redis connection options */
4
9
  options: RedisOptions;
5
- onMessage?: (message: Object) => void;
10
+ /** Optional initial message handler */
11
+ onMessage?: (message: T) => void;
6
12
  }
7
- export declare class EvRedisPubSub {
13
+ /**
14
+ * Redis-based Pub/Sub helper for cross-process communication.
15
+ *
16
+ * - Uses separate publisher and subscriber connections
17
+ * - Prevents self-message delivery using instance UID
18
+ * - Typed message payload via generics
19
+ */
20
+ export declare class EvRedisPubSub<T = unknown> {
8
21
  #private;
9
- constructor({ options, subject, onMessage }: EvRedisPubSubOptions);
22
+ constructor({ options, subject, onMessage }: EvRedisPubSubOptions<T>);
23
+ /**
24
+ * Initializes Redis subscriptions and listeners.
25
+ */
10
26
  private init;
11
- send(msg: Object): Promise<void>;
12
- onMessage(callback: (msg: Object) => void): void;
27
+ /**
28
+ * Publishes a message to the Redis channel.
29
+ */
30
+ send(msg: T): Promise<void>;
31
+ /**
32
+ * Registers or replaces the message handler.
33
+ */
34
+ onMessage(callback: (msg: T) => void): void;
35
+ /**
36
+ * Gracefully closes Redis connections.
37
+ */
38
+ close(): Promise<void>;
13
39
  }
14
40
  export {};
@@ -18,12 +18,18 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
18
18
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
19
19
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
20
20
  };
21
- var _EvRedisPubSub_options, _EvRedisPubSub_subject, _EvRedisPubSub_pub, _EvRedisPubSub_sub, _EvRedisPubSub_instanceId, _EvRedisPubSub_onMessage;
21
+ var _EvRedisPubSub_subject, _EvRedisPubSub_pub, _EvRedisPubSub_sub, _EvRedisPubSub_instanceId, _EvRedisPubSub_onMessage;
22
22
  import Redis from 'ioredis';
23
23
  import { uid } from '../utils.js';
24
+ /**
25
+ * Redis-based Pub/Sub helper for cross-process communication.
26
+ *
27
+ * - Uses separate publisher and subscriber connections
28
+ * - Prevents self-message delivery using instance UID
29
+ * - Typed message payload via generics
30
+ */
24
31
  export class EvRedisPubSub {
25
32
  constructor({ options, subject, onMessage }) {
26
- _EvRedisPubSub_options.set(this, void 0);
27
33
  _EvRedisPubSub_subject.set(this, void 0);
28
34
  _EvRedisPubSub_pub.set(this, void 0);
29
35
  _EvRedisPubSub_sub.set(this, void 0);
@@ -31,36 +37,55 @@ export class EvRedisPubSub {
31
37
  _EvRedisPubSub_onMessage.set(this, void 0);
32
38
  __classPrivateFieldSet(this, _EvRedisPubSub_pub, new Redis(options), "f");
33
39
  __classPrivateFieldSet(this, _EvRedisPubSub_sub, new Redis(options), "f");
40
+ __classPrivateFieldSet(this, _EvRedisPubSub_subject, subject, "f");
34
41
  __classPrivateFieldSet(this, _EvRedisPubSub_onMessage, onMessage, "f");
35
42
  __classPrivateFieldSet(this, _EvRedisPubSub_instanceId, uid({ prefix: subject, counter: Math.random() }), "f");
36
- __classPrivateFieldSet(this, _EvRedisPubSub_subject, subject, "f");
37
43
  this.init();
38
44
  }
45
+ /**
46
+ * Initializes Redis subscriptions and listeners.
47
+ */
39
48
  init() {
40
49
  return __awaiter(this, void 0, void 0, function* () {
41
- __classPrivateFieldGet(this, _EvRedisPubSub_pub, "f").on('error', (err) => { });
42
- __classPrivateFieldGet(this, _EvRedisPubSub_sub, "f").on('error', (err) => { });
50
+ __classPrivateFieldGet(this, _EvRedisPubSub_pub, "f").on('error', () => { });
51
+ __classPrivateFieldGet(this, _EvRedisPubSub_sub, "f").on('error', () => { });
43
52
  yield __classPrivateFieldGet(this, _EvRedisPubSub_sub, "f").subscribe(__classPrivateFieldGet(this, _EvRedisPubSub_subject, "f"));
44
53
  __classPrivateFieldGet(this, _EvRedisPubSub_sub, "f").on('message', (_, raw) => {
54
+ var _a;
45
55
  try {
46
- const msg = JSON.parse(raw);
47
- console.log("Incoming Message");
48
- console.log(msg);
49
- if ((msg === null || msg === void 0 ? void 0 : msg.uid) !== __classPrivateFieldGet(this, _EvRedisPubSub_instanceId, "f")) {
50
- __classPrivateFieldGet(this, _EvRedisPubSub_onMessage, "f").call(this, msg === null || msg === void 0 ? void 0 : msg.msg);
56
+ const data = JSON.parse(raw);
57
+ // Ignore messages from the same instance
58
+ if ((data === null || data === void 0 ? void 0 : data.uid) !== __classPrivateFieldGet(this, _EvRedisPubSub_instanceId, "f")) {
59
+ (_a = __classPrivateFieldGet(this, _EvRedisPubSub_onMessage, "f")) === null || _a === void 0 ? void 0 : _a.call(this, data.msg);
51
60
  }
52
61
  }
53
- catch (_a) { }
62
+ catch (_b) {
63
+ // Ignore malformed payloads
64
+ }
54
65
  });
55
66
  });
56
67
  }
68
+ /**
69
+ * Publishes a message to the Redis channel.
70
+ */
57
71
  send(msg) {
58
72
  return __awaiter(this, void 0, void 0, function* () {
59
73
  yield __classPrivateFieldGet(this, _EvRedisPubSub_pub, "f").publish(__classPrivateFieldGet(this, _EvRedisPubSub_subject, "f"), JSON.stringify({ uid: __classPrivateFieldGet(this, _EvRedisPubSub_instanceId, "f"), msg }));
60
74
  });
61
75
  }
76
+ /**
77
+ * Registers or replaces the message handler.
78
+ */
62
79
  onMessage(callback) {
63
80
  __classPrivateFieldSet(this, _EvRedisPubSub_onMessage, callback, "f");
64
81
  }
82
+ /**
83
+ * Gracefully closes Redis connections.
84
+ */
85
+ close() {
86
+ return __awaiter(this, void 0, void 0, function* () {
87
+ yield Promise.all([__classPrivateFieldGet(this, _EvRedisPubSub_pub, "f").quit(), __classPrivateFieldGet(this, _EvRedisPubSub_sub, "f").quit()]);
88
+ });
89
+ }
65
90
  }
66
- _EvRedisPubSub_options = new WeakMap(), _EvRedisPubSub_subject = new WeakMap(), _EvRedisPubSub_pub = new WeakMap(), _EvRedisPubSub_sub = new WeakMap(), _EvRedisPubSub_instanceId = new WeakMap(), _EvRedisPubSub_onMessage = new WeakMap();
91
+ _EvRedisPubSub_subject = new WeakMap(), _EvRedisPubSub_pub = new WeakMap(), _EvRedisPubSub_sub = new WeakMap(), _EvRedisPubSub_instanceId = new WeakMap(), _EvRedisPubSub_onMessage = new WeakMap();
@@ -1,12 +1,55 @@
1
1
  import { RedisOptions } from 'ioredis';
2
2
  import { EvRedisPubSub } from './pub-sub.js';
3
3
  import { EvStateAdapter } from '../types.js';
4
+ /**
5
+ * Redis-based implementation of {@link EvStateAdapter}.
6
+ *
7
+ * This adapter enables distributed state updates using Redis Pub/Sub.
8
+ * It supports:
9
+ * - Channel-based subscriptions
10
+ * - Multiple listeners per channel
11
+ * - Self-message filtering via instance ID
12
+ *
13
+ * Designed to be used by EvState / EvStateManager for
14
+ * cross-process state synchronization.
15
+ */
4
16
  declare class EvRedisAdapter implements EvStateAdapter {
5
17
  #private;
18
+ /**
19
+ * Creates a new Redis state adapter.
20
+ *
21
+ * @param options - Optional Redis connection options
22
+ */
6
23
  constructor(options?: RedisOptions);
24
+ /**
25
+ * Publishes a message to a Redis channel.
26
+ *
27
+ * The payload is wrapped with the instance ID to
28
+ * prevent self-delivery.
29
+ *
30
+ * @param channel - Redis channel name
31
+ * @param message - Message payload
32
+ */
7
33
  publish(channel: string, message: any): Promise<void>;
34
+ /**
35
+ * Subscribes to a Redis channel.
36
+ *
37
+ * Multiple listeners can be registered per channel.
38
+ * The Redis subscription is created only once per channel.
39
+ *
40
+ * @param channel - Redis channel name
41
+ * @param onMessage - Callback invoked on incoming messages
42
+ */
8
43
  subscribe(channel: string, onMessage: (message: any) => void): Promise<void>;
44
+ /**
45
+ * Unsubscribes from a Redis channel and removes all listeners.
46
+ *
47
+ * @param channel - Redis channel name
48
+ */
9
49
  unsubscribe(channel: string): Promise<void>;
50
+ /**
51
+ * Gracefully closes Redis connections.
52
+ */
10
53
  quit(): void;
11
54
  }
12
55
  export { EvRedisPubSub, EvRedisAdapter };
@@ -22,11 +22,35 @@ var _EvRedisAdapter_pub, _EvRedisAdapter_sub, _EvRedisAdapter_listeners, _EvRedi
22
22
  import Redis from 'ioredis';
23
23
  import { EvRedisPubSub } from './pub-sub.js';
24
24
  import { uid } from '../utils.js';
25
+ /**
26
+ * Redis-based implementation of {@link EvStateAdapter}.
27
+ *
28
+ * This adapter enables distributed state updates using Redis Pub/Sub.
29
+ * It supports:
30
+ * - Channel-based subscriptions
31
+ * - Multiple listeners per channel
32
+ * - Self-message filtering via instance ID
33
+ *
34
+ * Designed to be used by EvState / EvStateManager for
35
+ * cross-process state synchronization.
36
+ */
25
37
  class EvRedisAdapter {
38
+ /**
39
+ * Creates a new Redis state adapter.
40
+ *
41
+ * @param options - Optional Redis connection options
42
+ */
26
43
  constructor(options) {
44
+ /** Publisher Redis client */
27
45
  _EvRedisAdapter_pub.set(this, void 0);
46
+ /** Subscriber Redis client */
28
47
  _EvRedisAdapter_sub.set(this, void 0);
48
+ /**
49
+ * Channel → listeners mapping.
50
+ * Each channel may have multiple local handlers.
51
+ */
29
52
  _EvRedisAdapter_listeners.set(this, void 0);
53
+ /** Unique identifier for this adapter instance */
30
54
  _EvRedisAdapter_instanceId.set(this, void 0);
31
55
  __classPrivateFieldSet(this, _EvRedisAdapter_pub, new Redis(options), "f");
32
56
  __classPrivateFieldSet(this, _EvRedisAdapter_sub, new Redis(options), "f");
@@ -34,26 +58,45 @@ class EvRedisAdapter {
34
58
  __classPrivateFieldSet(this, _EvRedisAdapter_instanceId, uid({ counter: Math.ceil(Math.random() * 100) }), "f");
35
59
  __classPrivateFieldGet(this, _EvRedisAdapter_sub, "f").on('message', (channel, message) => {
36
60
  const handlers = __classPrivateFieldGet(this, _EvRedisAdapter_listeners, "f").get(channel);
37
- if (handlers) {
38
- let parsed;
39
- let canHandle;
40
- try {
41
- parsed = JSON.parse(message);
42
- canHandle = (parsed === null || parsed === void 0 ? void 0 : parsed.id) !== __classPrivateFieldGet(this, _EvRedisAdapter_instanceId, "f");
43
- }
44
- catch (_a) {
45
- return;
46
- }
47
- if (canHandle)
48
- handlers.forEach((handler) => handler(parsed === null || parsed === void 0 ? void 0 : parsed.message));
61
+ if (!handlers)
62
+ return;
63
+ let parsed;
64
+ try {
65
+ parsed = JSON.parse(message);
49
66
  }
67
+ catch (_a) {
68
+ // Ignore malformed payloads
69
+ return;
70
+ }
71
+ // Ignore messages published by this instance
72
+ if ((parsed === null || parsed === void 0 ? void 0 : parsed.id) === __classPrivateFieldGet(this, _EvRedisAdapter_instanceId, "f"))
73
+ return;
74
+ handlers.forEach((handler) => handler(parsed === null || parsed === void 0 ? void 0 : parsed.message));
50
75
  });
51
76
  }
77
+ /**
78
+ * Publishes a message to a Redis channel.
79
+ *
80
+ * The payload is wrapped with the instance ID to
81
+ * prevent self-delivery.
82
+ *
83
+ * @param channel - Redis channel name
84
+ * @param message - Message payload
85
+ */
52
86
  publish(channel, message) {
53
87
  return __awaiter(this, void 0, void 0, function* () {
54
- yield __classPrivateFieldGet(this, _EvRedisAdapter_pub, "f").publish(channel, JSON.stringify({ id: __classPrivateFieldGet(this, _EvRedisAdapter_instanceId, "f"), message: message }));
88
+ yield __classPrivateFieldGet(this, _EvRedisAdapter_pub, "f").publish(channel, JSON.stringify({ id: __classPrivateFieldGet(this, _EvRedisAdapter_instanceId, "f"), message }));
55
89
  });
56
90
  }
91
+ /**
92
+ * Subscribes to a Redis channel.
93
+ *
94
+ * Multiple listeners can be registered per channel.
95
+ * The Redis subscription is created only once per channel.
96
+ *
97
+ * @param channel - Redis channel name
98
+ * @param onMessage - Callback invoked on incoming messages
99
+ */
57
100
  subscribe(channel, onMessage) {
58
101
  return __awaiter(this, void 0, void 0, function* () {
59
102
  if (!__classPrivateFieldGet(this, _EvRedisAdapter_listeners, "f").has(channel)) {
@@ -63,12 +106,20 @@ class EvRedisAdapter {
63
106
  __classPrivateFieldGet(this, _EvRedisAdapter_listeners, "f").get(channel).add(onMessage);
64
107
  });
65
108
  }
109
+ /**
110
+ * Unsubscribes from a Redis channel and removes all listeners.
111
+ *
112
+ * @param channel - Redis channel name
113
+ */
66
114
  unsubscribe(channel) {
67
115
  return __awaiter(this, void 0, void 0, function* () {
68
116
  yield __classPrivateFieldGet(this, _EvRedisAdapter_sub, "f").unsubscribe(channel);
69
117
  __classPrivateFieldGet(this, _EvRedisAdapter_listeners, "f").delete(channel);
70
118
  });
71
119
  }
120
+ /**
121
+ * Gracefully closes Redis connections.
122
+ */
72
123
  quit() {
73
124
  __classPrivateFieldGet(this, _EvRedisAdapter_pub, "f").quit();
74
125
  __classPrivateFieldGet(this, _EvRedisAdapter_sub, "f").quit();
@@ -2,20 +2,94 @@ import type { EvStreamManager } from '../manager.js';
2
2
  import type { EvRedisAdapter } from '../adapters/redis.js';
3
3
  import type { EvRedisPubSub } from '../adapters/pub-sub.js';
4
4
  import { EvState } from '../state.js';
5
+ /**
6
+ * Options for creating an {@link EvStateManager}.
7
+ */
5
8
  interface EvStateManagerOptions {
9
+ /**
10
+ * Stream manager responsible for managing client connections
11
+ * and broadcasting state updates.
12
+ */
6
13
  manager: EvStreamManager;
14
+ /**
15
+ * Optional distributed state adapter (e.g. Redis).
16
+ * Enables cross-process state propagation.
17
+ */
7
18
  adapter?: EvRedisAdapter;
19
+ /**
20
+ * Optional Pub/Sub instance used to synchronize
21
+ * state creation and removal across instances.
22
+ */
8
23
  pubsub?: EvRedisPubSub;
9
24
  }
25
+ /**
26
+ * Manages a collection of named {@link EvState} instances.
27
+ *
28
+ * Responsibilities:
29
+ * - Create and cache state objects locally
30
+ * - Synchronize state lifecycle (create/remove) across processes
31
+ * - Bridge EvState with stream manager and adapters
32
+ *
33
+ * Internally, all state keys are converted to strings to remain
34
+ * Redis-safe and transport-friendly.
35
+ *
36
+ * @typeParam S - Mapping of state keys to their value types
37
+ */
10
38
  export declare class EvStateManager<S extends Record<string, any>> {
11
39
  #private;
40
+ /**
41
+ * Creates a new state manager.
42
+ *
43
+ * @param options - Initialization options
44
+ */
12
45
  constructor({ manager, adapter, pubsub }: EvStateManagerOptions);
46
+ /**
47
+ * Creates a state locally without emitting Pub/Sub events.
48
+ *
49
+ * @param channel - State channel name
50
+ * @param initialValue - Initial state value
51
+ */
13
52
  private createLocalState;
53
+ /**
54
+ * Removes a state locally without emitting Pub/Sub events.
55
+ *
56
+ * @param channel - State channel name
57
+ */
14
58
  private removeLocalState;
59
+ /**
60
+ * Creates or returns an existing state.
61
+ *
62
+ * If Pub/Sub is enabled, the creation is broadcast
63
+ * to other instances.
64
+ *
65
+ * @param key - State key
66
+ * @param initialValue - Initial state value
67
+ */
15
68
  createState<K extends keyof S>(key: K, initialValue: S[K]): EvState<S[K]>;
69
+ /**
70
+ * Retrieves an existing state.
71
+ *
72
+ * @param key - State key
73
+ */
16
74
  getState<K extends keyof S>(key: K): EvState<S[K]> | undefined;
75
+ /**
76
+ * Checks whether a state exists.
77
+ *
78
+ * @param key - State key
79
+ */
17
80
  hasState<K extends keyof S>(key: K): boolean;
81
+ /**
82
+ * Removes a state locally and propagates the removal
83
+ * to other instances via Pub/Sub.
84
+ *
85
+ * @param key - State key
86
+ */
18
87
  removeState<K extends keyof S>(key: K): void;
88
+ /**
89
+ * Handles incoming Pub/Sub lifecycle events.
90
+ *
91
+ * @param msg - Pub/Sub message payload
92
+ */
19
93
  private pubSubCallback;
20
94
  }
21
95
  export {};
@@ -11,12 +11,38 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
11
11
  };
12
12
  var _EvStateManager_states, _EvStateManager_manager, _EvStateManager_adapter, _EvStateManager_pubsub;
13
13
  import { EvState } from '../state.js';
14
+ /**
15
+ * Manages a collection of named {@link EvState} instances.
16
+ *
17
+ * Responsibilities:
18
+ * - Create and cache state objects locally
19
+ * - Synchronize state lifecycle (create/remove) across processes
20
+ * - Bridge EvState with stream manager and adapters
21
+ *
22
+ * Internally, all state keys are converted to strings to remain
23
+ * Redis-safe and transport-friendly.
24
+ *
25
+ * @typeParam S - Mapping of state keys to their value types
26
+ */
14
27
  export class EvStateManager {
28
+ /**
29
+ * Creates a new state manager.
30
+ *
31
+ * @param options - Initialization options
32
+ */
15
33
  constructor({ manager, adapter, pubsub }) {
16
- // 🔑 Internally everything is string-based (Redis-safe)
17
- _EvStateManager_states.set(this, new Map());
34
+ /**
35
+ * Internal state registry.
36
+ * Keyed by string channel name.
37
+ */
38
+ _EvStateManager_states.set(this, new Map()
39
+ /** Stream manager used by all states */
40
+ );
41
+ /** Stream manager used by all states */
18
42
  _EvStateManager_manager.set(this, void 0);
43
+ /** Optional distributed adapter */
19
44
  _EvStateManager_adapter.set(this, void 0);
45
+ /** Optional Pub/Sub synchronizer */
20
46
  _EvStateManager_pubsub.set(this, void 0);
21
47
  __classPrivateFieldSet(this, _EvStateManager_manager, manager, "f");
22
48
  __classPrivateFieldSet(this, _EvStateManager_adapter, adapter, "f");
@@ -26,6 +52,12 @@ export class EvStateManager {
26
52
  __classPrivateFieldGet(this, _EvStateManager_pubsub, "f").onMessage(this.pubSubCallback);
27
53
  }
28
54
  }
55
+ /**
56
+ * Creates a state locally without emitting Pub/Sub events.
57
+ *
58
+ * @param channel - State channel name
59
+ * @param initialValue - Initial state value
60
+ */
29
61
  createLocalState(channel, initialValue) {
30
62
  const state = new EvState({
31
63
  channel,
@@ -36,9 +68,23 @@ export class EvStateManager {
36
68
  __classPrivateFieldGet(this, _EvStateManager_states, "f").set(channel, state);
37
69
  return state;
38
70
  }
71
+ /**
72
+ * Removes a state locally without emitting Pub/Sub events.
73
+ *
74
+ * @param channel - State channel name
75
+ */
39
76
  removeLocalState(channel) {
40
77
  __classPrivateFieldGet(this, _EvStateManager_states, "f").delete(channel);
41
78
  }
79
+ /**
80
+ * Creates or returns an existing state.
81
+ *
82
+ * If Pub/Sub is enabled, the creation is broadcast
83
+ * to other instances.
84
+ *
85
+ * @param key - State key
86
+ * @param initialValue - Initial state value
87
+ */
42
88
  createState(key, initialValue) {
43
89
  var _a;
44
90
  const channel = String(key);
@@ -53,12 +99,28 @@ export class EvStateManager {
53
99
  });
54
100
  return state;
55
101
  }
102
+ /**
103
+ * Retrieves an existing state.
104
+ *
105
+ * @param key - State key
106
+ */
56
107
  getState(key) {
57
108
  return __classPrivateFieldGet(this, _EvStateManager_states, "f").get(String(key));
58
109
  }
110
+ /**
111
+ * Checks whether a state exists.
112
+ *
113
+ * @param key - State key
114
+ */
59
115
  hasState(key) {
60
116
  return __classPrivateFieldGet(this, _EvStateManager_states, "f").has(String(key));
61
117
  }
118
+ /**
119
+ * Removes a state locally and propagates the removal
120
+ * to other instances via Pub/Sub.
121
+ *
122
+ * @param key - State key
123
+ */
62
124
  removeState(key) {
63
125
  var _a;
64
126
  const channel = String(key);
@@ -68,6 +130,11 @@ export class EvStateManager {
68
130
  channel,
69
131
  });
70
132
  }
133
+ /**
134
+ * Handles incoming Pub/Sub lifecycle events.
135
+ *
136
+ * @param msg - Pub/Sub message payload
137
+ */
71
138
  pubSubCallback(msg) {
72
139
  if (!msg || typeof msg.channel !== 'string')
73
140
  return;
package/dist/manager.d.ts CHANGED
@@ -3,31 +3,41 @@ import type { EvManagerOptions, EvMessage, EvOnClose, EvOptions } from './types.
3
3
  /**
4
4
  * `EvStreamManager` manages multiple SSE connections.
5
5
  * Handles client creation, broadcasting messages, and channel-based listeners.
6
- *
7
- * Example :
8
- *
9
- * ```javascript
10
- * const evManager = new EvStreamManager();
11
- *
12
- * const stream = evManager.createStream(req, res);
13
- * ```
14
- *
15
6
  */
16
7
  export declare class EvStreamManager {
17
8
  #private;
18
9
  constructor(opts?: EvManagerOptions);
19
10
  /**
20
- * Creates a new SSE stream, tracks it, and returns control methods.
21
- * Enforces max connection limit.
11
+ * Creates a new SSE stream
22
12
  */
23
13
  createStream(req: IncomingMessage, res: ServerResponse, opts?: EvOptions): {
14
+ id: string;
24
15
  authenticate: any;
25
16
  message: any;
26
17
  close: (onClose?: EvOnClose) => void;
27
18
  listen: (name: string) => void;
28
19
  };
29
20
  /**
30
- * Sends a message to all clients listening to a specific channel.
21
+ * Sends a message directly to a local client by ID.
22
+ *
23
+ * This method only targets clients connected to the current process.
24
+ * It does not forward the message to Redis.
25
+ */
26
+ private toLocal;
27
+ /**
28
+ * Sends a message to a specific client by ID.
29
+ *
30
+ * If the client exists locally, the message is delivered immediately.
31
+ * If not, the message is forwarded through Redis so another instance
32
+ * can deliver it to the target client.
33
+ */
34
+ to(id: string, msg: EvMessage): void;
35
+ /**
36
+ * Send message locally to listeners
37
+ */
38
+ private sendLocal;
39
+ /**
40
+ * Sends message to channel (local + Redis)
31
41
  */
32
42
  send(name: string, msg: EvMessage): void;
33
43
  }
package/dist/manager.js CHANGED
@@ -9,22 +9,13 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
9
9
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
10
10
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
11
11
  };
12
- var _EvStreamManager_instances, _EvStreamManager_clients, _EvStreamManager_listeners, _EvStreamManager_count, _EvStreamManager_maxConnections, _EvStreamManager_maxListeners, _EvStreamManager_id, _EvStreamManager_listen, _EvStreamManager_unlisten;
12
+ var _EvStreamManager_instances, _EvStreamManager_clients, _EvStreamManager_listeners, _EvStreamManager_count, _EvStreamManager_maxConnections, _EvStreamManager_maxListeners, _EvStreamManager_pubSub, _EvStreamManager_id, _EvStreamManager_listen, _EvStreamManager_unlisten, _EvStreamManager_onMessage;
13
13
  import { Evstream } from './stream.js';
14
14
  import { uid } from './utils.js';
15
15
  import { EvMaxConnectionsError, EvMaxListenerError } from './errors.js';
16
16
  /**
17
17
  * `EvStreamManager` manages multiple SSE connections.
18
18
  * Handles client creation, broadcasting messages, and channel-based listeners.
19
- *
20
- * Example :
21
- *
22
- * ```javascript
23
- * const evManager = new EvStreamManager();
24
- *
25
- * const stream = evManager.createStream(req, res);
26
- * ```
27
- *
28
19
  */
29
20
  export class EvStreamManager {
30
21
  constructor(opts) {
@@ -34,6 +25,7 @@ export class EvStreamManager {
34
25
  _EvStreamManager_count.set(this, void 0);
35
26
  _EvStreamManager_maxConnections.set(this, void 0);
36
27
  _EvStreamManager_maxListeners.set(this, void 0);
28
+ _EvStreamManager_pubSub.set(this, void 0);
37
29
  _EvStreamManager_id.set(this, void 0);
38
30
  __classPrivateFieldSet(this, _EvStreamManager_clients, new Map(), "f");
39
31
  __classPrivateFieldSet(this, _EvStreamManager_listeners, new Map(), "f");
@@ -41,10 +33,13 @@ export class EvStreamManager {
41
33
  __classPrivateFieldSet(this, _EvStreamManager_maxConnections, (opts === null || opts === void 0 ? void 0 : opts.maxConnection) || 5000, "f");
42
34
  __classPrivateFieldSet(this, _EvStreamManager_maxListeners, (opts === null || opts === void 0 ? void 0 : opts.maxListeners) || 5000, "f");
43
35
  __classPrivateFieldSet(this, _EvStreamManager_id, opts === null || opts === void 0 ? void 0 : opts.id, "f");
36
+ __classPrivateFieldSet(this, _EvStreamManager_pubSub, opts === null || opts === void 0 ? void 0 : opts.pubSub, "f");
37
+ if (__classPrivateFieldGet(this, _EvStreamManager_pubSub, "f")) {
38
+ __classPrivateFieldGet(this, _EvStreamManager_pubSub, "f").onMessage((msg) => __classPrivateFieldGet(this, _EvStreamManager_instances, "m", _EvStreamManager_onMessage).call(this, msg));
39
+ }
44
40
  }
45
41
  /**
46
- * Creates a new SSE stream, tracks it, and returns control methods.
47
- * Enforces max connection limit.
42
+ * Creates a new SSE stream
48
43
  */
49
44
  createStream(req, res, opts) {
50
45
  if (__classPrivateFieldGet(this, _EvStreamManager_count, "f") >= __classPrivateFieldGet(this, _EvStreamManager_maxConnections, "f")) {
@@ -52,70 +47,99 @@ export class EvStreamManager {
52
47
  }
53
48
  const client = new Evstream(req, res, opts);
54
49
  const id = uid({ counter: __classPrivateFieldGet(this, _EvStreamManager_count, "f"), prefix: __classPrivateFieldGet(this, _EvStreamManager_id, "f") });
55
- const channel = [];
50
+ const channels = [];
56
51
  let isClosed = false;
57
52
  __classPrivateFieldSet(this, _EvStreamManager_count, __classPrivateFieldGet(this, _EvStreamManager_count, "f") + 1, "f");
58
53
  __classPrivateFieldGet(this, _EvStreamManager_clients, "f").set(id, client);
59
54
  const close = (onClose) => {
60
55
  if (isClosed)
61
56
  return;
57
+ isClosed = true;
62
58
  if (typeof onClose === 'function') {
63
- onClose(channel);
59
+ onClose(channels);
64
60
  }
65
- isClosed = true;
66
- // Remove close event listener to prevent memory leaks
67
61
  res.removeAllListeners('close');
68
- // Clean up client
69
62
  client.close();
70
- // Decrement count
71
63
  __classPrivateFieldSet(this, _EvStreamManager_count, __classPrivateFieldGet(this, _EvStreamManager_count, "f") - 1, "f");
72
- // Remove from all channels
73
- channel.forEach(chan => __classPrivateFieldGet(this, _EvStreamManager_instances, "m", _EvStreamManager_unlisten).call(this, chan, id));
74
- // Clear channel array to release references
75
- channel.length = 0;
76
- // Remove client from map
64
+ channels.forEach((ch) => __classPrivateFieldGet(this, _EvStreamManager_instances, "m", _EvStreamManager_unlisten).call(this, ch, id));
65
+ channels.length = 0;
77
66
  __classPrivateFieldGet(this, _EvStreamManager_clients, "f").delete(id);
78
- // End response if not already ended
79
- if (!res.writableEnded) {
67
+ if (!res.writableEnded)
80
68
  res.end();
81
- }
82
- };
83
- const onCloseHandler = () => {
84
- if (!isClosed) {
85
- close();
86
- }
87
69
  };
88
- res.on('close', onCloseHandler);
70
+ res.on('close', close);
89
71
  return {
72
+ id: id,
90
73
  authenticate: client.authenticate.bind(client),
91
74
  message: client.message.bind(client),
92
- close: close,
75
+ close,
93
76
  listen: (name) => {
94
77
  if (isClosed)
95
78
  return;
96
- channel.push(name);
79
+ channels.push(name);
97
80
  __classPrivateFieldGet(this, _EvStreamManager_instances, "m", _EvStreamManager_listen).call(this, name, id);
98
81
  },
99
82
  };
100
83
  }
101
84
  /**
102
- * Sends a message to all clients listening to a specific channel.
85
+ * Sends a message directly to a local client by ID.
86
+ *
87
+ * This method only targets clients connected to the current process.
88
+ * It does not forward the message to Redis.
103
89
  */
104
- send(name, msg) {
90
+ toLocal(id, msg) {
91
+ const client = __classPrivateFieldGet(this, _EvStreamManager_clients, "f").get(id);
92
+ if (client) {
93
+ client.message({
94
+ data: msg,
95
+ });
96
+ }
97
+ return client;
98
+ }
99
+ /**
100
+ * Sends a message to a specific client by ID.
101
+ *
102
+ * If the client exists locally, the message is delivered immediately.
103
+ * If not, the message is forwarded through Redis so another instance
104
+ * can deliver it to the target client.
105
+ */
106
+ to(id, msg) {
107
+ const client = this.toLocal(id, msg);
108
+ if (!client) {
109
+ if (__classPrivateFieldGet(this, _EvStreamManager_pubSub, "f")) {
110
+ __classPrivateFieldGet(this, _EvStreamManager_pubSub, "f").send({ type: 'to', data: { id: id, message: msg } });
111
+ }
112
+ }
113
+ }
114
+ /**
115
+ * Send message locally to listeners
116
+ */
117
+ sendLocal(name, msg) {
105
118
  const listeners = __classPrivateFieldGet(this, _EvStreamManager_listeners, "f").get(name);
106
119
  if (!listeners)
107
- return;
108
- for (const [_, id] of listeners.entries()) {
120
+ return msg;
121
+ for (const id of listeners) {
109
122
  const client = __classPrivateFieldGet(this, _EvStreamManager_clients, "f").get(id);
110
- if (client) {
111
- client.message(Object.assign(Object.assign({}, msg), { data: typeof msg.data === 'string'
112
- ? { ch: name, data: msg }
113
- : Object.assign({ ch: name }, msg.data) }));
123
+ if (!client) {
124
+ continue;
114
125
  }
126
+ client.message(Object.assign(Object.assign({}, msg), { data: typeof msg.data === 'string'
127
+ ? { ch: name, data: msg }
128
+ : Object.assign({ ch: name }, msg.data) }));
129
+ }
130
+ return msg;
131
+ }
132
+ /**
133
+ * Sends message to channel (local + Redis)
134
+ */
135
+ send(name, msg) {
136
+ this.sendLocal(name, msg);
137
+ if (__classPrivateFieldGet(this, _EvStreamManager_pubSub, "f")) {
138
+ __classPrivateFieldGet(this, _EvStreamManager_pubSub, "f").send({ type: 'send', data: { name, message: msg } });
115
139
  }
116
140
  }
117
141
  }
118
- _EvStreamManager_clients = new WeakMap(), _EvStreamManager_listeners = new WeakMap(), _EvStreamManager_count = new WeakMap(), _EvStreamManager_maxConnections = new WeakMap(), _EvStreamManager_maxListeners = new WeakMap(), _EvStreamManager_id = new WeakMap(), _EvStreamManager_instances = new WeakSet(), _EvStreamManager_listen = function _EvStreamManager_listen(name, id) {
142
+ _EvStreamManager_clients = new WeakMap(), _EvStreamManager_listeners = new WeakMap(), _EvStreamManager_count = new WeakMap(), _EvStreamManager_maxConnections = new WeakMap(), _EvStreamManager_maxListeners = new WeakMap(), _EvStreamManager_pubSub = new WeakMap(), _EvStreamManager_id = new WeakMap(), _EvStreamManager_instances = new WeakSet(), _EvStreamManager_listen = function _EvStreamManager_listen(name, id) {
119
143
  let listeners = __classPrivateFieldGet(this, _EvStreamManager_listeners, "f").get(name);
120
144
  if (!listeners) {
121
145
  listeners = new Set();
@@ -126,11 +150,28 @@ _EvStreamManager_clients = new WeakMap(), _EvStreamManager_listeners = new WeakM
126
150
  }
127
151
  listeners.add(id);
128
152
  }, _EvStreamManager_unlisten = function _EvStreamManager_unlisten(name, id) {
129
- const isListenerExists = __classPrivateFieldGet(this, _EvStreamManager_listeners, "f").get(name);
130
- if (isListenerExists) {
131
- isListenerExists.delete(id);
132
- if (isListenerExists.size === 0) {
133
- __classPrivateFieldGet(this, _EvStreamManager_listeners, "f").delete(name);
134
- }
153
+ const listeners = __classPrivateFieldGet(this, _EvStreamManager_listeners, "f").get(name);
154
+ if (!listeners)
155
+ return;
156
+ listeners.delete(id);
157
+ if (listeners.size === 0) {
158
+ __classPrivateFieldGet(this, _EvStreamManager_listeners, "f").delete(name);
159
+ }
160
+ }, _EvStreamManager_onMessage = function _EvStreamManager_onMessage(msg) {
161
+ var _a, _b, _c, _d;
162
+ const type = msg === null || msg === void 0 ? void 0 : msg.type;
163
+ switch (type) {
164
+ case 'send':
165
+ const name = (_a = msg === null || msg === void 0 ? void 0 : msg.data) === null || _a === void 0 ? void 0 : _a.name;
166
+ const message = (_b = msg === null || msg === void 0 ? void 0 : msg.data) === null || _b === void 0 ? void 0 : _b.message;
167
+ if (!name || !message)
168
+ return;
169
+ this.sendLocal(name, message);
170
+ break;
171
+ case 'to':
172
+ const data = (_c = msg === null || msg === void 0 ? void 0 : msg.data) === null || _c === void 0 ? void 0 : _c.message;
173
+ const id = (_d = msg === null || msg === void 0 ? void 0 : msg.data) === null || _d === void 0 ? void 0 : _d.id;
174
+ this.toLocal(id, data);
175
+ break;
135
176
  }
136
177
  };
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { EvRedisPubSub } from './adapters/pub-sub.js';
1
2
  import type { EvStreamManager } from './manager.js';
2
3
  export type EvEventsType = 'data' | 'error' | 'end';
3
4
  export interface EvMessage {
@@ -18,6 +19,7 @@ export interface EvManagerOptions {
18
19
  id?: string;
19
20
  maxConnection?: number;
20
21
  maxListeners?: number;
22
+ pubSub?: EvRedisPubSub;
21
23
  }
22
24
  export interface EvStateAdapter {
23
25
  publish(channel: string, message: any): Promise<void>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "evstream",
3
- "version": "1.0.3",
3
+ "version": "1.0.4",
4
4
  "description": "A simple and easy to implement server sent event library for express.js",
5
5
  "keywords": [
6
6
  "sse",
package/src/manager.ts CHANGED
@@ -5,167 +5,220 @@ import { uid } from './utils.js'
5
5
  import { EvMaxConnectionsError, EvMaxListenerError } from './errors.js'
6
6
 
7
7
  import type {
8
- EvManagerOptions,
9
- EvMessage,
10
- EvOnClose,
11
- EvOptions,
8
+ EvManagerOptions,
9
+ EvMessage,
10
+ EvOnClose,
11
+ EvOptions,
12
12
  } from './types.js'
13
+ import type { EvRedisPubSub } from './adapters/pub-sub.js'
13
14
 
14
15
  /**
15
16
  * `EvStreamManager` manages multiple SSE connections.
16
17
  * Handles client creation, broadcasting messages, and channel-based listeners.
17
- *
18
- * Example :
19
- *
20
- * ```javascript
21
- * const evManager = new EvStreamManager();
22
- *
23
- * const stream = evManager.createStream(req, res);
24
- * ```
25
- *
26
18
  */
27
19
  export class EvStreamManager {
28
- #clients: Map<string, Evstream>
29
- #listeners: Map<string, Set<string>>
30
- #count: number
31
- #maxConnections: number
32
- #maxListeners: number
33
- #id?: string
34
- constructor(opts?: EvManagerOptions) {
35
- this.#clients = new Map()
36
- this.#listeners = new Map()
37
- this.#count = 0
38
-
39
- this.#maxConnections = opts?.maxConnection || 5000
40
- this.#maxListeners = opts?.maxListeners || 5000
41
- this.#id = opts?.id
42
- }
43
-
44
- /**
45
- * Creates a new SSE stream, tracks it, and returns control methods.
46
- * Enforces max connection limit.
47
- */
48
- createStream(req: IncomingMessage, res: ServerResponse, opts?: EvOptions) {
49
- if (this.#count >= this.#maxConnections) {
50
- throw new EvMaxConnectionsError(this.#maxConnections)
51
- }
52
-
53
- const client = new Evstream(req, res, opts)
54
- const id = uid({ counter: this.#count, prefix: this.#id })
55
- const channel: string[] = []
56
- let isClosed = false
57
-
58
- this.#count += 1
59
- this.#clients.set(id, client)
60
-
61
- const close = (onClose?: EvOnClose) => {
62
- if (isClosed) return
63
-
64
- if (typeof onClose === 'function') {
65
- onClose(channel)
66
- }
67
-
68
- isClosed = true
69
-
70
- // Remove close event listener to prevent memory leaks
71
- res.removeAllListeners('close')
72
-
73
- // Clean up client
74
- client.close()
75
-
76
- // Decrement count
77
- this.#count -= 1
78
-
79
- // Remove from all channels
80
- channel.forEach(chan => this.#unlisten(chan, id))
81
-
82
- // Clear channel array to release references
83
- channel.length = 0
84
-
85
- // Remove client from map
86
- this.#clients.delete(id)
87
-
88
- // End response if not already ended
89
- if (!res.writableEnded) {
90
- res.end()
91
- }
92
- }
93
-
94
- const onCloseHandler = () => {
95
- if (!isClosed) {
96
- close()
97
- }
98
- }
99
-
100
- res.on('close', onCloseHandler)
101
-
102
- return {
103
- authenticate: client.authenticate.bind(client),
104
- message: client.message.bind(client),
105
- close: close,
106
- listen: (name: string) => {
107
- if (isClosed) return
108
- channel.push(name)
109
- this.#listen(name, id)
110
- },
111
- }
112
- }
113
-
114
- /**
115
- * Sends a message to all clients listening to a specific channel.
116
- */
117
- send(name: string, msg: EvMessage) {
118
- const listeners = this.#listeners.get(name)
119
-
120
- if (!listeners) return
121
-
122
- for (const [_, id] of listeners.entries()) {
123
- const client = this.#clients.get(id)
124
-
125
- if (client) {
126
- client.message({
127
- ...msg,
128
- data:
129
- typeof msg.data === 'string'
130
- ? { ch: name, data: msg }
131
- : { ch: name, ...msg.data },
132
- })
133
- }
134
- }
135
- }
136
-
137
- /**
138
- * Adds a client to a specific channel.
139
- * Enforces max listeners per channel.
140
- */
141
- #listen(name: string, id: string) {
142
- let listeners = this.#listeners.get(name)
143
-
144
- if (!listeners) {
145
- listeners = new Set<string>()
146
- this.#listeners.set(name, listeners)
147
- }
148
-
149
- if (listeners.size >= this.#maxListeners) {
150
- throw new EvMaxListenerError(listeners.size, name)
151
- }
152
-
153
- listeners.add(id)
154
- }
155
-
156
- /**
157
- * Removes a client from a specific channel.
158
- * Deletes the channel if no listeners remain.
159
- */
160
- #unlisten(name: string, id: string) {
161
- const isListenerExists = this.#listeners.get(name)
162
-
163
- if (isListenerExists) {
164
- isListenerExists.delete(id)
165
-
166
- if (isListenerExists.size === 0) {
167
- this.#listeners.delete(name)
168
- }
169
- }
170
- }
20
+ #clients: Map<string, Evstream>
21
+ #listeners: Map<string, Set<string>>
22
+ #count: number
23
+ #maxConnections: number
24
+ #maxListeners: number
25
+ #pubSub?: EvRedisPubSub
26
+ #id?: string
27
+
28
+ constructor(opts?: EvManagerOptions) {
29
+ this.#clients = new Map()
30
+ this.#listeners = new Map()
31
+ this.#count = 0
32
+
33
+ this.#maxConnections = opts?.maxConnection || 5000
34
+ this.#maxListeners = opts?.maxListeners || 5000
35
+ this.#id = opts?.id
36
+
37
+ this.#pubSub = opts?.pubSub
38
+
39
+ if (this.#pubSub) {
40
+ this.#pubSub.onMessage((msg) => this.#onMessage(msg))
41
+ }
42
+ }
43
+
44
+ /**
45
+ * Creates a new SSE stream
46
+ */
47
+ createStream(req: IncomingMessage, res: ServerResponse, opts?: EvOptions) {
48
+ if (this.#count >= this.#maxConnections) {
49
+ throw new EvMaxConnectionsError(this.#maxConnections)
50
+ }
51
+
52
+ const client = new Evstream(req, res, opts)
53
+ const id = uid({ counter: this.#count, prefix: this.#id })
54
+ const channels: string[] = []
55
+ let isClosed = false
56
+
57
+ this.#count += 1
58
+ this.#clients.set(id, client)
59
+
60
+ const close = (onClose?: EvOnClose) => {
61
+ if (isClosed) return
62
+ isClosed = true
63
+
64
+ if (typeof onClose === 'function') {
65
+ onClose(channels)
66
+ }
67
+
68
+ res.removeAllListeners('close')
69
+ client.close()
70
+
71
+ this.#count -= 1
72
+ channels.forEach((ch) => this.#unlisten(ch, id))
73
+ channels.length = 0
74
+ this.#clients.delete(id)
75
+
76
+ if (!res.writableEnded) res.end()
77
+ }
78
+
79
+ res.on('close', close)
80
+
81
+ return {
82
+ id: id,
83
+ authenticate: client.authenticate.bind(client),
84
+ message: client.message.bind(client),
85
+ close,
86
+ listen: (name: string) => {
87
+ if (isClosed) return
88
+ channels.push(name)
89
+ this.#listen(name, id)
90
+ },
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Sends a message directly to a local client by ID.
96
+ *
97
+ * This method only targets clients connected to the current process.
98
+ * It does not forward the message to Redis.
99
+ */
100
+ private toLocal(id: string, msg: EvMessage) {
101
+ const client = this.#clients.get(id)
102
+
103
+ if (client) {
104
+ client.message({
105
+ data: msg,
106
+ })
107
+ }
108
+
109
+ return client
110
+ }
111
+
112
+ /**
113
+ * Sends a message to a specific client by ID.
114
+ *
115
+ * If the client exists locally, the message is delivered immediately.
116
+ * If not, the message is forwarded through Redis so another instance
117
+ * can deliver it to the target client.
118
+ */
119
+ to(id: string, msg: EvMessage) {
120
+ const client = this.toLocal(id, msg)
121
+
122
+ if (!client) {
123
+ if (this.#pubSub) {
124
+ this.#pubSub.send({ type: 'to', data: { id: id, message: msg } })
125
+ }
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Send message locally to listeners
131
+ */
132
+ private sendLocal(name: string, msg: EvMessage) {
133
+ const listeners = this.#listeners.get(name)
134
+
135
+ if (!listeners) return msg
136
+
137
+ for (const id of listeners) {
138
+ const client = this.#clients.get(id)
139
+
140
+ if (!client) {
141
+ continue
142
+ }
143
+
144
+ client.message({
145
+ ...msg,
146
+ data:
147
+ typeof msg.data === 'string'
148
+ ? { ch: name, data: msg }
149
+ : { ch: name, ...msg.data },
150
+ })
151
+ }
152
+
153
+ return msg
154
+ }
155
+
156
+ /**
157
+ * Sends message to channel (local + Redis)
158
+ */
159
+ send(name: string, msg: EvMessage) {
160
+ this.sendLocal(name, msg)
161
+
162
+ if (this.#pubSub) {
163
+ this.#pubSub.send({ type: 'send', data: { name, message: msg } })
164
+ }
165
+ }
166
+
167
+ /**
168
+ * Subscribe client to channel
169
+ */
170
+ #listen(name: string, id: string) {
171
+ let listeners = this.#listeners.get(name)
172
+
173
+ if (!listeners) {
174
+ listeners = new Set()
175
+ this.#listeners.set(name, listeners)
176
+ }
177
+
178
+ if (listeners.size >= this.#maxListeners) {
179
+ throw new EvMaxListenerError(listeners.size, name)
180
+ }
181
+
182
+ listeners.add(id)
183
+ }
184
+
185
+ /**
186
+ * Unsubscribe client from channel
187
+ */
188
+ #unlisten(name: string, id: string) {
189
+ const listeners = this.#listeners.get(name)
190
+
191
+ if (!listeners) return
192
+
193
+ listeners.delete(id)
194
+
195
+ if (listeners.size === 0) {
196
+ this.#listeners.delete(name)
197
+ }
198
+ }
199
+
200
+ /**
201
+ * Redis → process entry
202
+ */
203
+ #onMessage(msg: Record<string, any>) {
204
+ const type = msg?.type
205
+
206
+ switch (type) {
207
+ case 'send':
208
+ const name = msg?.data?.name
209
+ const message = msg?.data?.message
210
+
211
+ if (!name || !message) return
212
+
213
+ this.sendLocal(name, message)
214
+ break
215
+
216
+ case 'to':
217
+ const data = msg?.data?.message
218
+ const id = msg?.data?.id
219
+
220
+ this.toLocal(id, data)
221
+ break
222
+ }
223
+ }
171
224
  }
package/src/types.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { EvRedisPubSub } from './adapters/pub-sub.js'
1
2
  import type { EvStreamManager } from './manager.js'
2
3
 
3
4
  // Built-in event types.
@@ -5,52 +6,54 @@ export type EvEventsType = 'data' | 'error' | 'end'
5
6
 
6
7
  // Represents a message sent to the client over SSE.
7
8
  export interface EvMessage {
8
- // Optional event name.
9
- event?: string | EvEventsType
10
- // Data to send; can be a string or object.
11
- data: string | object
12
- // Optional ID of the event.
13
- id?: string
9
+ // Optional event name.
10
+ event?: string | EvEventsType
11
+ // Data to send; can be a string or object.
12
+ data: string | object
13
+ // Optional ID of the event.
14
+ id?: string
14
15
  }
15
16
 
16
17
  // Options for token-based authentication from query parameters.
17
18
  export interface EvAuthenticationOptions {
18
- method: 'query'
19
- param: string
20
- verify: (token: string) => Promise<EvMessage> | undefined | null | boolean
19
+ method: 'query'
20
+ param: string
21
+ verify: (token: string) => Promise<EvMessage> | undefined | null | boolean
21
22
  }
22
23
 
23
24
  // Options for configuring a single SSE stream.
24
25
  export interface EvOptions {
25
- authentication?: EvAuthenticationOptions
26
- heartbeat?: number
26
+ authentication?: EvAuthenticationOptions
27
+ heartbeat?: number
27
28
  }
28
29
 
29
30
  // Configuration options for EvStreamManager.
30
31
  export interface EvManagerOptions {
31
- // Unique ID for the manager
32
- id?: string
32
+ // Unique ID for the manager
33
+ id?: string
33
34
 
34
- // Max Connection which a manager can handle. If this limit exceeds it throws `EvMaxConnectionsError`
35
- maxConnection?: number
35
+ // Max Connection which a manager can handle. If this limit exceeds it throws `EvMaxConnectionsError`
36
+ maxConnection?: number
36
37
 
37
- // Max Listeners which a listener can broadcast a message to. If this limit exceeds it throw `EvMaxListenerError`
38
- maxListeners?: number
38
+ // Max Listeners which a listener can broadcast a message to. If this limit exceeds it throw `EvMaxListenerError`
39
+ maxListeners?: number
40
+
41
+ pubSub?: EvRedisPubSub
39
42
  }
40
43
 
41
44
  // Options for initializing EvState.
42
45
  export interface EvStateAdapter {
43
- publish(channel: string, message: any): Promise<void>
44
- subscribe(channel: string, onMessage: (message: any) => void): Promise<void>
45
- unsubscribe(channel: string): Promise<void>
46
+ publish(channel: string, message: any): Promise<void>
47
+ subscribe(channel: string, onMessage: (message: any) => void): Promise<void>
48
+ unsubscribe(channel: string): Promise<void>
46
49
  }
47
50
 
48
51
  export interface EvStateOptions<T> {
49
- initialValue: T
50
- channel: string
51
- manager: EvStreamManager
52
- key?: string
53
- adapter?: EvStateAdapter
52
+ initialValue: T
53
+ channel: string
54
+ manager: EvStreamManager
55
+ key?: string
56
+ adapter?: EvStateAdapter
54
57
  }
55
58
 
56
59
  export type EvOnClose = (channels: string[]) => Promise<void>