@teincfood/chat-client 0.1.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 (78) hide show
  1. package/README.md +437 -0
  2. package/dist/api/auth.d.ts +13 -0
  3. package/dist/api/auth.d.ts.map +1 -0
  4. package/dist/api/auth.js +27 -0
  5. package/dist/api/auth.js.map +1 -0
  6. package/dist/api/contacts.d.ts +6 -0
  7. package/dist/api/contacts.d.ts.map +1 -0
  8. package/dist/api/contacts.js +21 -0
  9. package/dist/api/contacts.js.map +1 -0
  10. package/dist/api/conversations.d.ts +21 -0
  11. package/dist/api/conversations.d.ts.map +1 -0
  12. package/dist/api/conversations.js +24 -0
  13. package/dist/api/conversations.js.map +1 -0
  14. package/dist/api/messages.d.ts +11 -0
  15. package/dist/api/messages.d.ts.map +1 -0
  16. package/dist/api/messages.js +21 -0
  17. package/dist/api/messages.js.map +1 -0
  18. package/dist/api/notifications.d.ts +15 -0
  19. package/dist/api/notifications.d.ts.map +1 -0
  20. package/dist/api/notifications.js +12 -0
  21. package/dist/api/notifications.js.map +1 -0
  22. package/dist/api/upload.d.ts +6 -0
  23. package/dist/api/upload.d.ts.map +1 -0
  24. package/dist/api/upload.js +15 -0
  25. package/dist/api/upload.js.map +1 -0
  26. package/dist/client.d.ts +98 -0
  27. package/dist/client.d.ts.map +1 -0
  28. package/dist/client.js +277 -0
  29. package/dist/client.js.map +1 -0
  30. package/dist/events.d.ts +9 -0
  31. package/dist/events.d.ts.map +1 -0
  32. package/dist/events.js +28 -0
  33. package/dist/events.js.map +1 -0
  34. package/dist/formatters.d.ts +4 -0
  35. package/dist/formatters.d.ts.map +1 -0
  36. package/dist/formatters.js +32 -0
  37. package/dist/formatters.js.map +1 -0
  38. package/dist/http.d.ts +25 -0
  39. package/dist/http.d.ts.map +1 -0
  40. package/dist/http.js +90 -0
  41. package/dist/http.js.map +1 -0
  42. package/dist/index.d.ts +13 -0
  43. package/dist/index.d.ts.map +1 -0
  44. package/dist/index.js +25 -0
  45. package/dist/index.js.map +1 -0
  46. package/dist/integration.test.d.ts +10 -0
  47. package/dist/integration.test.d.ts.map +1 -0
  48. package/dist/integration.test.js +184 -0
  49. package/dist/integration.test.js.map +1 -0
  50. package/dist/optimistic.d.ts +26 -0
  51. package/dist/optimistic.d.ts.map +1 -0
  52. package/dist/optimistic.js +85 -0
  53. package/dist/optimistic.js.map +1 -0
  54. package/dist/react.d.ts +26 -0
  55. package/dist/react.d.ts.map +1 -0
  56. package/dist/react.js +208 -0
  57. package/dist/react.js.map +1 -0
  58. package/dist/retry.d.ts +7 -0
  59. package/dist/retry.d.ts.map +1 -0
  60. package/dist/retry.js +27 -0
  61. package/dist/retry.js.map +1 -0
  62. package/dist/socket.d.ts +83 -0
  63. package/dist/socket.d.ts.map +1 -0
  64. package/dist/socket.js +615 -0
  65. package/dist/socket.js.map +1 -0
  66. package/dist/store.d.ts +36 -0
  67. package/dist/store.d.ts.map +1 -0
  68. package/dist/store.js +185 -0
  69. package/dist/store.js.map +1 -0
  70. package/dist/types.d.ts +229 -0
  71. package/dist/types.d.ts.map +1 -0
  72. package/dist/types.js +22 -0
  73. package/dist/types.js.map +1 -0
  74. package/dist/unit.test.d.ts +8 -0
  75. package/dist/unit.test.d.ts.map +1 -0
  76. package/dist/unit.test.js +425 -0
  77. package/dist/unit.test.js.map +1 -0
  78. package/package.json +31 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.d.ts","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,wBAAgB,eAAe,CAAC,IAAI,EAAE,UAAU;6BAGhC,MAAM,eACH,MAAM,YACV,MAAM,GAAG,QAAQ,GAAG,KAAK;EAUvC"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createUploadApi = createUploadApi;
4
+ function createUploadApi(http) {
5
+ return {
6
+ getUploadUrl: (fileName, contentType, purpose = "chat") => http
7
+ .post("/upload", {
8
+ file_name: fileName,
9
+ content_type: contentType,
10
+ purpose,
11
+ })
12
+ .then((r) => r.data),
13
+ };
14
+ }
15
+ //# sourceMappingURL=upload.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload.js","sourceRoot":"","sources":["../../src/api/upload.ts"],"names":[],"mappings":";;AAGA,0CAeC;AAfD,SAAgB,eAAe,CAAC,IAAgB;IAC9C,OAAO;QACL,YAAY,EAAE,CACZ,QAAgB,EAChB,WAAmB,EACnB,UAAqC,MAAM,EAC3C,EAAE,CACF,IAAI;aACD,IAAI,CAA4B,SAAS,EAAE;YAC1C,SAAS,EAAE,QAAQ;YACnB,YAAY,EAAE,WAAW;YACzB,OAAO;SACR,CAAC;aACD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;KACzB,CAAA;AACH,CAAC"}
@@ -0,0 +1,98 @@
1
+ import type { AuthTokens, ChatClientOptions, ChatEventName, ChatEventMap, Contact, ContactContext, Conversation, Message, TempMessage, MessageListMeta, PushEventResult } from "./types";
2
+ import { TypedEventEmitter } from "./events";
3
+ import { ChatStore } from "./store";
4
+ import { createAuthApi } from "./api/auth";
5
+ import { createConversationsApi } from "./api/conversations";
6
+ import { createMessagesApi } from "./api/messages";
7
+ import { createContactsApi } from "./api/contacts";
8
+ import { createNotificationsApi } from "./api/notifications";
9
+ import { createUploadApi } from "./api/upload";
10
+ export declare class ChatClient {
11
+ readonly auth: ReturnType<typeof createAuthApi>;
12
+ readonly conversations: ReturnType<typeof createConversationsApi>;
13
+ readonly messages: ReturnType<typeof createMessagesApi>;
14
+ readonly contacts: ReturnType<typeof createContactsApi>;
15
+ readonly notifications: ReturnType<typeof createNotificationsApi>;
16
+ readonly upload: ReturnType<typeof createUploadApi>;
17
+ readonly store: ChatStore;
18
+ readonly emitter: TypedEventEmitter;
19
+ private http;
20
+ private optimistic;
21
+ private socket;
22
+ private tokens;
23
+ private currentUserId;
24
+ constructor(opts: ChatClientOptions);
25
+ /** Hydrate cached data from storage and connect the WebSocket. */
26
+ start(tokens: AuthTokens): Promise<void>;
27
+ /** Disconnect WebSocket and clear all listeners. */
28
+ stop(): void;
29
+ /** Fully clear local cache. */
30
+ clearCache(): Promise<void>;
31
+ getTokens(): AuthTokens | null;
32
+ setTokens(tokens: AuthTokens | null): void;
33
+ setCurrentUserId(userId: string): void;
34
+ getCurrentUserId(): string | null;
35
+ /** Check if the WebSocket is currently connected. */
36
+ isConnected(): boolean;
37
+ /** Return optimistic messages not yet confirmed by the server. */
38
+ getPendingMessages(conversationId: string): TempMessage[];
39
+ /** Join a conversation channel to receive live updates. */
40
+ joinConversation(conversationId: string): Promise<void>;
41
+ /** Leave a conversation channel. */
42
+ leaveConversation(conversationId: string): void;
43
+ /**
44
+ * Send a text message with optimistic UI.
45
+ * Must be joined to the conversation's WebSocket channel first.
46
+ * Returns the temp message immediately; reconciled when server confirms.
47
+ */
48
+ sendMessage(conversationId: string, body: string, metadata?: Record<string, unknown> | null): TempMessage;
49
+ /**
50
+ * Remove a pending (optimistic) message by its client-generated ID.
51
+ * Useful when retrying a failed message — remove the old failed temp
52
+ * before re-sending.
53
+ */
54
+ removePendingMessage(clientId: string): void;
55
+ /**
56
+ * Resend all messages with status "failed" across all conversations.
57
+ * Called automatically after reconnection.
58
+ */
59
+ resendFailedMessages(): void;
60
+ sendTyping(conversationId: string): void;
61
+ markRead(conversationId: string): void;
62
+ /** Fetch message history via WebSocket. */
63
+ fetchMessages(conversationId: string, limit?: number): void;
64
+ /** Fetch inbox then cache it. */
65
+ loadInbox(): Promise<Conversation[]>;
66
+ /** Fetch contacts then cache them. */
67
+ loadContacts(context?: ContactContext, businessId?: string, q?: string): Promise<Contact[]>;
68
+ /** Get messages from REST (falls back when WebSocket response unreliable). */
69
+ loadMessages(conversationId: string, limit?: number, offset?: number): Promise<{
70
+ data: Message[];
71
+ meta: MessageListMeta;
72
+ }>;
73
+ /**
74
+ * Process a remote push notification payload outside the WebSocket.
75
+ *
76
+ * On mobile platforms the WS connection is killed when the app goes to
77
+ * background. Push notifications carry the event data directly. This
78
+ * method routes the payload through the same store + event logic as the
79
+ * live events channel, so the UI updates even without a WS connection.
80
+ *
81
+ * The returned `sequence` (if present) is tracked internally so that
82
+ * when the WS reconnects and the events channel is re-joined, the
83
+ * `last_sequence` parameter prevents duplicate event delivery.
84
+ *
85
+ * Usage with React Native background push:
86
+ * ```ts
87
+ * // In your push notification handler:
88
+ * const result = chatClient.handlePushPayload(notification.data)
89
+ * if (result.handled) {
90
+ * // Optional: trigger a local notification or update badge
91
+ * }
92
+ * ```
93
+ */
94
+ handlePushPayload(payload: Record<string, unknown>): PushEventResult;
95
+ on<E extends ChatEventName>(event: E, listener: ChatEventMap[E]): () => void;
96
+ off<E extends ChatEventName>(event: E, listener: ChatEventMap[E]): void;
97
+ }
98
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
package/dist/client.js ADDED
@@ -0,0 +1,277 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChatClient = void 0;
4
+ const events_1 = require("./events");
5
+ const http_1 = require("./http");
6
+ const store_1 = require("./store");
7
+ const optimistic_1 = require("./optimistic");
8
+ const socket_1 = require("./socket");
9
+ const auth_1 = require("./api/auth");
10
+ const conversations_1 = require("./api/conversations");
11
+ const messages_1 = require("./api/messages");
12
+ const contacts_1 = require("./api/contacts");
13
+ const notifications_1 = require("./api/notifications");
14
+ const upload_1 = require("./api/upload");
15
+ const retry_1 = require("./retry");
16
+ class ChatClient {
17
+ constructor(opts) {
18
+ this.tokens = null;
19
+ this.currentUserId = null;
20
+ this.store = new store_1.ChatStore(opts.storage);
21
+ this.emitter = new events_1.TypedEventEmitter();
22
+ this.optimistic = new optimistic_1.OptimisticEngine(this.emitter);
23
+ this.http = new http_1.HttpClient({
24
+ baseUrl: opts.baseUrl,
25
+ getTokens: () => this.tokens,
26
+ setTokens: (t) => {
27
+ this.tokens = t;
28
+ },
29
+ onTokenExpired: opts.onTokenExpired,
30
+ emitter: this.emitter,
31
+ });
32
+ this.socket = new socket_1.PhoenixChatSocket(opts.wsUrl, this.emitter, this.optimistic, this.store);
33
+ this.auth = (0, auth_1.createAuthApi)(this.http);
34
+ this.conversations = (0, conversations_1.createConversationsApi)(this.http);
35
+ this.messages = (0, messages_1.createMessagesApi)(this.http);
36
+ this.contacts = (0, contacts_1.createContactsApi)(this.http);
37
+ this.notifications = (0, notifications_1.createNotificationsApi)(this.http);
38
+ this.upload = (0, upload_1.createUploadApi)(this.http);
39
+ }
40
+ // ─── Lifecycle ────────────────────────────────────
41
+ /** Hydrate cached data from storage and connect the WebSocket. */
42
+ async start(tokens) {
43
+ this.tokens = tokens;
44
+ await this.store.hydrate();
45
+ try {
46
+ await this.socket.connect(tokens.access_token);
47
+ // Auto-join the events channel for live updates
48
+ if (this.currentUserId) {
49
+ await this.socket.joinEventsChannel(this.currentUserId).catch(() => { });
50
+ }
51
+ }
52
+ catch {
53
+ // WS is optional — REST calls still work
54
+ }
55
+ }
56
+ /** Disconnect WebSocket and clear all listeners. */
57
+ stop() {
58
+ this.socket.leaveEventsChannel();
59
+ this.socket.disconnect();
60
+ this.optimistic.clear();
61
+ this.emitter.removeAllListeners();
62
+ }
63
+ /** Fully clear local cache. */
64
+ async clearCache() {
65
+ this.store.clear();
66
+ }
67
+ // ─── Token management ─────────────────────────────
68
+ getTokens() {
69
+ return this.tokens;
70
+ }
71
+ setTokens(tokens) {
72
+ this.tokens = tokens;
73
+ }
74
+ setCurrentUserId(userId) {
75
+ this.currentUserId = userId;
76
+ this.socket.currentUserId = userId;
77
+ // Auto-join events channel if socket already connected
78
+ if (this.socket.isConnected()) {
79
+ this.socket.joinEventsChannel(userId).catch(() => { });
80
+ }
81
+ }
82
+ getCurrentUserId() {
83
+ return this.currentUserId;
84
+ }
85
+ /** Check if the WebSocket is currently connected. */
86
+ isConnected() {
87
+ return this.socket.isConnected();
88
+ }
89
+ /** Return optimistic messages not yet confirmed by the server. */
90
+ getPendingMessages(conversationId) {
91
+ return this.optimistic.getPending(conversationId);
92
+ }
93
+ // ─── WebSocket channel management ─────────────────
94
+ /** Join a conversation channel to receive live updates. */
95
+ async joinConversation(conversationId) {
96
+ await this.socket.joinConversation(conversationId);
97
+ }
98
+ /** Leave a conversation channel. */
99
+ leaveConversation(conversationId) {
100
+ this.socket.leaveConversation(conversationId);
101
+ }
102
+ // ─── Sending messages (optimistic) ────────────────
103
+ /**
104
+ * Send a text message with optimistic UI.
105
+ * Must be joined to the conversation's WebSocket channel first.
106
+ * Returns the temp message immediately; reconciled when server confirms.
107
+ */
108
+ sendMessage(conversationId, body, metadata) {
109
+ const senderId = this.currentUserId ?? "unknown";
110
+ // Create optimistic message (not stored — useChat merges pending + stored)
111
+ const temp = this.optimistic.create(conversationId, senderId, body, metadata);
112
+ if (!this.socket.isJoined(conversationId)) {
113
+ console.warn(`[chat-client] Not joined to chat:${conversationId}, cannot send`);
114
+ temp.status = "failed";
115
+ this.emitter.emit("message:failed", temp);
116
+ return temp;
117
+ }
118
+ // Send via WebSocket (with retry)
119
+ (0, retry_1.retry)(() => {
120
+ this.socket.sendMessage(conversationId, body, metadata);
121
+ return Promise.resolve();
122
+ }).catch(() => {
123
+ temp.status = "failed";
124
+ this.emitter.emit("message:failed", temp);
125
+ });
126
+ return temp;
127
+ }
128
+ /**
129
+ * Remove a pending (optimistic) message by its client-generated ID.
130
+ * Useful when retrying a failed message — remove the old failed temp
131
+ * before re-sending.
132
+ */
133
+ removePendingMessage(clientId) {
134
+ this.optimistic.removeByClientId(clientId);
135
+ }
136
+ /**
137
+ * Resend all messages with status "failed" across all conversations.
138
+ * Called automatically after reconnection.
139
+ */
140
+ resendFailedMessages() {
141
+ const all = this.optimistic.getAll();
142
+ for (const msg of all) {
143
+ if (msg.status !== "failed")
144
+ continue;
145
+ if (!this.socket.isJoined(msg.conversation_id))
146
+ continue;
147
+ this.socket.sendMessage(msg.conversation_id, msg.content, msg.metadata);
148
+ }
149
+ }
150
+ // ─── Other real-time actions ──────────────────────
151
+ sendTyping(conversationId) {
152
+ this.socket.sendTyping(conversationId);
153
+ }
154
+ markRead(conversationId) {
155
+ this.socket.markRead(conversationId);
156
+ }
157
+ /** Fetch message history via WebSocket. */
158
+ fetchMessages(conversationId, limit) {
159
+ this.socket.fetchMessages(conversationId, limit);
160
+ }
161
+ // ─── REST helpers ─────────────────────────────────
162
+ /** Fetch inbox then cache it. */
163
+ async loadInbox() {
164
+ const convs = await this.conversations.getInbox();
165
+ this.store.setConversations(convs);
166
+ return convs;
167
+ }
168
+ /** Fetch contacts then cache them. */
169
+ async loadContacts(context, businessId, q) {
170
+ const result = await this.contacts.getContacts(context, businessId, q);
171
+ this.store.setContacts(result);
172
+ return result;
173
+ }
174
+ /** Get messages from REST (falls back when WebSocket response unreliable). */
175
+ async loadMessages(conversationId, limit = 50, offset = 0) {
176
+ const result = await this.messages.list(conversationId, limit, offset);
177
+ if (offset === 0) {
178
+ this.store.setMessages(conversationId, result.data);
179
+ }
180
+ else {
181
+ for (const m of result.data) {
182
+ this.store.upsertMessage(conversationId, m);
183
+ }
184
+ }
185
+ return result;
186
+ }
187
+ // ─── Background push notification handling ─────────
188
+ /**
189
+ * Process a remote push notification payload outside the WebSocket.
190
+ *
191
+ * On mobile platforms the WS connection is killed when the app goes to
192
+ * background. Push notifications carry the event data directly. This
193
+ * method routes the payload through the same store + event logic as the
194
+ * live events channel, so the UI updates even without a WS connection.
195
+ *
196
+ * The returned `sequence` (if present) is tracked internally so that
197
+ * when the WS reconnects and the events channel is re-joined, the
198
+ * `last_sequence` parameter prevents duplicate event delivery.
199
+ *
200
+ * Usage with React Native background push:
201
+ * ```ts
202
+ * // In your push notification handler:
203
+ * const result = chatClient.handlePushPayload(notification.data)
204
+ * if (result.handled) {
205
+ * // Optional: trigger a local notification or update badge
206
+ * }
207
+ * ```
208
+ */
209
+ handlePushPayload(payload) {
210
+ const event = payload.event;
211
+ if (!event)
212
+ return { handled: false };
213
+ const seq = payload._sequence;
214
+ if (typeof seq === "number") {
215
+ this.socket.setLastSequence(seq);
216
+ }
217
+ switch (event) {
218
+ case "new_message": {
219
+ const raw = (payload.message ?? payload);
220
+ const msg = (0, socket_1.toMessage)(raw);
221
+ this.store.upsertMessage(msg.conversation_id, msg);
222
+ this.emitter.emit("message:received", msg);
223
+ return { handled: true, event: "message:received", data: raw, sequence: seq };
224
+ }
225
+ case "conversation:created": {
226
+ const raw = (payload.conversation ?? payload);
227
+ const conv = (0, socket_1.toConversation)(raw);
228
+ this.store.upsertConversation(conv);
229
+ this.emitter.emit("conversation:created", { conversation: conv });
230
+ return { handled: true, event: "conversation:created", data: raw, sequence: seq };
231
+ }
232
+ case "conversation:updated": {
233
+ const raw = (payload.conversation ?? payload);
234
+ const conv = (0, socket_1.toConversation)(raw);
235
+ this.store.upsertConversation(conv);
236
+ this.emitter.emit("conversation:updated", { conversation: conv });
237
+ return { handled: true, event: "conversation:updated", data: raw, sequence: seq };
238
+ }
239
+ case "messages_read": {
240
+ this.store.markConversationRead(payload.conversation_id, payload.reader_id);
241
+ this.emitter.emit("messages_read", {
242
+ conversationId: payload.conversation_id,
243
+ userId: payload.reader_id,
244
+ readAt: payload.read_at,
245
+ });
246
+ return { handled: true, event: "messages_read", data: payload, sequence: seq };
247
+ }
248
+ case "messages_delivered": {
249
+ this.store.markConversationDelivered(payload.conversation_id, payload.user_id);
250
+ this.emitter.emit("messages_delivered", {
251
+ conversationId: payload.conversation_id,
252
+ userId: payload.user_id,
253
+ });
254
+ return { handled: true, event: "messages_delivered", data: payload, sequence: seq };
255
+ }
256
+ case "unread_count": {
257
+ this.store.setUnreadCount(payload.conversation_id, payload.unread_count);
258
+ this.emitter.emit("unread_count", {
259
+ conversationId: payload.conversation_id,
260
+ unread_count: payload.unread_count,
261
+ });
262
+ return { handled: true, event: "unread_count", data: payload, sequence: seq };
263
+ }
264
+ default:
265
+ return { handled: false };
266
+ }
267
+ }
268
+ // ─── Event subscriptions ──────────────────────────
269
+ on(event, listener) {
270
+ return this.emitter.on(event, listener);
271
+ }
272
+ off(event, listener) {
273
+ this.emitter.off(event, listener);
274
+ }
275
+ }
276
+ exports.ChatClient = ChatClient;
277
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ChatEventMap, ChatEventName } from "./types";
2
+ export declare class TypedEventEmitter {
3
+ private listeners;
4
+ on<E extends ChatEventName>(event: E, listener: ChatEventMap[E]): () => void;
5
+ off<E extends ChatEventName>(event: E, listener: ChatEventMap[E]): void;
6
+ emit<E extends ChatEventName>(event: E, ...args: Parameters<ChatEventMap[E]>): void;
7
+ removeAllListeners(): void;
8
+ }
9
+ //# sourceMappingURL=events.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.d.ts","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAA;AAI1D,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,SAAS,CAAgC;IAEjD,EAAE,CAAC,CAAC,SAAS,aAAa,EACxB,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,GACxB,MAAM,IAAI;IAQb,GAAG,CAAC,CAAC,SAAS,aAAa,EACzB,KAAK,EAAE,CAAC,EACR,QAAQ,EAAE,YAAY,CAAC,CAAC,CAAC,GACxB,IAAI;IAIP,IAAI,CAAC,CAAC,SAAS,aAAa,EAC1B,KAAK,EAAE,CAAC,EACR,GAAG,IAAI,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GACnC,IAAI;IAMP,kBAAkB,IAAI,IAAI;CAG3B"}
package/dist/events.js ADDED
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.TypedEventEmitter = void 0;
4
+ class TypedEventEmitter {
5
+ constructor() {
6
+ this.listeners = new Map();
7
+ }
8
+ on(event, listener) {
9
+ if (!this.listeners.has(event)) {
10
+ this.listeners.set(event, new Set());
11
+ }
12
+ this.listeners.get(event).add(listener);
13
+ return () => this.off(event, listener);
14
+ }
15
+ off(event, listener) {
16
+ this.listeners.get(event)?.delete(listener);
17
+ }
18
+ emit(event, ...args) {
19
+ this.listeners
20
+ .get(event)
21
+ ?.forEach((fn) => fn(...args));
22
+ }
23
+ removeAllListeners() {
24
+ this.listeners.clear();
25
+ }
26
+ }
27
+ exports.TypedEventEmitter = TypedEventEmitter;
28
+ //# sourceMappingURL=events.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"events.js","sourceRoot":"","sources":["../src/events.ts"],"names":[],"mappings":";;;AAIA,MAAa,iBAAiB;IAA9B;QACU,cAAS,GAAG,IAAI,GAAG,EAAsB,CAAA;IAgCnD,CAAC;IA9BC,EAAE,CACA,KAAQ,EACR,QAAyB;QAEzB,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC/B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,GAAG,EAAE,CAAC,CAAA;QACtC,CAAC;QACD,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAE,CAAC,GAAG,CAAC,QAAiB,CAAC,CAAA;QACjD,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAA;IACxC,CAAC;IAED,GAAG,CACD,KAAQ,EACR,QAAyB;QAEzB,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,QAAiB,CAAC,CAAA;IACtD,CAAC;IAED,IAAI,CACF,KAAQ,EACR,GAAG,IAAiC;QAEpC,IAAI,CAAC,SAAS;aACX,GAAG,CAAC,KAAK,CAAC;YACX,EAAE,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,GAAI,IAAkB,CAAC,CAAC,CAAA;IACjD,CAAC;IAED,kBAAkB;QAChB,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAA;IACxB,CAAC;CACF;AAjCD,8CAiCC"}
@@ -0,0 +1,4 @@
1
+ export declare function formatTime(date: string | Date): string;
2
+ export declare function formatDateSeparator(date: string | Date): string;
3
+ export declare function shouldShowDateSeparator(current: string | Date, previous: string | Date | null): boolean;
4
+ //# sourceMappingURL=formatters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"AAAA,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAGtD;AAED,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAW/D;AAED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,GAAG,OAAO,CASvG"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.formatTime = formatTime;
4
+ exports.formatDateSeparator = formatDateSeparator;
5
+ exports.shouldShowDateSeparator = shouldShowDateSeparator;
6
+ function formatTime(date) {
7
+ const d = typeof date === "string" ? new Date(date) : date;
8
+ return d.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" });
9
+ }
10
+ function formatDateSeparator(date) {
11
+ const d = typeof date === "string" ? new Date(date) : date;
12
+ const now = new Date();
13
+ const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
14
+ const yesterday = new Date(today);
15
+ yesterday.setDate(yesterday.getDate() - 1);
16
+ const target = new Date(d.getFullYear(), d.getMonth(), d.getDate());
17
+ if (target.getTime() === today.getTime())
18
+ return "Today";
19
+ if (target.getTime() === yesterday.getTime())
20
+ return "Yesterday";
21
+ return d.toLocaleDateString([], { weekday: "long", month: "long", day: "numeric" });
22
+ }
23
+ function shouldShowDateSeparator(current, previous) {
24
+ if (!previous)
25
+ return true;
26
+ const cur = typeof current === "string" ? new Date(current) : current;
27
+ const prev = typeof previous === "string" ? new Date(previous) : previous;
28
+ return (cur.getFullYear() !== prev.getFullYear() ||
29
+ cur.getMonth() !== prev.getMonth() ||
30
+ cur.getDate() !== prev.getDate());
31
+ }
32
+ //# sourceMappingURL=formatters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatters.js","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":";;AAAA,gCAGC;AAED,kDAWC;AAED,0DASC;AA3BD,SAAgB,UAAU,CAAC,IAAmB;IAC5C,MAAM,CAAC,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC1D,OAAO,CAAC,CAAC,kBAAkB,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,SAAS,EAAE,CAAC,CAAA;AACzE,CAAC;AAED,SAAgB,mBAAmB,CAAC,IAAmB;IACrD,MAAM,CAAC,GAAG,OAAO,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAA;IAC1D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAA;IACtB,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,WAAW,EAAE,EAAE,GAAG,CAAC,QAAQ,EAAE,EAAE,GAAG,CAAC,OAAO,EAAE,CAAC,CAAA;IACxE,MAAM,SAAS,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,CAAA;IACjC,SAAS,CAAC,OAAO,CAAC,SAAS,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;IAC1C,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,CAAC,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,CAAA;IAEnE,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,KAAK,CAAC,OAAO,EAAE;QAAE,OAAO,OAAO,CAAA;IACxD,IAAI,MAAM,CAAC,OAAO,EAAE,KAAK,SAAS,CAAC,OAAO,EAAE;QAAE,OAAO,WAAW,CAAA;IAChE,OAAO,CAAC,CAAC,kBAAkB,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAA;AACrF,CAAC;AAED,SAAgB,uBAAuB,CAAC,OAAsB,EAAE,QAA8B;IAC5F,IAAI,CAAC,QAAQ;QAAE,OAAO,IAAI,CAAA;IAC1B,MAAM,GAAG,GAAG,OAAO,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAA;IACrE,MAAM,IAAI,GAAG,OAAO,QAAQ,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAA;IACzE,OAAO,CACL,GAAG,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE;QACxC,GAAG,CAAC,QAAQ,EAAE,KAAK,IAAI,CAAC,QAAQ,EAAE;QAClC,GAAG,CAAC,OAAO,EAAE,KAAK,IAAI,CAAC,OAAO,EAAE,CACjC,CAAA;AACH,CAAC"}
package/dist/http.d.ts ADDED
@@ -0,0 +1,25 @@
1
+ import type { AuthTokens } from "./types";
2
+ import { TypedEventEmitter } from "./events";
3
+ export interface HttpOptions {
4
+ baseUrl: string;
5
+ getTokens: () => AuthTokens | null;
6
+ setTokens: (tokens: AuthTokens | null) => void;
7
+ onTokenExpired?: () => Promise<AuthTokens | null>;
8
+ emitter: TypedEventEmitter;
9
+ }
10
+ export declare class HttpClient {
11
+ private opts;
12
+ constructor(opts: HttpOptions);
13
+ private get base();
14
+ private request;
15
+ get<T>(path: string): Promise<T>;
16
+ post<T>(path: string, body?: unknown): Promise<T>;
17
+ patch<T>(path: string, body?: unknown): Promise<T>;
18
+ delete<T>(path: string, body?: unknown): Promise<T>;
19
+ }
20
+ export declare class HttpError extends Error {
21
+ status: number;
22
+ detail: unknown;
23
+ constructor(status: number, statusText: string, detail: unknown);
24
+ }
25
+ //# sourceMappingURL=http.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.d.ts","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,UAAU,CAAA;AAE5C,MAAM,WAAW,WAAW;IAC1B,OAAO,EAAE,MAAM,CAAA;IACf,SAAS,EAAE,MAAM,UAAU,GAAG,IAAI,CAAA;IAClC,SAAS,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI,KAAK,IAAI,CAAA;IAC9C,cAAc,CAAC,EAAE,MAAM,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAAA;IACjD,OAAO,EAAE,iBAAiB,CAAA;CAC3B;AAED,qBAAa,UAAU;IACrB,OAAO,CAAC,IAAI,CAAa;gBAEb,IAAI,EAAE,WAAW;IAI7B,OAAO,KAAK,IAAI,GAOf;YAEa,OAAO;IAsDrB,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;IAIhC,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIjD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;IAIlD,MAAM,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,CAAC,CAAC;CAGpD;AAYD,qBAAa,SAAU,SAAQ,KAAK;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,OAAO,CAAA;gBAEH,MAAM,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO;CAMhE"}
package/dist/http.js ADDED
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.HttpError = exports.HttpClient = void 0;
4
+ class HttpClient {
5
+ constructor(opts) {
6
+ this.opts = opts;
7
+ }
8
+ get base() {
9
+ if (!this.opts.baseUrl) {
10
+ throw new Error("API base URL is not configured. Pass a valid `baseUrl` to ChatClientOptions.");
11
+ }
12
+ return this.opts.baseUrl.replace(/\/+$/, "");
13
+ }
14
+ async request(method, path, body) {
15
+ const url = `${this.base}${path}`;
16
+ if (url.startsWith("/")) {
17
+ console.error("[HttpClient] BUG: url is relative!", { baseUrl: this.opts.baseUrl, path, url });
18
+ throw new Error(`Invalid URL: ${url} (base="${this.opts.baseUrl}")`);
19
+ }
20
+ const tokens = this.opts.getTokens();
21
+ const headers = {
22
+ "Content-Type": "application/json",
23
+ Accept: "application/json",
24
+ };
25
+ if (tokens?.access_token) {
26
+ headers["Authorization"] = `Bearer ${tokens.access_token}`;
27
+ }
28
+ const res = await fetch(url, {
29
+ method,
30
+ headers,
31
+ body: body != null ? JSON.stringify(body) : undefined,
32
+ });
33
+ if (res.status === 401) {
34
+ if (this.opts.onTokenExpired) {
35
+ const newTokens = await this.opts.onTokenExpired();
36
+ if (newTokens) {
37
+ this.opts.setTokens(newTokens);
38
+ headers["Authorization"] = `Bearer ${newTokens.access_token}`;
39
+ const retryRes = await fetch(url, {
40
+ method,
41
+ headers,
42
+ body: body != null ? JSON.stringify(body) : undefined,
43
+ });
44
+ if (!retryRes.ok) {
45
+ throw await toHttpError(retryRes);
46
+ }
47
+ return retryRes.json();
48
+ }
49
+ }
50
+ this.opts.emitter.emit("auth:expired");
51
+ }
52
+ if (!res.ok) {
53
+ throw await toHttpError(res);
54
+ }
55
+ return res.json();
56
+ }
57
+ get(path) {
58
+ return this.request("GET", path);
59
+ }
60
+ post(path, body) {
61
+ return this.request("POST", path, body);
62
+ }
63
+ patch(path, body) {
64
+ return this.request("PATCH", path, body);
65
+ }
66
+ delete(path, body) {
67
+ return this.request("DELETE", path, body);
68
+ }
69
+ }
70
+ exports.HttpClient = HttpClient;
71
+ async function toHttpError(res) {
72
+ let detail;
73
+ try {
74
+ detail = await res.json();
75
+ }
76
+ catch {
77
+ detail = await res.text().catch(() => null);
78
+ }
79
+ return new HttpError(res.status, res.statusText, detail);
80
+ }
81
+ class HttpError extends Error {
82
+ constructor(status, statusText, detail) {
83
+ super(`${status} ${statusText}`);
84
+ this.name = "HttpError";
85
+ this.status = status;
86
+ this.detail = detail;
87
+ }
88
+ }
89
+ exports.HttpError = HttpError;
90
+ //# sourceMappingURL=http.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"http.js","sourceRoot":"","sources":["../src/http.ts"],"names":[],"mappings":"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"}