@manonero/chat-client-sdk 1.0.0-beta.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 (110) hide show
  1. package/README.md +2343 -0
  2. package/dist/ChatClient.d.ts +122 -0
  3. package/dist/ChatClient.d.ts.map +1 -0
  4. package/dist/ChatClient.js +173 -0
  5. package/dist/ChatClient.js.map +1 -0
  6. package/dist/errors/ChatApiError.d.ts +22 -0
  7. package/dist/errors/ChatApiError.d.ts.map +1 -0
  8. package/dist/errors/ChatApiError.js +50 -0
  9. package/dist/errors/ChatApiError.js.map +1 -0
  10. package/dist/http/AuthApi.d.ts +26 -0
  11. package/dist/http/AuthApi.d.ts.map +1 -0
  12. package/dist/http/AuthApi.js +35 -0
  13. package/dist/http/AuthApi.js.map +1 -0
  14. package/dist/http/BotApi.d.ts +50 -0
  15. package/dist/http/BotApi.d.ts.map +1 -0
  16. package/dist/http/BotApi.js +67 -0
  17. package/dist/http/BotApi.js.map +1 -0
  18. package/dist/http/ConversationApi.d.ts +81 -0
  19. package/dist/http/ConversationApi.d.ts.map +1 -0
  20. package/dist/http/ConversationApi.js +115 -0
  21. package/dist/http/ConversationApi.js.map +1 -0
  22. package/dist/http/FileApi.d.ts +48 -0
  23. package/dist/http/FileApi.d.ts.map +1 -0
  24. package/dist/http/FileApi.js +68 -0
  25. package/dist/http/FileApi.js.map +1 -0
  26. package/dist/http/HealthApi.d.ts +26 -0
  27. package/dist/http/HealthApi.d.ts.map +1 -0
  28. package/dist/http/HealthApi.js +31 -0
  29. package/dist/http/HealthApi.js.map +1 -0
  30. package/dist/http/HttpClient.d.ts +51 -0
  31. package/dist/http/HttpClient.d.ts.map +1 -0
  32. package/dist/http/HttpClient.js +182 -0
  33. package/dist/http/HttpClient.js.map +1 -0
  34. package/dist/http/MessageApi.d.ts +67 -0
  35. package/dist/http/MessageApi.d.ts.map +1 -0
  36. package/dist/http/MessageApi.js +91 -0
  37. package/dist/http/MessageApi.js.map +1 -0
  38. package/dist/http/ParticipantApi.d.ts +28 -0
  39. package/dist/http/ParticipantApi.d.ts.map +1 -0
  40. package/dist/http/ParticipantApi.js +37 -0
  41. package/dist/http/ParticipantApi.js.map +1 -0
  42. package/dist/http/ProxyApi.d.ts +41 -0
  43. package/dist/http/ProxyApi.d.ts.map +1 -0
  44. package/dist/http/ProxyApi.js +57 -0
  45. package/dist/http/ProxyApi.js.map +1 -0
  46. package/dist/index.d.ts +26 -0
  47. package/dist/index.d.ts.map +1 -0
  48. package/dist/index.js +18 -0
  49. package/dist/index.js.map +1 -0
  50. package/dist/realtime/ChatHubClient.d.ts +87 -0
  51. package/dist/realtime/ChatHubClient.d.ts.map +1 -0
  52. package/dist/realtime/ChatHubClient.js +182 -0
  53. package/dist/realtime/ChatHubClient.js.map +1 -0
  54. package/dist/realtime/NotificationHubClient.d.ts +73 -0
  55. package/dist/realtime/NotificationHubClient.d.ts.map +1 -0
  56. package/dist/realtime/NotificationHubClient.js +162 -0
  57. package/dist/realtime/NotificationHubClient.js.map +1 -0
  58. package/dist/realtime/ReconnectionManager.d.ts +65 -0
  59. package/dist/realtime/ReconnectionManager.d.ts.map +1 -0
  60. package/dist/realtime/ReconnectionManager.js +118 -0
  61. package/dist/realtime/ReconnectionManager.js.map +1 -0
  62. package/dist/types/auth.d.ts +28 -0
  63. package/dist/types/auth.d.ts.map +1 -0
  64. package/dist/types/auth.js +3 -0
  65. package/dist/types/auth.js.map +1 -0
  66. package/dist/types/block.d.ts +148 -0
  67. package/dist/types/block.d.ts.map +1 -0
  68. package/dist/types/block.js +47 -0
  69. package/dist/types/block.js.map +1 -0
  70. package/dist/types/bot.d.ts +48 -0
  71. package/dist/types/bot.d.ts.map +1 -0
  72. package/dist/types/bot.js +3 -0
  73. package/dist/types/bot.js.map +1 -0
  74. package/dist/types/chat-events.d.ts +153 -0
  75. package/dist/types/chat-events.d.ts.map +1 -0
  76. package/dist/types/chat-events.js +3 -0
  77. package/dist/types/chat-events.js.map +1 -0
  78. package/dist/types/common.d.ts +57 -0
  79. package/dist/types/common.d.ts.map +1 -0
  80. package/dist/types/common.js +3 -0
  81. package/dist/types/common.js.map +1 -0
  82. package/dist/types/conversation.d.ts +70 -0
  83. package/dist/types/conversation.d.ts.map +1 -0
  84. package/dist/types/conversation.js +3 -0
  85. package/dist/types/conversation.js.map +1 -0
  86. package/dist/types/file.d.ts +31 -0
  87. package/dist/types/file.d.ts.map +1 -0
  88. package/dist/types/file.js +3 -0
  89. package/dist/types/file.js.map +1 -0
  90. package/dist/types/message.d.ts +83 -0
  91. package/dist/types/message.d.ts.map +1 -0
  92. package/dist/types/message.js +3 -0
  93. package/dist/types/message.js.map +1 -0
  94. package/dist/types/notification-events.d.ts +97 -0
  95. package/dist/types/notification-events.d.ts.map +1 -0
  96. package/dist/types/notification-events.js +3 -0
  97. package/dist/types/notification-events.js.map +1 -0
  98. package/dist/types/participant.d.ts +25 -0
  99. package/dist/types/participant.d.ts.map +1 -0
  100. package/dist/types/participant.js +3 -0
  101. package/dist/types/participant.js.map +1 -0
  102. package/dist/types/signalr.d.ts +87 -0
  103. package/dist/types/signalr.d.ts.map +1 -0
  104. package/dist/types/signalr.js +3 -0
  105. package/dist/types/signalr.js.map +1 -0
  106. package/dist/utils/TypedEventEmitter.d.ts +32 -0
  107. package/dist/utils/TypedEventEmitter.d.ts.map +1 -0
  108. package/dist/utils/TypedEventEmitter.js +60 -0
  109. package/dist/utils/TypedEventEmitter.js.map +1 -0
  110. package/package.json +40 -0
@@ -0,0 +1,182 @@
1
+ // realtime/ChatHubClient.ts — Manages ChatHub SignalR connection
2
+ import { HubConnection, HubConnectionBuilder, HubConnectionState, LogLevel, } from '@microsoft/signalr';
3
+ import { TypedEventEmitter as EventEmitter } from '../utils/TypedEventEmitter.js';
4
+ // ---------------------------------------------------------------------------
5
+ // ChatHubClient
6
+ // ---------------------------------------------------------------------------
7
+ /**
8
+ * Typed client for the ChatHub (`/hubs/chat`).
9
+ *
10
+ * Responsibilities:
11
+ * - Manages HubConnection lifecycle (connect / disconnect / auto-reconnect)
12
+ * - Exposes typed Hub methods (Client → Server)
13
+ * - Exposes typed event subscription (Server → Client)
14
+ * - Tracks joined conversations so ReconnectionManager can re-join on reconnect
15
+ */
16
+ export class ChatHubClient {
17
+ constructor(options) {
18
+ this.connection = null;
19
+ /** Set of conversationIds that have been joined in the current session */
20
+ this.joinedConversations = new Set();
21
+ this.options = options;
22
+ this.emitter = new EventEmitter();
23
+ }
24
+ // ---------------------------------------------------------------------------
25
+ // Connection state
26
+ // ---------------------------------------------------------------------------
27
+ get state() {
28
+ return this.connection?.state ?? HubConnectionState.Disconnected;
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Connection management
32
+ // ---------------------------------------------------------------------------
33
+ /** Build a new HubConnection using the latest token from tokenProvider */
34
+ buildConnection() {
35
+ const { hubUrl, tokenProvider, logLevel = LogLevel.Warning } = this.options;
36
+ return new HubConnectionBuilder()
37
+ .withUrl(hubUrl, {
38
+ accessTokenFactory: () => tokenProvider() ?? '',
39
+ })
40
+ .withAutomaticReconnect()
41
+ .configureLogging(logLevel)
42
+ .build();
43
+ }
44
+ /** Attach SignalR server→client event handlers to the connection */
45
+ attachHandlers(conn) {
46
+ // Message events
47
+ conn.on('MessageReceived', (msg) => this.emitter.emit('messageReceived', msg));
48
+ conn.on('MessageUpdated', (dto) => this.emitter.emit('messageUpdated', dto));
49
+ conn.on('MessageDeleted', (dto) => this.emitter.emit('messageDeleted', dto));
50
+ conn.on('MessageRecovered', (msg) => this.emitter.emit('messageRecovered', msg));
51
+ // Reaction events
52
+ conn.on('ReactionAdded', (dto) => this.emitter.emit('reactionAdded', dto));
53
+ conn.on('ReactionRemoved', (dto) => this.emitter.emit('reactionRemoved', dto));
54
+ // Typing events
55
+ conn.on('TypingStarted', (dto) => this.emitter.emit('typingStarted', dto));
56
+ conn.on('TypingStopped', (dto) => this.emitter.emit('typingStopped', dto));
57
+ // Read receipt
58
+ conn.on('ReadReceiptUpdated', (dto) => this.emitter.emit('readReceiptUpdated', dto));
59
+ // Streaming events
60
+ conn.on('StreamStarted', (dto) => this.emitter.emit('streamStarted', dto));
61
+ conn.on('StreamStatusUpdated', (dto) => this.emitter.emit('streamStatusUpdated', dto));
62
+ conn.on('StreamChunkReceived', (dto) => this.emitter.emit('streamChunkReceived', dto));
63
+ conn.on('StreamCompleted', (dto) => this.emitter.emit('streamCompleted', dto));
64
+ conn.on('StreamAborted', (dto) => this.emitter.emit('streamAborted', dto));
65
+ // Hub errors (server→client)
66
+ conn.on('Error', (error) => this.emitter.emit('error', error));
67
+ // Connection lifecycle
68
+ conn.onreconnecting((err) => this.emitter.emit('reconnecting', err));
69
+ conn.onreconnected((connectionId) => {
70
+ // Re-join all previously joined conversations after reconnect
71
+ this.rejoinConversations().catch(() => {
72
+ // Best-effort — errors will be surfaced via error events
73
+ });
74
+ this.emitter.emit('reconnected', connectionId);
75
+ });
76
+ conn.onclose((err) => this.emitter.emit('disconnected', err));
77
+ }
78
+ /**
79
+ * Connect to ChatHub. If already connected, resolves immediately.
80
+ * Builds a fresh connection using the current token.
81
+ */
82
+ async connect() {
83
+ if (this.connection && this.connection.state === HubConnectionState.Connected) {
84
+ return;
85
+ }
86
+ // Disconnect existing stale connection cleanly
87
+ if (this.connection) {
88
+ await this.safeStop();
89
+ }
90
+ const conn = this.buildConnection();
91
+ this.attachHandlers(conn);
92
+ this.connection = conn;
93
+ await conn.start();
94
+ }
95
+ /**
96
+ * Disconnect from ChatHub and clear joined conversation tracking.
97
+ */
98
+ async disconnect() {
99
+ this.joinedConversations.clear();
100
+ await this.safeStop();
101
+ this.connection = null;
102
+ }
103
+ async safeStop() {
104
+ try {
105
+ await this.connection?.stop();
106
+ }
107
+ catch {
108
+ // Ignore errors during stop
109
+ }
110
+ }
111
+ /**
112
+ * Re-join all tracked conversations. Called internally after reconnect.
113
+ * Called by ReconnectionManager as well.
114
+ */
115
+ async rejoinConversations() {
116
+ const ids = Array.from(this.joinedConversations);
117
+ for (const id of ids) {
118
+ try {
119
+ await this.invokeHub('JoinConversation', id);
120
+ }
121
+ catch {
122
+ // Best-effort
123
+ }
124
+ }
125
+ }
126
+ // ---------------------------------------------------------------------------
127
+ // Hub Methods (Client → Server)
128
+ // ---------------------------------------------------------------------------
129
+ async invokeHub(method, ...args) {
130
+ if (!this.connection || this.connection.state !== HubConnectionState.Connected) {
131
+ throw new Error(`ChatHub is not connected (state: ${this.connection?.state ?? 'null'})`);
132
+ }
133
+ return this.connection.invoke(method, ...args);
134
+ }
135
+ async joinConversation(conversationId) {
136
+ await this.invokeHub('JoinConversation', conversationId);
137
+ this.joinedConversations.add(conversationId);
138
+ }
139
+ async leaveConversation(conversationId) {
140
+ await this.invokeHub('LeaveConversation', conversationId);
141
+ this.joinedConversations.delete(conversationId);
142
+ }
143
+ async startTyping(conversationId) {
144
+ await this.invokeHub('StartTyping', conversationId);
145
+ }
146
+ async stopTyping(conversationId) {
147
+ await this.invokeHub('StopTyping', conversationId);
148
+ }
149
+ async markAsRead(conversationId, messageId) {
150
+ return this.invokeHub('MarkAsRead', conversationId, messageId);
151
+ }
152
+ async sendMessage(request) {
153
+ return this.invokeHub('SendMessage', request);
154
+ }
155
+ async editMessage(request) {
156
+ return this.invokeHub('EditMessage', request);
157
+ }
158
+ /** Server expects an object { messageId }, not a bare string */
159
+ async deleteMessage(request) {
160
+ return this.invokeHub('DeleteMessage', request);
161
+ }
162
+ /** Server expects an object { messageId }, not a bare string */
163
+ async recoverMessage(request) {
164
+ return this.invokeHub('RecoverMessage', request);
165
+ }
166
+ async addReaction(request) {
167
+ return this.invokeHub('AddReaction', request);
168
+ }
169
+ async removeReaction(request) {
170
+ return this.invokeHub('RemoveReaction', request);
171
+ }
172
+ // ---------------------------------------------------------------------------
173
+ // Event subscription (Server → Client)
174
+ // ---------------------------------------------------------------------------
175
+ on(event, handler) {
176
+ return this.emitter.on(event, handler);
177
+ }
178
+ off(event, handler) {
179
+ this.emitter.off(event, handler);
180
+ }
181
+ }
182
+ //# sourceMappingURL=ChatHubClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ChatHubClient.js","sourceRoot":"","sources":["../../src/realtime/ChatHubClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ import { HubConnectionState, LogLevel } from '@microsoft/signalr';
2
+ import type { Unsubscribe } from '../utils/TypedEventEmitter.js';
3
+ import type { PresenceStateItem, PresenceChangedDto, NewMessageNotificationDto, MentionedNotificationDto, UnreadCountChangedDto, ConversationCreatedDto, ConversationUpdatedDto, ParticipantJoinedDto, ParticipantLeftDto, ConversationPinnedDto, ConversationUnpinnedDto, ReadStatusChangedDto } from '../types/notification-events.js';
4
+ import type { HubErrorDto } from '../types/signalr.js';
5
+ export interface NotificationHubEventMap {
6
+ presenceState: PresenceStateItem[];
7
+ presenceChanged: PresenceChangedDto;
8
+ newMessageNotification: NewMessageNotificationDto;
9
+ mentionedNotification: MentionedNotificationDto;
10
+ unreadCountChanged: UnreadCountChangedDto;
11
+ conversationCreated: ConversationCreatedDto;
12
+ conversationUpdated: ConversationUpdatedDto;
13
+ participantJoined: ParticipantJoinedDto;
14
+ participantLeft: ParticipantLeftDto;
15
+ conversationPinned: ConversationPinnedDto;
16
+ conversationUnpinned: ConversationUnpinnedDto;
17
+ readStatusChanged: ReadStatusChangedDto;
18
+ error: HubErrorDto;
19
+ reconnecting: Error | undefined;
20
+ reconnected: string | undefined;
21
+ disconnected: Error | undefined;
22
+ }
23
+ export interface NotificationHubClientOptions {
24
+ /** Full URL of the NotificationHub endpoint, e.g. "https://api.example.com/hubs/notifications" */
25
+ hubUrl: string;
26
+ /** Returns the current JWT token to use as access_token query param */
27
+ tokenProvider: () => string | null;
28
+ /** Minimum log level for SignalR internal logging (default: Warning) */
29
+ logLevel?: LogLevel;
30
+ }
31
+ /**
32
+ * Typed client for the NotificationHub (`/hubs/notifications`).
33
+ *
34
+ * Responsibilities:
35
+ * - Manages HubConnection lifecycle (connect / disconnect / auto-reconnect)
36
+ * - Exposes typed Hub methods: subscribeToPresence / unsubscribeFromPresence
37
+ * - Exposes typed event subscription (Server → Client)
38
+ * - Tracks subscribed presence participants for re-subscribe on reconnect
39
+ */
40
+ export declare class NotificationHubClient {
41
+ private readonly options;
42
+ private connection;
43
+ private readonly emitter;
44
+ /** Set of participantIds currently subscribed to presence updates */
45
+ readonly subscribedPresenceIds: Set<string>;
46
+ constructor(options: NotificationHubClientOptions);
47
+ get state(): HubConnectionState;
48
+ /** Build a new HubConnection using the latest token from tokenProvider */
49
+ private buildConnection;
50
+ /** Attach SignalR server→client event handlers to the connection */
51
+ private attachHandlers;
52
+ /**
53
+ * Connect to NotificationHub. If already connected, resolves immediately.
54
+ * Connecting automatically marks the user as online on the server.
55
+ */
56
+ connect(): Promise<void>;
57
+ /**
58
+ * Disconnect from NotificationHub. Automatically marks user as offline (server-side).
59
+ */
60
+ disconnect(): Promise<void>;
61
+ private safeStop;
62
+ /**
63
+ * Re-subscribe to all tracked presence participant IDs.
64
+ * Called internally after reconnect. Also callable by ReconnectionManager.
65
+ */
66
+ resubscribePresence(): Promise<void>;
67
+ private invokeHub;
68
+ subscribeToPresence(participantIds: string[]): Promise<void>;
69
+ unsubscribeFromPresence(participantIds: string[]): Promise<void>;
70
+ on<K extends keyof NotificationHubEventMap>(event: K, handler: (payload: NotificationHubEventMap[K]) => void): Unsubscribe;
71
+ off<K extends keyof NotificationHubEventMap>(event: K, handler: (payload: NotificationHubEventMap[K]) => void): void;
72
+ }
73
+ //# sourceMappingURL=NotificationHubClient.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationHubClient.d.ts","sourceRoot":"","sources":["../../src/realtime/NotificationHubClient.ts"],"names":[],"mappings":"AAEA,OAAO,EAGL,kBAAkB,EAClB,QAAQ,EACT,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAqB,WAAW,EAAE,MAAM,+BAA+B,CAAC;AAEpF,OAAO,KAAK,EACV,iBAAiB,EACjB,kBAAkB,EAClB,yBAAyB,EACzB,wBAAwB,EACxB,qBAAqB,EACrB,sBAAsB,EACtB,sBAAsB,EACtB,oBAAoB,EACpB,kBAAkB,EAClB,qBAAqB,EACrB,uBAAuB,EACvB,oBAAoB,EACrB,MAAM,iCAAiC,CAAC;AACzC,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAMvD,MAAM,WAAW,uBAAuB;IAEtC,aAAa,EAAE,iBAAiB,EAAE,CAAC;IACnC,eAAe,EAAE,kBAAkB,CAAC;IAGpC,sBAAsB,EAAE,yBAAyB,CAAC;IAClD,qBAAqB,EAAE,wBAAwB,CAAC;IAChD,kBAAkB,EAAE,qBAAqB,CAAC;IAG1C,mBAAmB,EAAE,sBAAsB,CAAC;IAC5C,mBAAmB,EAAE,sBAAsB,CAAC;IAC5C,iBAAiB,EAAE,oBAAoB,CAAC;IACxC,eAAe,EAAE,kBAAkB,CAAC;IACpC,kBAAkB,EAAE,qBAAqB,CAAC;IAC1C,oBAAoB,EAAE,uBAAuB,CAAC;IAC9C,iBAAiB,EAAE,oBAAoB,CAAC;IAGxC,KAAK,EAAE,WAAW,CAAC;IAGnB,YAAY,EAAE,KAAK,GAAG,SAAS,CAAC;IAChC,WAAW,EAAE,MAAM,GAAG,SAAS,CAAC;IAChC,YAAY,EAAE,KAAK,GAAG,SAAS,CAAC;CACjC;AAMD,MAAM,WAAW,4BAA4B;IAC3C,kGAAkG;IAClG,MAAM,EAAE,MAAM,CAAC;IACf,uEAAuE;IACvE,aAAa,EAAE,MAAM,MAAM,GAAG,IAAI,CAAC;IACnC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,QAAQ,CAAC;CACrB;AAMD;;;;;;;;GAQG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA+B;IACvD,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6C;IAErE,qEAAqE;IACrE,QAAQ,CAAC,qBAAqB,cAAqB;gBAEvC,OAAO,EAAE,4BAA4B;IASjD,IAAI,KAAK,IAAI,kBAAkB,CAE9B;IAMD,0EAA0E;IAC1E,OAAO,CAAC,eAAe;IAWvB,oEAAoE;IACpE,OAAO,CAAC,cAAc;IA4CtB;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB9B;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;YAMnB,QAAQ;IAQtB;;;OAGG;IACG,mBAAmB,IAAI,OAAO,CAAC,IAAI,CAAC;YAiB5B,SAAS;IASjB,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAU5D,uBAAuB,CAAC,cAAc,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IActE,EAAE,CAAC,CAAC,SAAS,MAAM,uBAAuB,EACxC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,KAAK,IAAI,GACrD,WAAW;IAId,GAAG,CAAC,CAAC,SAAS,MAAM,uBAAuB,EACzC,KAAK,EAAE,CAAC,EACR,OAAO,EAAE,CAAC,OAAO,EAAE,uBAAuB,CAAC,CAAC,CAAC,KAAK,IAAI,GACrD,IAAI;CAGR"}
@@ -0,0 +1,162 @@
1
+ // realtime/NotificationHubClient.ts — Manages NotificationHub SignalR connection
2
+ import { HubConnection, HubConnectionBuilder, HubConnectionState, LogLevel, } from '@microsoft/signalr';
3
+ import { TypedEventEmitter as EventEmitter } from '../utils/TypedEventEmitter.js';
4
+ // ---------------------------------------------------------------------------
5
+ // NotificationHubClient
6
+ // ---------------------------------------------------------------------------
7
+ /**
8
+ * Typed client for the NotificationHub (`/hubs/notifications`).
9
+ *
10
+ * Responsibilities:
11
+ * - Manages HubConnection lifecycle (connect / disconnect / auto-reconnect)
12
+ * - Exposes typed Hub methods: subscribeToPresence / unsubscribeFromPresence
13
+ * - Exposes typed event subscription (Server → Client)
14
+ * - Tracks subscribed presence participants for re-subscribe on reconnect
15
+ */
16
+ export class NotificationHubClient {
17
+ constructor(options) {
18
+ this.connection = null;
19
+ /** Set of participantIds currently subscribed to presence updates */
20
+ this.subscribedPresenceIds = new Set();
21
+ this.options = options;
22
+ this.emitter = new EventEmitter();
23
+ }
24
+ // ---------------------------------------------------------------------------
25
+ // Connection state
26
+ // ---------------------------------------------------------------------------
27
+ get state() {
28
+ return this.connection?.state ?? HubConnectionState.Disconnected;
29
+ }
30
+ // ---------------------------------------------------------------------------
31
+ // Connection management
32
+ // ---------------------------------------------------------------------------
33
+ /** Build a new HubConnection using the latest token from tokenProvider */
34
+ buildConnection() {
35
+ const { hubUrl, tokenProvider, logLevel = LogLevel.Warning } = this.options;
36
+ return new HubConnectionBuilder()
37
+ .withUrl(hubUrl, {
38
+ accessTokenFactory: () => tokenProvider() ?? '',
39
+ })
40
+ .withAutomaticReconnect()
41
+ .configureLogging(logLevel)
42
+ .build();
43
+ }
44
+ /** Attach SignalR server→client event handlers to the connection */
45
+ attachHandlers(conn) {
46
+ // Presence events
47
+ conn.on('PresenceState', (states) => this.emitter.emit('presenceState', states));
48
+ conn.on('PresenceChanged', (dto) => this.emitter.emit('presenceChanged', dto));
49
+ // Message notifications
50
+ conn.on('NewMessageNotification', (dto) => this.emitter.emit('newMessageNotification', dto));
51
+ conn.on('MentionedNotification', (dto) => this.emitter.emit('mentionedNotification', dto));
52
+ conn.on('UnreadCountChanged', (dto) => this.emitter.emit('unreadCountChanged', dto));
53
+ // Conversation events
54
+ conn.on('ConversationCreated', (dto) => this.emitter.emit('conversationCreated', dto));
55
+ conn.on('ConversationUpdated', (dto) => this.emitter.emit('conversationUpdated', dto));
56
+ conn.on('ParticipantJoined', (dto) => this.emitter.emit('participantJoined', dto));
57
+ conn.on('ParticipantLeft', (dto) => this.emitter.emit('participantLeft', dto));
58
+ conn.on('ConversationPinned', (dto) => this.emitter.emit('conversationPinned', dto));
59
+ conn.on('ConversationUnpinned', (dto) => this.emitter.emit('conversationUnpinned', dto));
60
+ conn.on('ReadStatusChanged', (dto) => this.emitter.emit('readStatusChanged', dto));
61
+ // Hub errors (server→client)
62
+ conn.on('Error', (error) => this.emitter.emit('error', error));
63
+ // Connection lifecycle
64
+ conn.onreconnecting((err) => this.emitter.emit('reconnecting', err));
65
+ conn.onreconnected((connectionId) => {
66
+ // Re-subscribe presence for all tracked participants
67
+ this.resubscribePresence().catch(() => {
68
+ // Best-effort
69
+ });
70
+ this.emitter.emit('reconnected', connectionId);
71
+ });
72
+ conn.onclose((err) => this.emitter.emit('disconnected', err));
73
+ }
74
+ /**
75
+ * Connect to NotificationHub. If already connected, resolves immediately.
76
+ * Connecting automatically marks the user as online on the server.
77
+ */
78
+ async connect() {
79
+ if (this.connection && this.connection.state === HubConnectionState.Connected) {
80
+ return;
81
+ }
82
+ // Disconnect existing stale connection cleanly
83
+ if (this.connection) {
84
+ await this.safeStop();
85
+ }
86
+ const conn = this.buildConnection();
87
+ this.attachHandlers(conn);
88
+ this.connection = conn;
89
+ await conn.start();
90
+ }
91
+ /**
92
+ * Disconnect from NotificationHub. Automatically marks user as offline (server-side).
93
+ */
94
+ async disconnect() {
95
+ this.subscribedPresenceIds.clear();
96
+ await this.safeStop();
97
+ this.connection = null;
98
+ }
99
+ async safeStop() {
100
+ try {
101
+ await this.connection?.stop();
102
+ }
103
+ catch {
104
+ // Ignore errors during stop
105
+ }
106
+ }
107
+ /**
108
+ * Re-subscribe to all tracked presence participant IDs.
109
+ * Called internally after reconnect. Also callable by ReconnectionManager.
110
+ */
111
+ async resubscribePresence() {
112
+ const ids = Array.from(this.subscribedPresenceIds);
113
+ if (ids.length === 0)
114
+ return;
115
+ // Batch into chunks of 200 to respect the server limit
116
+ for (let i = 0; i < ids.length; i += 200) {
117
+ try {
118
+ await this.invokeHub('SubscribeToPresence', ids.slice(i, i + 200));
119
+ }
120
+ catch {
121
+ // Best-effort
122
+ }
123
+ }
124
+ }
125
+ // ---------------------------------------------------------------------------
126
+ // Hub Methods (Client → Server)
127
+ // ---------------------------------------------------------------------------
128
+ async invokeHub(method, ...args) {
129
+ if (!this.connection || this.connection.state !== HubConnectionState.Connected) {
130
+ throw new Error(`NotificationHub is not connected (state: ${this.connection?.state ?? 'null'})`);
131
+ }
132
+ return this.connection.invoke(method, ...args);
133
+ }
134
+ async subscribeToPresence(participantIds) {
135
+ if (participantIds.length > 200) {
136
+ throw new Error('Cannot subscribe to more than 200 participants at once');
137
+ }
138
+ await this.invokeHub('SubscribeToPresence', participantIds);
139
+ for (const id of participantIds) {
140
+ this.subscribedPresenceIds.add(id);
141
+ }
142
+ }
143
+ async unsubscribeFromPresence(participantIds) {
144
+ if (participantIds.length > 200) {
145
+ throw new Error('Cannot unsubscribe from more than 200 participants at once');
146
+ }
147
+ await this.invokeHub('UnsubscribeFromPresence', participantIds);
148
+ for (const id of participantIds) {
149
+ this.subscribedPresenceIds.delete(id);
150
+ }
151
+ }
152
+ // ---------------------------------------------------------------------------
153
+ // Event subscription (Server → Client)
154
+ // ---------------------------------------------------------------------------
155
+ on(event, handler) {
156
+ return this.emitter.on(event, handler);
157
+ }
158
+ off(event, handler) {
159
+ this.emitter.off(event, handler);
160
+ }
161
+ }
162
+ //# sourceMappingURL=NotificationHubClient.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NotificationHubClient.js","sourceRoot":"","sources":["../../src/realtime/NotificationHubClient.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,65 @@
1
+ import type { ChatHubClient } from './ChatHubClient.js';
2
+ import type { NotificationHubClient } from './NotificationHubClient.js';
3
+ export interface ReconnectionManagerOptions {
4
+ /** ChatHub client to manage */
5
+ chatHub: ChatHubClient;
6
+ /** NotificationHub client to manage */
7
+ notificationHub: NotificationHubClient;
8
+ /**
9
+ * Called when reconnect is rejected with 401 (token likely expired).
10
+ * Should return a fresh token, or null to cancel reconnect attempts.
11
+ *
12
+ * The returned token should be committed to the application's token store
13
+ * so that the shared tokenProvider reflects the new value immediately —
14
+ * the SDK will then pick it up on the next connect() call.
15
+ */
16
+ onTokenExpired?: () => Promise<string | null>;
17
+ }
18
+ /**
19
+ * ReconnectionManager coordinates reconnection for both ChatHub and NotificationHub.
20
+ *
21
+ * Responsibilities:
22
+ * 1. Listens for `disconnected` events from both hubs and attempts to reconnect.
23
+ * 2. After ChatHub reconnects, re-joins all previously joined conversations.
24
+ * 3. After NotificationHub reconnects, re-subscribes to all previously tracked presences.
25
+ * 4. On 401-style reconnect failures, calls `onTokenExpired` to obtain a fresh token,
26
+ * then retries the connection once.
27
+ *
28
+ * Note on token propagation:
29
+ * - SignalR tokens are provided at connect-time via accessTokenFactory.
30
+ * - Both hub clients call tokenProvider() on every new connect(), so updating
31
+ * the token in the provider is sufficient before calling connect() again.
32
+ * - Existing active connections cannot receive a new token mid-session.
33
+ */
34
+ export declare class ReconnectionManager {
35
+ private readonly chatHub;
36
+ private readonly notificationHub;
37
+ private readonly onTokenExpired?;
38
+ private unsubscribeChatDisconnected?;
39
+ private unsubscribeNotificationDisconnected?;
40
+ /** Whether the manager is actively managing reconnections */
41
+ private active;
42
+ constructor(options: ReconnectionManagerOptions);
43
+ /**
44
+ * Start managing reconnections. Attaches `disconnected` listeners to both hubs.
45
+ * Safe to call multiple times — subsequent calls are no-ops.
46
+ */
47
+ start(): void;
48
+ /**
49
+ * Stop managing reconnections and detach all listeners.
50
+ */
51
+ stop(): void;
52
+ private handleChatDisconnected;
53
+ private handleNotificationDisconnected;
54
+ /**
55
+ * Determine if the disconnect error is likely due to token expiry (HTTP 401).
56
+ * SignalR surfaces this as an error message containing "401" or "Unauthorized".
57
+ */
58
+ private isTokenExpiredError;
59
+ /**
60
+ * Attempt to reconnect a hub with exponential backoff (max 3 retries).
61
+ * After a successful connect, calls `afterConnect` to restore subscriptions.
62
+ */
63
+ private reconnectHub;
64
+ }
65
+ //# sourceMappingURL=ReconnectionManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReconnectionManager.d.ts","sourceRoot":"","sources":["../../src/realtime/ReconnectionManager.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACxD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAExE,MAAM,WAAW,0BAA0B;IACzC,+BAA+B;IAC/B,OAAO,EAAE,aAAa,CAAC;IACvB,uCAAuC;IACvC,eAAe,EAAE,qBAAqB,CAAC;IACvC;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;CAC/C;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgB;IACxC,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAwB;IACxD,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC,CAA+B;IAE/D,OAAO,CAAC,2BAA2B,CAAC,CAAa;IACjD,OAAO,CAAC,mCAAmC,CAAC,CAAa;IAEzD,6DAA6D;IAC7D,OAAO,CAAC,MAAM,CAAS;gBAEX,OAAO,EAAE,0BAA0B;IAM/C;;;OAGG;IACH,KAAK,IAAI,IAAI;IAeb;;OAEG;IACH,IAAI,IAAI,IAAI;YAYE,sBAAsB;YAiBtB,8BAA8B;IAmB5C;;;OAGG;IACH,OAAO,CAAC,mBAAmB;IAM3B;;;OAGG;YACW,YAAY;CA2B3B"}
@@ -0,0 +1,118 @@
1
+ // realtime/ReconnectionManager.ts — Coordinates auto-reconnect for both hubs
2
+ import { HubConnectionState } from '@microsoft/signalr';
3
+ /**
4
+ * ReconnectionManager coordinates reconnection for both ChatHub and NotificationHub.
5
+ *
6
+ * Responsibilities:
7
+ * 1. Listens for `disconnected` events from both hubs and attempts to reconnect.
8
+ * 2. After ChatHub reconnects, re-joins all previously joined conversations.
9
+ * 3. After NotificationHub reconnects, re-subscribes to all previously tracked presences.
10
+ * 4. On 401-style reconnect failures, calls `onTokenExpired` to obtain a fresh token,
11
+ * then retries the connection once.
12
+ *
13
+ * Note on token propagation:
14
+ * - SignalR tokens are provided at connect-time via accessTokenFactory.
15
+ * - Both hub clients call tokenProvider() on every new connect(), so updating
16
+ * the token in the provider is sufficient before calling connect() again.
17
+ * - Existing active connections cannot receive a new token mid-session.
18
+ */
19
+ export class ReconnectionManager {
20
+ constructor(options) {
21
+ /** Whether the manager is actively managing reconnections */
22
+ this.active = false;
23
+ this.chatHub = options.chatHub;
24
+ this.notificationHub = options.notificationHub;
25
+ this.onTokenExpired = options.onTokenExpired;
26
+ }
27
+ /**
28
+ * Start managing reconnections. Attaches `disconnected` listeners to both hubs.
29
+ * Safe to call multiple times — subsequent calls are no-ops.
30
+ */
31
+ start() {
32
+ if (this.active)
33
+ return;
34
+ this.active = true;
35
+ this.unsubscribeChatDisconnected = this.chatHub.on('disconnected', (err) => void this.handleChatDisconnected(err));
36
+ this.unsubscribeNotificationDisconnected = this.notificationHub.on('disconnected', (err) => void this.handleNotificationDisconnected(err));
37
+ }
38
+ /**
39
+ * Stop managing reconnections and detach all listeners.
40
+ */
41
+ stop() {
42
+ this.active = false;
43
+ this.unsubscribeChatDisconnected?.();
44
+ this.unsubscribeNotificationDisconnected?.();
45
+ this.unsubscribeChatDisconnected = undefined;
46
+ this.unsubscribeNotificationDisconnected = undefined;
47
+ }
48
+ // ---------------------------------------------------------------------------
49
+ // Disconnect handlers
50
+ // ---------------------------------------------------------------------------
51
+ async handleChatDisconnected(err) {
52
+ if (!this.active)
53
+ return;
54
+ // withAutomaticReconnect already tried — this is a terminal disconnect.
55
+ // Attempt one more manual reconnect after optional token refresh.
56
+ if (this.isTokenExpiredError(err)) {
57
+ const newToken = await this.onTokenExpired?.();
58
+ if (!newToken)
59
+ return; // Client chose to abandon reconnect
60
+ }
61
+ await this.reconnectHub(() => this.chatHub.state, () => this.chatHub.connect(), () => this.chatHub.rejoinConversations());
62
+ }
63
+ async handleNotificationDisconnected(err) {
64
+ if (!this.active)
65
+ return;
66
+ if (this.isTokenExpiredError(err)) {
67
+ const newToken = await this.onTokenExpired?.();
68
+ if (!newToken)
69
+ return;
70
+ }
71
+ await this.reconnectHub(() => this.notificationHub.state, () => this.notificationHub.connect(), () => this.notificationHub.resubscribePresence());
72
+ }
73
+ // ---------------------------------------------------------------------------
74
+ // Helpers
75
+ // ---------------------------------------------------------------------------
76
+ /**
77
+ * Determine if the disconnect error is likely due to token expiry (HTTP 401).
78
+ * SignalR surfaces this as an error message containing "401" or "Unauthorized".
79
+ */
80
+ isTokenExpiredError(err) {
81
+ if (!err)
82
+ return false;
83
+ const msg = err.message ?? '';
84
+ return msg.includes('401') || msg.toLowerCase().includes('unauthorized');
85
+ }
86
+ /**
87
+ * Attempt to reconnect a hub with exponential backoff (max 3 retries).
88
+ * After a successful connect, calls `afterConnect` to restore subscriptions.
89
+ */
90
+ async reconnectHub(getState, connect, afterConnect) {
91
+ if (getState() === HubConnectionState.Connected)
92
+ return;
93
+ const delays = [2000, 5000, 10000]; // ms — 2s, 5s, 10s
94
+ for (const delay of delays) {
95
+ if (!this.active)
96
+ return;
97
+ await sleep(delay);
98
+ if (getState() === HubConnectionState.Connected)
99
+ return;
100
+ try {
101
+ await connect();
102
+ // Successful connect — restore state
103
+ await afterConnect().catch(() => {
104
+ // Best-effort: don't fail reconnect if re-subscribe fails
105
+ });
106
+ return;
107
+ }
108
+ catch {
109
+ // Retry after next delay
110
+ }
111
+ }
112
+ // All retries exhausted — emit is handled by the hub's onclose handler
113
+ }
114
+ }
115
+ function sleep(ms) {
116
+ return new Promise((resolve) => setTimeout(resolve, ms));
117
+ }
118
+ //# sourceMappingURL=ReconnectionManager.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReconnectionManager.js","sourceRoot":"","sources":["../../src/realtime/ReconnectionManager.ts"],"names":[],"mappings":"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"}