@osise/api-client 0.0.9 → 0.0.11

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,238 @@
1
+ "use strict";
2
+ /**
3
+ * Real-time client for Osise SignalR hubs
4
+ * Provides typed connections to ChatHub and NotificationHub
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.OsiseRealTimeClient = void 0;
8
+ const signalr_1 = require("@microsoft/signalr");
9
+ /**
10
+ * Manages SignalR connections to the Osise real-time hubs.
11
+ *
12
+ * Messages flow through REST API (not through SignalR). The hubs push
13
+ * server-side events to connected clients in real-time.
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * const realtime = new OsiseRealTimeClient({
18
+ * baseUrl: 'https://api.osise.com',
19
+ * accessTokenFactory: () => client.getAccessToken(),
20
+ * });
21
+ *
22
+ * realtime.chat.onMessage = (msg) => console.log('New message:', msg);
23
+ * realtime.notifications.onNotification = (n) => console.log('Notification:', n);
24
+ *
25
+ * await realtime.connect();
26
+ * ```
27
+ */
28
+ class OsiseRealTimeClient {
29
+ constructor(config) {
30
+ this.chatConnection = null;
31
+ this.notificationConnection = null;
32
+ /** Chat hub event handlers */
33
+ this.chat = {};
34
+ /** Notification hub event handlers */
35
+ this.notifications = {};
36
+ /** Connection lifecycle event handlers */
37
+ this.connection = {};
38
+ /** Current chat hub connection state */
39
+ this.chatState = 'disconnected';
40
+ /** Current notification hub connection state */
41
+ this.notificationState = 'disconnected';
42
+ this.config = {
43
+ autoReconnect: true,
44
+ reconnectDelays: [0, 2000, 5000, 10000, 30000],
45
+ debug: false,
46
+ ...config,
47
+ };
48
+ }
49
+ // ==================== Connection Management ====================
50
+ /**
51
+ * Connect to both chat and notification hubs.
52
+ * Call this after the user is authenticated.
53
+ */
54
+ async connect() {
55
+ await Promise.all([
56
+ this.connectChat(),
57
+ this.connectNotifications(),
58
+ ]);
59
+ }
60
+ /**
61
+ * Disconnect from both hubs.
62
+ * Call this on logout or when real-time features are no longer needed.
63
+ */
64
+ async disconnect() {
65
+ await Promise.all([
66
+ this.disconnectChat(),
67
+ this.disconnectNotifications(),
68
+ ]);
69
+ }
70
+ /**
71
+ * Connect to the chat hub only.
72
+ */
73
+ async connectChat() {
74
+ if (this.chatConnection?.state === signalr_1.HubConnectionState.Connected)
75
+ return;
76
+ this.chatConnection = this.buildConnection('/hubs/chat');
77
+ this.registerChatHandlers(this.chatConnection);
78
+ this.registerConnectionHandlers(this.chatConnection, 'chat');
79
+ this.chatState = 'connecting';
80
+ await this.chatConnection.start();
81
+ this.chatState = 'connected';
82
+ }
83
+ /**
84
+ * Connect to the notification hub only.
85
+ */
86
+ async connectNotifications() {
87
+ if (this.notificationConnection?.state === signalr_1.HubConnectionState.Connected)
88
+ return;
89
+ this.notificationConnection = this.buildConnection('/hubs/notifications');
90
+ this.registerNotificationHandlers(this.notificationConnection);
91
+ this.registerConnectionHandlers(this.notificationConnection, 'notifications');
92
+ this.notificationState = 'connecting';
93
+ await this.notificationConnection.start();
94
+ this.notificationState = 'connected';
95
+ }
96
+ /**
97
+ * Disconnect from the chat hub.
98
+ */
99
+ async disconnectChat() {
100
+ if (this.chatConnection) {
101
+ await this.chatConnection.stop();
102
+ this.chatConnection = null;
103
+ this.chatState = 'disconnected';
104
+ }
105
+ }
106
+ /**
107
+ * Disconnect from the notification hub.
108
+ */
109
+ async disconnectNotifications() {
110
+ if (this.notificationConnection) {
111
+ await this.notificationConnection.stop();
112
+ this.notificationConnection = null;
113
+ this.notificationState = 'disconnected';
114
+ }
115
+ }
116
+ // ==================== Chat Hub Client → Server Methods ====================
117
+ /**
118
+ * Send a typing indicator to another user.
119
+ *
120
+ * @param conversationId - The conversation where typing is happening
121
+ * @param recipientUserId - The other participant's user ID
122
+ * @param isTyping - Whether the user is currently typing
123
+ */
124
+ async sendTypingIndicator(conversationId, recipientUserId, isTyping) {
125
+ this.ensureChatConnected();
126
+ await this.chatConnection.invoke('SendTypingIndicator', conversationId, recipientUserId, isTyping);
127
+ }
128
+ /**
129
+ * Join a conversation group to receive targeted events.
130
+ * Call this when the user opens a conversation.
131
+ *
132
+ * @param conversationId - The conversation to join
133
+ */
134
+ async joinConversation(conversationId) {
135
+ this.ensureChatConnected();
136
+ await this.chatConnection.invoke('JoinConversation', conversationId);
137
+ }
138
+ /**
139
+ * Leave a conversation group.
140
+ * Call this when the user navigates away from a conversation.
141
+ *
142
+ * @param conversationId - The conversation to leave
143
+ */
144
+ async leaveConversation(conversationId) {
145
+ this.ensureChatConnected();
146
+ await this.chatConnection.invoke('LeaveConversation', conversationId);
147
+ }
148
+ // ==================== Notification Hub Client → Server Methods ====================
149
+ /**
150
+ * Acknowledge receipt of a notification.
151
+ *
152
+ * @param notificationId - The notification to acknowledge
153
+ */
154
+ async acknowledgeNotification(notificationId) {
155
+ this.ensureNotificationConnected();
156
+ await this.notificationConnection.invoke('AcknowledgeNotification', notificationId);
157
+ }
158
+ // ==================== Private Helpers ====================
159
+ buildConnection(hubPath) {
160
+ const url = `${this.config.baseUrl}${hubPath}`;
161
+ const logLevel = this.config.debug ? signalr_1.LogLevel.Debug : signalr_1.LogLevel.Warning;
162
+ let builder = new signalr_1.HubConnectionBuilder()
163
+ .withUrl(url, {
164
+ accessTokenFactory: () => this.config.accessTokenFactory() ?? '',
165
+ })
166
+ .configureLogging(logLevel);
167
+ if (this.config.autoReconnect) {
168
+ builder = builder.withAutomaticReconnect(this.config.reconnectDelays);
169
+ }
170
+ return builder.build();
171
+ }
172
+ registerChatHandlers(connection) {
173
+ connection.on('ReceiveMessage', (message) => {
174
+ this.chat.onMessage?.(message);
175
+ });
176
+ connection.on('TypingIndicator', (event) => {
177
+ this.chat.onTypingIndicator?.(event);
178
+ });
179
+ connection.on('MessageDelivered', (event) => {
180
+ this.chat.onMessageDelivered?.(event);
181
+ });
182
+ connection.on('MessagesRead', (event) => {
183
+ this.chat.onMessagesRead?.(event);
184
+ });
185
+ connection.on('MessageEdited', (event) => {
186
+ this.chat.onMessageEdited?.(event);
187
+ });
188
+ connection.on('MessageDeleted', (event) => {
189
+ this.chat.onMessageDeleted?.(event);
190
+ });
191
+ connection.on('UserPresenceChanged', (event) => {
192
+ this.chat.onUserPresenceChanged?.(event);
193
+ });
194
+ }
195
+ registerNotificationHandlers(connection) {
196
+ connection.on('ReceiveNotification', (notification) => {
197
+ this.notifications.onNotification?.(notification);
198
+ });
199
+ connection.on('NotificationCountUpdated', (event) => {
200
+ this.notifications.onNotificationCountUpdated?.(event);
201
+ });
202
+ }
203
+ registerConnectionHandlers(connection, hub) {
204
+ connection.onclose((error) => {
205
+ if (hub === 'chat')
206
+ this.chatState = 'disconnected';
207
+ else
208
+ this.notificationState = 'disconnected';
209
+ this.connection.onDisconnected?.(error);
210
+ });
211
+ connection.onreconnecting((error) => {
212
+ if (hub === 'chat')
213
+ this.chatState = 'reconnecting';
214
+ else
215
+ this.notificationState = 'reconnecting';
216
+ this.connection.onReconnecting?.(error);
217
+ });
218
+ connection.onreconnected((connectionId) => {
219
+ if (hub === 'chat')
220
+ this.chatState = 'connected';
221
+ else
222
+ this.notificationState = 'connected';
223
+ this.connection.onReconnected?.(connectionId);
224
+ });
225
+ }
226
+ ensureChatConnected() {
227
+ if (this.chatConnection?.state !== signalr_1.HubConnectionState.Connected) {
228
+ throw new Error('Chat hub is not connected. Call connectChat() first.');
229
+ }
230
+ }
231
+ ensureNotificationConnected() {
232
+ if (this.notificationConnection?.state !== signalr_1.HubConnectionState.Connected) {
233
+ throw new Error('Notification hub is not connected. Call connectNotifications() first.');
234
+ }
235
+ }
236
+ }
237
+ exports.OsiseRealTimeClient = OsiseRealTimeClient;
238
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/realtime/client.ts"],"names":[],"mappings":"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"}
@@ -50,4 +50,6 @@ __exportStar(require("./communication"), exports);
50
50
  __exportStar(require("./location"), exports);
51
51
  // Paystack integration types
52
52
  __exportStar(require("./paystack"), exports);
53
+ // Real-time types (SignalR)
54
+ __exportStar(require("./realtime"), exports);
53
55
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;AAEH,QAAQ;AACR,0CAAwB;AAExB,eAAe;AACf,2CAAyB;AAEzB,aAAa;AACb,yCAAuB;AAEvB,iBAAiB;AACjB,6CAA2B;AAE3B,gBAAgB;AAChB,4CAA0B;AAE1B,cAAc;AACd,0CAAwB;AAkFxB,gBAAgB;AAChB,4CAA0B;AAE1B,0BAA0B;AAC1B,kDAAgC;AAEhC,qBAAqB;AACrB,iDAA+B;AAE/B,sBAAsB;AACtB,kDAAgC;AAEhC,iCAAiC;AACjC,wCAAsB;AAEtB,wBAAwB;AACxB,uCAAqB;AAErB,gBAAgB;AAChB,4CAA0B;AAE1B,iEAAiE;AACjE,kDAAgC;AAEhC,iBAAiB;AACjB,6CAA2B;AAE3B,6BAA6B;AAC7B,6CAA2B"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/types/index.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;;;;;;;;;;;;AAEH,QAAQ;AACR,0CAAwB;AAExB,eAAe;AACf,2CAAyB;AAEzB,aAAa;AACb,yCAAuB;AAEvB,iBAAiB;AACjB,6CAA2B;AAE3B,gBAAgB;AAChB,4CAA0B;AAE1B,cAAc;AACd,0CAAwB;AAkFxB,gBAAgB;AAChB,4CAA0B;AAE1B,0BAA0B;AAC1B,kDAAgC;AAEhC,qBAAqB;AACrB,iDAA+B;AAE/B,sBAAsB;AACtB,kDAAgC;AAEhC,iCAAiC;AACjC,wCAAsB;AAEtB,wBAAwB;AACxB,uCAAqB;AAErB,gBAAgB;AAChB,4CAA0B;AAE1B,iEAAiE;AACjE,kDAAgC;AAEhC,iBAAiB;AACjB,6CAA2B;AAE3B,6BAA6B;AAC7B,6CAA2B;AAE3B,4BAA4B;AAC5B,6CAA2B"}
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * Real-time types for SignalR communication
4
+ * Defines event payloads for chat and notification hubs
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=realtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"realtime.js","sourceRoot":"","sources":["../../../src/types/realtime.ts"],"names":[],"mappings":";AAAA;;;GAGG"}
@@ -2034,4 +2034,282 @@ export function useRateArtisanTicket() {
2034
2034
  const client = useOsiseClient();
2035
2035
  return useMutation(({ ticketId, data }) => client.artisanSupport.rateTicket(ticketId, data));
2036
2036
  }
2037
+ /**
2038
+ * Hook to access the real-time client from the Osise provider.
2039
+ *
2040
+ * @example
2041
+ * ```tsx
2042
+ * const realtime = useRealTime();
2043
+ * await realtime.connect();
2044
+ * ```
2045
+ */
2046
+ export function useRealTime() {
2047
+ const client = useOsiseClient();
2048
+ return client.realtime;
2049
+ }
2050
+ /**
2051
+ * Hook that manages the SignalR connection lifecycle.
2052
+ * Connects when the user is authenticated and disconnects on logout or unmount.
2053
+ *
2054
+ * @example
2055
+ * ```tsx
2056
+ * function App() {
2057
+ * const { chatState, notificationState } = useRealTimeConnection();
2058
+ * return <div>Chat: {chatState}, Notifications: {notificationState}</div>;
2059
+ * }
2060
+ * ```
2061
+ */
2062
+ export function useRealTimeConnection() {
2063
+ const client = useOsiseClient();
2064
+ const isAuthenticated = useIsAuthenticated();
2065
+ const realtime = client.realtime;
2066
+ const [chatState, setChatState] = useState('disconnected');
2067
+ const [notificationState, setNotificationState] = useState('disconnected');
2068
+ useEffect(() => {
2069
+ if (!isAuthenticated)
2070
+ return;
2071
+ realtime.connection.onDisconnected = () => {
2072
+ setChatState(realtime.chatState);
2073
+ setNotificationState(realtime.notificationState);
2074
+ };
2075
+ realtime.connection.onReconnecting = () => {
2076
+ setChatState(realtime.chatState);
2077
+ setNotificationState(realtime.notificationState);
2078
+ };
2079
+ realtime.connection.onReconnected = () => {
2080
+ setChatState(realtime.chatState);
2081
+ setNotificationState(realtime.notificationState);
2082
+ };
2083
+ realtime.connect().then(() => {
2084
+ setChatState(realtime.chatState);
2085
+ setNotificationState(realtime.notificationState);
2086
+ }).catch(() => {
2087
+ setChatState('disconnected');
2088
+ setNotificationState('disconnected');
2089
+ });
2090
+ return () => {
2091
+ realtime.disconnect();
2092
+ setChatState('disconnected');
2093
+ setNotificationState('disconnected');
2094
+ };
2095
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2096
+ }, [isAuthenticated]);
2097
+ return { chatState, notificationState, realtime };
2098
+ }
2099
+ /**
2100
+ * Hook that subscribes to incoming chat messages.
2101
+ *
2102
+ * @example
2103
+ * ```tsx
2104
+ * const messages = useChatMessages();
2105
+ * // messages is an array of received messages since mount
2106
+ * ```
2107
+ */
2108
+ export function useChatMessages() {
2109
+ const realtime = useRealTime();
2110
+ const [messages, setMessages] = useState([]);
2111
+ useEffect(() => {
2112
+ const prev = realtime.chat.onMessage;
2113
+ realtime.chat.onMessage = (msg) => {
2114
+ setMessages((m) => [...m, msg]);
2115
+ prev?.(msg);
2116
+ };
2117
+ return () => {
2118
+ realtime.chat.onMessage = prev;
2119
+ };
2120
+ }, [realtime]);
2121
+ const clear = useCallback(() => setMessages([]), []);
2122
+ return { messages, clear };
2123
+ }
2124
+ /**
2125
+ * Hook that tracks typing indicators for a specific conversation.
2126
+ *
2127
+ * @param conversationId - The conversation to monitor
2128
+ *
2129
+ * @example
2130
+ * ```tsx
2131
+ * const { typingUsers, sendTyping } = useTypingIndicator(conversationId, recipientId);
2132
+ * // typingUsers is a Set<string> of user IDs currently typing
2133
+ * ```
2134
+ */
2135
+ export function useTypingIndicator(conversationId, recipientUserId) {
2136
+ const realtime = useRealTime();
2137
+ const [typingUsers, setTypingUsers] = useState(new Set());
2138
+ const timersRef = useRef(new Map());
2139
+ useEffect(() => {
2140
+ const prev = realtime.chat.onTypingIndicator;
2141
+ realtime.chat.onTypingIndicator = (event) => {
2142
+ if (event.conversationId === conversationId) {
2143
+ setTypingUsers((current) => {
2144
+ const next = new Set(current);
2145
+ if (event.isTyping) {
2146
+ next.add(event.userId);
2147
+ // Auto-clear after 5s if no stop event
2148
+ const existing = timersRef.current.get(event.userId);
2149
+ if (existing)
2150
+ clearTimeout(existing);
2151
+ timersRef.current.set(event.userId, setTimeout(() => {
2152
+ setTypingUsers((c) => {
2153
+ const n = new Set(c);
2154
+ n.delete(event.userId);
2155
+ return n;
2156
+ });
2157
+ timersRef.current.delete(event.userId);
2158
+ }, 5000));
2159
+ }
2160
+ else {
2161
+ next.delete(event.userId);
2162
+ const existing = timersRef.current.get(event.userId);
2163
+ if (existing) {
2164
+ clearTimeout(existing);
2165
+ timersRef.current.delete(event.userId);
2166
+ }
2167
+ }
2168
+ return next;
2169
+ });
2170
+ }
2171
+ prev?.(event);
2172
+ };
2173
+ return () => {
2174
+ realtime.chat.onTypingIndicator = prev;
2175
+ timersRef.current.forEach((timer) => clearTimeout(timer));
2176
+ timersRef.current.clear();
2177
+ };
2178
+ }, [realtime, conversationId]);
2179
+ const sendTyping = useCallback(async (isTyping) => {
2180
+ if (recipientUserId) {
2181
+ await realtime.sendTypingIndicator(conversationId, recipientUserId, isTyping);
2182
+ }
2183
+ }, [realtime, conversationId, recipientUserId]);
2184
+ return { typingUsers, sendTyping };
2185
+ }
2186
+ /**
2187
+ * Hook that tracks online/offline presence for specific user IDs.
2188
+ *
2189
+ * @param userIds - User IDs to track presence for
2190
+ *
2191
+ * @example
2192
+ * ```tsx
2193
+ * const presence = usePresence([artisanId]);
2194
+ * // presence.get(artisanId) → { isOnline: true, lastSeenAt: '...' }
2195
+ * ```
2196
+ */
2197
+ export function usePresence(userIds) {
2198
+ const realtime = useRealTime();
2199
+ const [presence, setPresence] = useState(new Map());
2200
+ useEffect(() => {
2201
+ const prev = realtime.chat.onUserPresenceChanged;
2202
+ realtime.chat.onUserPresenceChanged = (event) => {
2203
+ if (userIds.includes(event.userId)) {
2204
+ setPresence((current) => {
2205
+ const next = new Map(current);
2206
+ next.set(event.userId, { isOnline: event.isOnline, lastSeenAt: event.lastSeenAt });
2207
+ return next;
2208
+ });
2209
+ }
2210
+ prev?.(event);
2211
+ };
2212
+ return () => {
2213
+ realtime.chat.onUserPresenceChanged = prev;
2214
+ };
2215
+ // eslint-disable-next-line react-hooks/exhaustive-deps
2216
+ }, [realtime, ...userIds]);
2217
+ return presence;
2218
+ }
2219
+ /**
2220
+ * Hook that subscribes to incoming notifications in real-time.
2221
+ *
2222
+ * @example
2223
+ * ```tsx
2224
+ * const { notifications, unreadCount } = useRealtimeNotifications();
2225
+ * ```
2226
+ */
2227
+ export function useRealtimeNotifications() {
2228
+ const realtime = useRealTime();
2229
+ const [notifications, setNotifications] = useState([]);
2230
+ const [unreadCount, setUnreadCount] = useState(null);
2231
+ useEffect(() => {
2232
+ const prevNotif = realtime.notifications.onNotification;
2233
+ const prevCount = realtime.notifications.onNotificationCountUpdated;
2234
+ realtime.notifications.onNotification = (notification) => {
2235
+ setNotifications((n) => [notification, ...n]);
2236
+ prevNotif?.(notification);
2237
+ };
2238
+ realtime.notifications.onNotificationCountUpdated = (event) => {
2239
+ setUnreadCount(event.unreadCount);
2240
+ prevCount?.(event);
2241
+ };
2242
+ return () => {
2243
+ realtime.notifications.onNotification = prevNotif;
2244
+ realtime.notifications.onNotificationCountUpdated = prevCount;
2245
+ };
2246
+ }, [realtime]);
2247
+ const clear = useCallback(() => setNotifications([]), []);
2248
+ return { notifications, unreadCount, clear };
2249
+ }
2250
+ /**
2251
+ * Hook that manages joining/leaving a conversation and provides
2252
+ * delivery and read receipt events for that conversation.
2253
+ *
2254
+ * @param conversationId - The conversation to join
2255
+ *
2256
+ * @example
2257
+ * ```tsx
2258
+ * const { deliveredIds, readMessageIds } = useConversation(conversationId);
2259
+ * ```
2260
+ */
2261
+ export function useConversation(conversationId) {
2262
+ const realtime = useRealTime();
2263
+ const [deliveredIds, setDeliveredIds] = useState(new Set());
2264
+ const [readMessageIds, setReadMessageIds] = useState(new Set());
2265
+ const [editedMessages, setEditedMessages] = useState(new Map());
2266
+ const [deletedIds, setDeletedIds] = useState(new Set());
2267
+ useEffect(() => {
2268
+ realtime.joinConversation(conversationId).catch(() => { });
2269
+ const prevDelivered = realtime.chat.onMessageDelivered;
2270
+ const prevRead = realtime.chat.onMessagesRead;
2271
+ const prevEdited = realtime.chat.onMessageEdited;
2272
+ const prevDeleted = realtime.chat.onMessageDeleted;
2273
+ realtime.chat.onMessageDelivered = (event) => {
2274
+ if (event.conversationId === conversationId) {
2275
+ setDeliveredIds((s) => new Set(s).add(event.messageId));
2276
+ }
2277
+ prevDelivered?.(event);
2278
+ };
2279
+ realtime.chat.onMessagesRead = (event) => {
2280
+ if (event.conversationId === conversationId) {
2281
+ setReadMessageIds((s) => {
2282
+ const next = new Set(s);
2283
+ event.messageIds.forEach((id) => next.add(id));
2284
+ return next;
2285
+ });
2286
+ }
2287
+ prevRead?.(event);
2288
+ };
2289
+ realtime.chat.onMessageEdited = (event) => {
2290
+ if (event.conversationId === conversationId) {
2291
+ setEditedMessages((m) => {
2292
+ const next = new Map(m);
2293
+ next.set(event.messageId, { content: event.newContent, editedAt: event.editedAt });
2294
+ return next;
2295
+ });
2296
+ }
2297
+ prevEdited?.(event);
2298
+ };
2299
+ realtime.chat.onMessageDeleted = (event) => {
2300
+ if (event.conversationId === conversationId) {
2301
+ setDeletedIds((s) => new Set(s).add(event.messageId));
2302
+ }
2303
+ prevDeleted?.(event);
2304
+ };
2305
+ return () => {
2306
+ realtime.leaveConversation(conversationId).catch(() => { });
2307
+ realtime.chat.onMessageDelivered = prevDelivered;
2308
+ realtime.chat.onMessagesRead = prevRead;
2309
+ realtime.chat.onMessageEdited = prevEdited;
2310
+ realtime.chat.onMessageDeleted = prevDeleted;
2311
+ };
2312
+ }, [realtime, conversationId]);
2313
+ return { deliveredIds, readMessageIds, editedMessages, deletedIds };
2314
+ }
2037
2315
  //# sourceMappingURL=index.js.map