@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
package/README.md ADDED
@@ -0,0 +1,437 @@
1
+ # teincfood-chat-client
2
+
3
+ Headless, event-driven chat client for TeincFood. Connects to the TeincFood Phoenix backend via raw WebSocket (Phoenix v2 protocol) for real-time messaging and REST for initial data load.
4
+
5
+ ## Features
6
+
7
+ - **Event-driven** — all data flows through WebSocket events. No polling.
8
+ - **Optimistic UI** — messages appear instantly, reconciled when the server confirms.
9
+ - **Auto-mark-read** — joining a conversation automatically marks messages as read (like WhatsApp).
10
+ - **Background push handling** — `handlePushPayload()` processes push notification events when the app is in background (where WS is killed).
11
+ - **Pluggable storage** — bring your own adapter (AsyncStorage, MMKV, etc.).
12
+ - **React hooks** — optional `useChat`, `useConversations`, `useContacts`.
13
+ - **Typed events** — full TypeScript types for every event payload.
14
+
15
+ ## Architecture
16
+
17
+ ```
18
+ ┌──────────────┐ WebSocket (Phoenix v2) ┌─────────────┐
19
+ │ ChatClient │ ────────────────────────────── │ Backend │
20
+ │ │ events:{userId} │ │
21
+ │ ┌────────┐ │ ─ conversation:created │ Phoenix │
22
+ │ │ Store │◄──── ─ conversation:updated │ Channels │
23
+ │ ├────────┤ │ ─ new_message │ │
24
+ │ │ Emitter│ │ ─ messages_read │ │
25
+ │ ├────────┤ │ ─ unread_count │ │
26
+ │ │ Socket │─── │ │
27
+ │ ├────────┤ │ chat:{conversationId} │ │
28
+ │ │Optimist│ │ ─ new_message (send/recv) │ │
29
+ │ └────────┘ │ ─ mark_read │ │
30
+ │ │ ─ typing │ │
31
+ │ │ ─ presence_state/diff │ │
32
+ └──────────────┘ └─────────────┘
33
+ ```
34
+
35
+ Two channel types:
36
+
37
+ - **`events:{userId}`** — user-level channel. Receives `conversation:created`, `conversation:updated`, `new_message`, `messages_read`, `unread_count`. Auto-joined on `start()`. Uses `last_sequence` to avoid replaying events seen via push notifications.
38
+ - **`chat:{conversationId}`** — per-conversation channel. Used for sending messages, typing indicators, presence, and auto-mark-read on join.
39
+
40
+ ## Installation
41
+
42
+ ```bash
43
+ npm install teincfood-chat-client
44
+ ```
45
+
46
+ ## Quick Start
47
+
48
+ ```typescript
49
+ import { ChatClient } from "teincfood-chat-client"
50
+ import AsyncStorage from "@react-native-async-storage/async-storage"
51
+
52
+ const storage: ChatStorage = {
53
+ getItem: (key) => AsyncStorage.getItem(key).then((v) => v ? JSON.parse(v) : null),
54
+ setItem: (key, value) => AsyncStorage.setItem(key, JSON.stringify(value)),
55
+ removeItem: (key) => AsyncStorage.removeItem(key),
56
+ clear: () => AsyncStorage.clear(),
57
+ }
58
+
59
+ const client = new ChatClient({
60
+ baseUrl: "https://api.teincfood.com/api/v1",
61
+ wsUrl: "wss://api.teincfood.com/ws",
62
+ storage,
63
+ })
64
+
65
+ // Set current user and connect
66
+ client.setCurrentUserId("user-uuid")
67
+ await client.start({
68
+ access_token: "jwt-access-token",
69
+ refresh_token: "jwt-refresh-token",
70
+ })
71
+
72
+ // Listen for events
73
+ client.on("message:received", (msg) => {
74
+ console.log("New message:", msg.content)
75
+ })
76
+
77
+ client.on("conversation:created", ({ conversation }) => {
78
+ console.log("New conversation:", conversation.id)
79
+ })
80
+
81
+ // Join a conversation and send a message
82
+ await client.joinConversation("conv-uuid")
83
+ client.sendMessage("conv-uuid", "Hello, world!")
84
+
85
+ // Receipt: wait for the message:sent confirmation
86
+ client.on("message:sent", (msg) => {
87
+ console.log("Message confirmed by server:", msg.id)
88
+ })
89
+ ```
90
+
91
+ ## Configuration
92
+
93
+ | Option | Type | Required | Description |
94
+ |--------|------|----------|-------------|
95
+ | `baseUrl` | `string` | yes | REST API base URL (e.g. `https://api.example.com/api/v1`) |
96
+ | `wsUrl` | `string` | yes | WebSocket endpoint (e.g. `wss://api.example.com/ws`) |
97
+ | `storage` | `ChatStorage` | yes | Persistent storage adapter |
98
+ | `onTokenExpired` | `() => Promise<AuthTokens \| null>` | no | Called on 401. Return new tokens or `null` to trigger `auth:expired`. |
99
+
100
+ ## API Reference
101
+
102
+ ### Lifecycle
103
+
104
+ #### `start(tokens: AuthTokens): Promise<void>`
105
+
106
+ Hydrate cached data from storage, connect the WebSocket, and auto-join the `events:{userId}` channel.
107
+
108
+ ```typescript
109
+ await client.start({
110
+ access_token: "...",
111
+ refresh_token: "...",
112
+ })
113
+ ```
114
+
115
+ #### `stop(): void`
116
+
117
+ Disconnect WebSocket, leave events channel, clear optimistic messages and all event listeners.
118
+
119
+ #### `clearCache(): Promise<void>`
120
+
121
+ Clear all local data from the store and storage adapter.
122
+
123
+ ### Token & User Management
124
+
125
+ | Method | Returns | Description |
126
+ |--------|---------|-------------|
127
+ | `getTokens()` | `AuthTokens \| null` | Current tokens |
128
+ | `setTokens(tokens)` | `void` | Update tokens |
129
+ | `getCurrentUserId()` | `string \| null` | Current user ID |
130
+ | `setCurrentUserId(userId)` | `void` | Set user ID (auto-joins events channel if WS connected) |
131
+
132
+ ### Real-time Messaging
133
+
134
+ #### `sendMessage(conversationId: string, body: string, metadata?: Record<string, unknown> | null): TempMessage`
135
+
136
+ Send a text message. Creates an optimistic message immediately and pushes to the WebSocket. The `message:sent` event fires when the server confirms.
137
+
138
+ - Must be joined to the conversation channel first.
139
+ - Returns a `TempMessage` (has `_clientId` for reconciliation tracking).
140
+ - On failure, `message:failed` event fires and `temp.status` becomes `"failed"`.
141
+
142
+ ```typescript
143
+ client.sendMessage("conv-1", "Hello!", { priority: "high" })
144
+ ```
145
+
146
+ #### `joinConversation(conversationId: string): Promise<void>`
147
+
148
+ Join a conversation channel for live updates. Automatically pushes `mark_read` on join (like WhatsApp), which triggers `messages_read` events for other participants.
149
+
150
+ ```typescript
151
+ await client.joinConversation("conv-1")
152
+ ```
153
+
154
+ #### `leaveConversation(conversationId: string): void`
155
+
156
+ Leave a conversation channel.
157
+
158
+ #### `markRead(conversationId: string): void`
159
+
160
+ Manually push `mark_read` to a conversation.
161
+
162
+ #### `sendTyping(conversationId: string): void`
163
+
164
+ Push a typing indicator to a conversation.
165
+
166
+ #### `fetchMessages(conversationId: string, limit?: number): void`
167
+
168
+ Request message history via WebSocket. Emits `messages:loaded` when the server responds.
169
+
170
+ ### REST Helpers
171
+
172
+ These make one-time HTTP requests and cache the results in the store. Primarily used for initial data load or fallback.
173
+
174
+ | Method | Returns | Description |
175
+ |--------|---------|-------------|
176
+ | `loadInbox()` | `Promise<Conversation[]>` | Fetch inbox from REST, cache in store |
177
+ | `loadContacts(context?, businessId?, q?)` | `Promise<Contact[]>` | Fetch contacts from REST, cache in store |
178
+ | `loadMessages(convId, limit?, offset?)` | `Promise<{ data: Message[]; meta: MessageListMeta }>` | Fetch message page from REST, cache in store |
179
+
180
+ ### Background Push Notification Handling
181
+
182
+ #### `handlePushPayload(payload: Record<string, unknown>): PushEventResult`
183
+
184
+ Process a remote push notification payload outside the WebSocket connection.
185
+
186
+ On mobile platforms, the WebSocket is killed when the app goes to background. Push notifications carry the event data directly. This method routes the payload through the same store + event logic as the live events channel.
187
+
188
+ ```typescript
189
+ // In your React Native push notification handler:
190
+ function onPushNotification(notification: { data: Record<string, unknown> }) {
191
+ const result = client.handlePushPayload(notification.data)
192
+ if (result.handled) {
193
+ console.log(`Processed ${result.event}`)
194
+ }
195
+ }
196
+ ```
197
+
198
+ **`PushEventResult`:**
199
+
200
+ ```typescript
201
+ interface PushEventResult {
202
+ handled: boolean
203
+ event?: ChatEventName
204
+ data?: Record<string, unknown>
205
+ sequence?: number
206
+ }
207
+ ```
208
+
209
+ When the WS reconnects, `joinEventsChannel` sends `{ last_sequence: N }` to avoid replaying events already seen via push notifications.
210
+
211
+ ### Sub-APIs
212
+
213
+ Each sub-API is a namespace of REST methods accessible via `client.*`:
214
+
215
+ ```typescript
216
+ client.auth // login, register, refresh, otp, oidc
217
+ client.conversations // getInbox, getConversation, startChat, close, listSupportConversations
218
+ client.messages // list, sendText, sendMedia
219
+ client.contacts // getContacts
220
+ client.notifications // list, getUnreadCount
221
+ client.upload // getUploadUrl
222
+ ```
223
+
224
+ ### Event Subscriptions
225
+
226
+ #### `on<E extends ChatEventName>(event: E, listener: ChatEventMap[E]): () => void`
227
+
228
+ Subscribe to an event. Returns an unsubscribe function.
229
+
230
+ ```typescript
231
+ const unsub = client.on("message:received", (msg) => {
232
+ console.log(msg.content)
233
+ })
234
+ // Later:
235
+ unsub()
236
+ ```
237
+
238
+ #### `off<E extends ChatEventName>(event: E, listener: ChatEventMap[E]): void`
239
+
240
+ Remove a specific listener.
241
+
242
+ ### Store
243
+
244
+ #### `store: ChatStore`
245
+
246
+ The client's in-memory store, also persisted to the storage adapter.
247
+
248
+ | Method | Returns | Description |
249
+ |--------|---------|-------------|
250
+ | `getMessages(conversationId)` | `Message[]` | Messages for a conversation |
251
+ | `getConversations()` | `Conversation[]` | All cached conversations, sorted by updated_at desc |
252
+ | `getConversation(id)` | `Conversation \| undefined` | Single conversation |
253
+ | `getContacts()` | `Contact[]` | Cached contacts |
254
+
255
+ ## Event Reference
256
+
257
+ All 14 events with their payload types:
258
+
259
+ | Event | Payload | Description |
260
+ |-------|---------|-------------|
261
+ | `message:sending` | `TempMessage` | Optimistic message created |
262
+ | `message:sent` | `Message` | Message confirmed by server (via reconciliation) |
263
+ | `message:received` | `Message` | New message from another user |
264
+ | `message:failed` | `TempMessage` | Message send failed |
265
+ | `messages:loaded` | `{ conversationId, messages }` | Message history from `fetchMessages` |
266
+ | `conversation:created` | `{ conversation }` | New conversation from events channel |
267
+ | `conversation:updated` | `{ conversation }` | Conversation updated |
268
+ | `unread_count` | `{ conversationId, unread_count }` | Unread count changed |
269
+ | `typing` | `{ conversationId, userId }` | User is typing |
270
+ | `messages_read` | `{ conversationId, userId, readAt }` | Messages read receipt |
271
+ | `messages_delivered` | `{ conversationId, userId }` | Messages delivered receipt |
272
+ | `presence` | `{ conversationId, users }` | Presence state change |
273
+ | `connection:status` | `"connected" \| "disconnected" \| "reconnecting"` | WS connection state |
274
+ | `auth:expired` | `void` | Token expired (onTokenExpired returned null) |
275
+ | `error` | `Error` | Internal error |
276
+
277
+ ## Storage Adapters
278
+
279
+ Implement the `ChatStorage` interface for your platform:
280
+
281
+ ```typescript
282
+ interface ChatStorage {
283
+ getItem<T = unknown>(key: string): Promise<T | null>
284
+ setItem<T = unknown>(key: string, value: T): Promise<void>
285
+ removeItem(key: string): Promise<void>
286
+ clear(): Promise<void>
287
+ }
288
+ ```
289
+
290
+ **React Native (AsyncStorage):**
291
+
292
+ ```typescript
293
+ import AsyncStorage from "@react-native-async-storage/async-storage"
294
+
295
+ const storage: ChatStorage = {
296
+ getItem: (key) => AsyncStorage.getItem(key).then((v) => v ? JSON.parse(v) : null),
297
+ setItem: (key, value) => AsyncStorage.setItem(key, JSON.stringify(value)),
298
+ removeItem: (key) => AsyncStorage.removeItem(key),
299
+ clear: () => AsyncStorage.clear(),
300
+ }
301
+ ```
302
+
303
+ **React Native (MMKV):**
304
+
305
+ ```typescript
306
+ import { MMKV } from "react-native-mmkv"
307
+
308
+ const mmkv = new MMKV()
309
+
310
+ const storage: ChatStorage = {
311
+ getItem: async (key) => {
312
+ const v = mmkv.getString(key)
313
+ return v ? JSON.parse(v) : null
314
+ },
315
+ setItem: async (key, value) => mmkv.set(key, JSON.stringify(value)),
316
+ removeItem: async (key) => mmkv.delete(key),
317
+ clear: async () => mmkv.clearAll(),
318
+ }
319
+ ```
320
+
321
+ ## React Hooks
322
+
323
+ Optional React hooks for component-level state:
324
+
325
+ ```typescript
326
+ import { useChat, useConversations, useContacts } from "teincfood-chat-client"
327
+
328
+ function ChatScreen() {
329
+ const { messages, send } = useChat("conv-1")
330
+ const { conversations } = useConversations()
331
+ const { contacts } = useContacts()
332
+
333
+ return (
334
+ <FlatList
335
+ data={messages}
336
+ renderItem={({ item }) => <Text>{item.content}</Text>}
337
+ />
338
+ )
339
+ }
340
+ ```
341
+
342
+ Hooks use `useSyncExternalStore` under the hood — no `useEffect` polling, purely event-driven.
343
+
344
+ ## Background / Foreground Pattern
345
+
346
+ When the app goes to background on iOS/Android, the WebSocket is killed. Push notifications deliver event data directly:
347
+
348
+ ```
349
+ App in foreground → WS connected → events arrive in real-time
350
+ App in background → WS killed → push notification arrives
351
+ App tapped / opened → WS reconnects → events channel joins with last_sequence
352
+ ```
353
+
354
+ ```typescript
355
+ // Push notification handler (runs in background)
356
+ function onPushNotification(notification) {
357
+ const result = client.handlePushPayload(notification.data)
358
+ if (result.handled && result.event === "message:received") {
359
+ // Show a local notification
360
+ }
361
+ }
362
+
363
+ // App foreground handler
364
+ function onAppForeground() {
365
+ // WS reconnects automatically via start()
366
+ // Events channel uses stored last_sequence to skip duplicates
367
+ }
368
+ ```
369
+
370
+ ## Error Handling
371
+
372
+ ```typescript
373
+ // HTTP errors (REST calls)
374
+ try {
375
+ await client.loadInbox()
376
+ } catch (err) {
377
+ if (err instanceof HttpError) {
378
+ console.error(err.status, err.message) // e.g. 401, 404, 500
379
+ }
380
+ }
381
+
382
+ // Auth expiry
383
+ client.on("auth:expired", () => {
384
+ // Redirect to login
385
+ })
386
+
387
+ // WS connection changes
388
+ client.on("connection:status", (status) => {
389
+ if (status === "disconnected") {
390
+ // Show offline indicator
391
+ }
392
+ })
393
+ ```
394
+
395
+ ## TypeScript Types
396
+
397
+ All types are exported from the package:
398
+
399
+ ```typescript
400
+ import type {
401
+ Message, TempMessage,
402
+ Conversation, Contact,
403
+ AuthTokens, ChatStorage,
404
+ ChatEventMap, ChatEventName,
405
+ ChatClientOptions,
406
+ PushEventResult,
407
+ MessagesReadPayload,
408
+ PresencePayload,
409
+ ConversationCreatedPayload,
410
+ ConversationUpdatedPayload,
411
+ } from "teincfood-chat-client"
412
+ ```
413
+
414
+ ## Utils
415
+
416
+ ```typescript
417
+ import { toMessage, toConversation, retry, HttpError } from "teincfood-chat-client"
418
+ ```
419
+
420
+ - `toMessage(raw)` — parse a raw payload into a typed `Message`
421
+ - `toConversation(raw)` — parse a raw payload into a typed `Conversation`
422
+ - `retry(fn, options?)` — retry an async function with exponential backoff
423
+ - `HttpError` — HTTP error class with `status`, `statusText`, `detail`
424
+
425
+ ## Development
426
+
427
+ ```bash
428
+ npm run build # TypeScript compile
429
+ npx vitest run # Run all tests
430
+ npx vitest run --reporter=verbose # Verbose output
431
+ npm test # Alias for vitest
432
+ ```
433
+
434
+ ### Test structure
435
+
436
+ - `src/unit.test.ts` — 32 unit tests: store, socket events, push payload, optimistic engine, emitter
437
+ - `src/integration.test.ts` — 6 integration tests against localhost:4000: presence, send/receive, auto-mark-read, auth expiry, error handling
@@ -0,0 +1,13 @@
1
+ import type { HttpClient } from "../http";
2
+ import type { AuthTokens, LoginParams, RegisterParams, OtpSendParams, OtpVerifyParams, OidcParams } from "../types";
3
+ export declare function createAuthApi(http: HttpClient): {
4
+ login: (params: LoginParams) => Promise<AuthTokens>;
5
+ register: (params: RegisterParams) => Promise<AuthTokens>;
6
+ refresh: (refreshToken: string) => Promise<AuthTokens>;
7
+ sendOtp: (params: OtpSendParams) => Promise<{
8
+ success: boolean;
9
+ }>;
10
+ verifyOtp: (params: OtpVerifyParams) => Promise<AuthTokens>;
11
+ oidc: (params: OidcParams) => Promise<AuthTokens>;
12
+ };
13
+ //# sourceMappingURL=auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,cAAc,EACd,aAAa,EACb,eAAe,EACf,UAAU,EACX,MAAM,UAAU,CAAA;AAMjB,wBAAgB,aAAa,CAAC,IAAI,EAAE,UAAU;oBAE1B,WAAW;uBAGR,cAAc;4BAKT,MAAM;sBAOZ,aAAa;iBACR,OAAO;;wBAEV,eAAe;mBAKpB,UAAU;EAK5B"}
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createAuthApi = createAuthApi;
4
+ function authResponse(res) {
5
+ return res.data;
6
+ }
7
+ function createAuthApi(http) {
8
+ return {
9
+ login: (params) => http.post("/auth/login", params).then(authResponse),
10
+ register: (params) => http
11
+ .post("/auth/register", params)
12
+ .then(authResponse),
13
+ refresh: (refreshToken) => http
14
+ .post("/auth/refresh", {
15
+ refresh_token: refreshToken,
16
+ })
17
+ .then(authResponse),
18
+ sendOtp: (params) => http.post("/auth/otp/send", params),
19
+ verifyOtp: (params) => http
20
+ .post("/auth/otp/verify", params)
21
+ .then(authResponse),
22
+ oidc: (params) => http
23
+ .post("/auth/oidc", params)
24
+ .then(authResponse),
25
+ };
26
+ }
27
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/api/auth.ts"],"names":[],"mappings":";;AAcA,sCA8BC;AAlCD,SAAS,YAAY,CAAC,GAAyB;IAC7C,OAAO,GAAG,CAAC,IAAI,CAAA;AACjB,CAAC;AAED,SAAgB,aAAa,CAAC,IAAgB;IAC5C,OAAO;QACL,KAAK,EAAE,CAAC,MAAmB,EAAE,EAAE,CAC7B,IAAI,CAAC,IAAI,CAAuB,aAAa,EAAE,MAAM,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC;QAE3E,QAAQ,EAAE,CAAC,MAAsB,EAAE,EAAE,CACnC,IAAI;aACD,IAAI,CAAuB,gBAAgB,EAAE,MAAM,CAAC;aACpD,IAAI,CAAC,YAAY,CAAC;QAEvB,OAAO,EAAE,CAAC,YAAoB,EAAE,EAAE,CAChC,IAAI;aACD,IAAI,CAAuB,eAAe,EAAE;YAC3C,aAAa,EAAE,YAAY;SAC5B,CAAC;aACD,IAAI,CAAC,YAAY,CAAC;QAEvB,OAAO,EAAE,CAAC,MAAqB,EAAE,EAAE,CACjC,IAAI,CAAC,IAAI,CAAuB,gBAAgB,EAAE,MAAM,CAAC;QAE3D,SAAS,EAAE,CAAC,MAAuB,EAAE,EAAE,CACrC,IAAI;aACD,IAAI,CAAuB,kBAAkB,EAAE,MAAM,CAAC;aACtD,IAAI,CAAC,YAAY,CAAC;QAEvB,IAAI,EAAE,CAAC,MAAkB,EAAE,EAAE,CAC3B,IAAI;aACD,IAAI,CAAuB,YAAY,EAAE,MAAM,CAAC;aAChD,IAAI,CAAC,YAAY,CAAC;KACxB,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { HttpClient } from "../http";
2
+ import type { Contact, ContactContext } from "../types";
3
+ export declare function createContactsApi(http: HttpClient): {
4
+ getContacts: (context?: ContactContext, businessId?: string, q?: string) => Promise<Contact[]>;
5
+ };
6
+ //# sourceMappingURL=contacts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contacts.d.ts","sourceRoot":"","sources":["../../src/api/contacts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAEvD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU;4BAGlC,cAAc,eACX,MAAM,MACf,MAAM;EAYf"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createContactsApi = createContactsApi;
4
+ function createContactsApi(http) {
5
+ return {
6
+ getContacts: (context, businessId, q) => {
7
+ const params = new URLSearchParams();
8
+ if (context)
9
+ params.set("context", context);
10
+ if (businessId)
11
+ params.set("business_id", businessId);
12
+ if (q)
13
+ params.set("q", q);
14
+ const qs = params.toString();
15
+ return http
16
+ .get(`/contacts${qs ? `?${qs}` : ""}`)
17
+ .then((r) => r.data);
18
+ },
19
+ };
20
+ }
21
+ //# sourceMappingURL=contacts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contacts.js","sourceRoot":"","sources":["../../src/api/contacts.ts"],"names":[],"mappings":";;AAGA,8CAiBC;AAjBD,SAAgB,iBAAiB,CAAC,IAAgB;IAChD,OAAO;QACL,WAAW,EAAE,CACX,OAAwB,EACxB,UAAmB,EACnB,CAAU,EACV,EAAE;YACF,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAA;YACpC,IAAI,OAAO;gBAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;YAC3C,IAAI,UAAU;gBAAE,MAAM,CAAC,GAAG,CAAC,aAAa,EAAE,UAAU,CAAC,CAAA;YACrD,IAAI,CAAC;gBAAE,MAAM,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,CAAA;YACzB,MAAM,EAAE,GAAG,MAAM,CAAC,QAAQ,EAAE,CAAA;YAC5B,OAAO,IAAI;iBACR,GAAG,CAAsB,YAAY,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC;iBAC1D,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ import type { HttpClient } from "../http";
2
+ import type { Conversation, PaginationMeta } from "../types";
3
+ export interface StartChatParams {
4
+ contact_type?: "user" | "business" | "support";
5
+ contact_id?: string | null;
6
+ order_id?: string | null;
7
+ }
8
+ export declare function createConversationsApi(http: HttpClient): {
9
+ getInbox: () => Promise<Conversation[]>;
10
+ getConversation: (id: string) => Promise<Conversation>;
11
+ startChat: (params: StartChatParams) => Promise<Conversation>;
12
+ startSupportChat: () => Promise<Conversation>;
13
+ close: (id: string) => Promise<{
14
+ success: boolean;
15
+ }>;
16
+ listSupportConversations: (page?: number, pageSize?: number) => Promise<{
17
+ data: Conversation[];
18
+ pagination: PaginationMeta;
19
+ }>;
20
+ };
21
+ //# sourceMappingURL=conversations.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversations.d.ts","sourceRoot":"","sources":["../../src/api/conversations.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AAE5D,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,GAAG,UAAU,GAAG,SAAS,CAAA;IAC9C,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC1B,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU;;0BAO7B,MAAM;wBAKR,eAAe;;gBAUvB,MAAM;iBAEY,OAAO;;;cAK3B,YAAY,EAAE;oBACR,cAAc;;EAGjC"}
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createConversationsApi = createConversationsApi;
4
+ function createConversationsApi(http) {
5
+ return {
6
+ getInbox: () => http
7
+ .get("/inbox")
8
+ .then((r) => r.data),
9
+ getConversation: (id) => http
10
+ .get(`/conversations/${id}`)
11
+ .then((r) => r.data),
12
+ startChat: (params) => http
13
+ .post("/chats", params)
14
+ .then((r) => r.data),
15
+ startSupportChat: () => http
16
+ .post("/chats/support")
17
+ .then((r) => r.data),
18
+ close: (id) => http
19
+ .patch(`/conversations/${id}/close`)
20
+ .then((r) => r.data),
21
+ listSupportConversations: (page = 1, pageSize = 20) => http.get(`/chats?page=${page}&page_size=${pageSize}`),
22
+ };
23
+ }
24
+ //# sourceMappingURL=conversations.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversations.js","sourceRoot":"","sources":["../../src/api/conversations.ts"],"names":[],"mappings":";;AASA,wDAiCC;AAjCD,SAAgB,sBAAsB,CAAC,IAAgB;IACrD,OAAO;QACL,QAAQ,EAAE,GAAG,EAAE,CACb,IAAI;aACD,GAAG,CAA2B,QAAQ,CAAC;aACvC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,eAAe,EAAE,CAAC,EAAU,EAAE,EAAE,CAC9B,IAAI;aACD,GAAG,CAAyB,kBAAkB,EAAE,EAAE,CAAC;aACnD,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,SAAS,EAAE,CAAC,MAAuB,EAAE,EAAE,CACrC,IAAI;aACD,IAAI,CAAyB,QAAQ,EAAE,MAAM,CAAC;aAC9C,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,gBAAgB,EAAE,GAAG,EAAE,CACrB,IAAI;aACD,IAAI,CAAyB,gBAAgB,CAAC;aAC9C,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,KAAK,EAAE,CAAC,EAAU,EAAE,EAAE,CACpB,IAAI;aACD,KAAK,CAAiC,kBAAkB,EAAE,QAAQ,CAAC;aACnE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,wBAAwB,EAAE,CAAC,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAE,EAAE,CACpD,IAAI,CAAC,GAAG,CAGL,eAAe,IAAI,cAAc,QAAQ,EAAE,CAAC;KAClD,CAAA;AACH,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { HttpClient } from "../http";
2
+ import type { Message, MessageListMeta, MessageType } from "../types";
3
+ export declare function createMessagesApi(http: HttpClient): {
4
+ list: (conversationId: string, limit?: number, offset?: number) => Promise<{
5
+ data: Message[];
6
+ meta: MessageListMeta;
7
+ }>;
8
+ sendText: (conversationId: string, body: string) => Promise<Message>;
9
+ sendMedia: (conversationId: string, key: string, messageType: MessageType, caption?: string) => Promise<Message>;
10
+ };
11
+ //# sourceMappingURL=messages.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/api/messages.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,OAAO,EAAE,eAAe,EAAE,WAAW,EAAE,MAAM,UAAU,CAAA;AAErE,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU;2BAEvB,MAAM;cAGjB,OAAO,EAAE;cACT,eAAe;;+BAMA,MAAM,QAAQ,MAAM;gCAS7B,MAAM,OACjB,MAAM,eACE,WAAW,YACd,MAAM;EAarB"}
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createMessagesApi = createMessagesApi;
4
+ function createMessagesApi(http) {
5
+ return {
6
+ list: (conversationId, limit = 50, offset = 0) => http
7
+ .get(`/conversations/${conversationId}/messages?limit=${limit}&offset=${offset}`)
8
+ .then((r) => r),
9
+ sendText: (conversationId, body) => http
10
+ .post(`/conversations/${conversationId}/messages`, { body })
11
+ .then((r) => r.data),
12
+ sendMedia: (conversationId, key, messageType, caption) => http
13
+ .post(`/conversations/${conversationId}/messages/media`, {
14
+ key,
15
+ message_type: messageType,
16
+ caption,
17
+ })
18
+ .then((r) => r.data),
19
+ };
20
+ }
21
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/api/messages.ts"],"names":[],"mappings":";;AAGA,8CAqCC;AArCD,SAAgB,iBAAiB,CAAC,IAAgB;IAChD,OAAO;QACL,IAAI,EAAE,CAAC,cAAsB,EAAE,KAAK,GAAG,EAAE,EAAE,MAAM,GAAG,CAAC,EAAE,EAAE,CACvD,IAAI;aACD,GAAG,CAIF,kBAAkB,cAAc,mBAAmB,KAAK,WAAW,MAAM,EAAE,CAC5E;aACA,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;QAEnB,QAAQ,EAAE,CAAC,cAAsB,EAAE,IAAY,EAAE,EAAE,CACjD,IAAI;aACD,IAAI,CACH,kBAAkB,cAAc,WAAW,EAC3C,EAAE,IAAI,EAAE,CACT;aACA,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAExB,SAAS,EAAE,CACT,cAAsB,EACtB,GAAW,EACX,WAAwB,EACxB,OAAgB,EAChB,EAAE,CACF,IAAI;aACD,IAAI,CACH,kBAAkB,cAAc,iBAAiB,EACjD;YACE,GAAG;YACH,YAAY,EAAE,WAAW;YACzB,OAAO;SACR,CACF;aACA,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;KACzB,CAAA;AACH,CAAC"}
@@ -0,0 +1,15 @@
1
+ import type { HttpClient } from "../http";
2
+ import type { AppNotification } from "../types";
3
+ export declare function createNotificationsApi(http: HttpClient): {
4
+ list: (page?: number, pageSize?: number) => Promise<{
5
+ data: AppNotification[];
6
+ pagination: {
7
+ page: number;
8
+ page_size: number;
9
+ total_entries: number;
10
+ total_pages: number;
11
+ };
12
+ }>;
13
+ unreadCount: () => Promise<number>;
14
+ };
15
+ //# sourceMappingURL=notifications.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications.d.ts","sourceRoot":"","sources":["../../src/api/notifications.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE/C,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,UAAU;;cAIzC,eAAe,EAAE;oBACX;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,SAAS,EAAE,MAAM,CAAC;YAAC,aAAa,EAAE,MAAM,CAAC;YAAC,WAAW,EAAE,MAAM,CAAA;SAAE;;;EAQlG"}
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createNotificationsApi = createNotificationsApi;
4
+ function createNotificationsApi(http) {
5
+ return {
6
+ list: (page = 1, pageSize = 20) => http.get(`/notifications?page=${page}&page_size=${pageSize}`),
7
+ unreadCount: () => http
8
+ .get("/notifications/unread-count")
9
+ .then((r) => r.data.count),
10
+ };
11
+ }
12
+ //# sourceMappingURL=notifications.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notifications.js","sourceRoot":"","sources":["../../src/api/notifications.ts"],"names":[],"mappings":";;AAGA,wDAaC;AAbD,SAAgB,sBAAsB,CAAC,IAAgB;IACrD,OAAO;QACL,IAAI,EAAE,CAAC,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAE,EAAE,CAChC,IAAI,CAAC,GAAG,CAGL,uBAAuB,IAAI,cAAc,QAAQ,EAAE,CAAC;QAEzD,WAAW,EAAE,GAAG,EAAE,CAChB,IAAI;aACD,GAAG,CAA8B,6BAA6B,CAAC;aAC/D,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC;KAC/B,CAAA;AACH,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { HttpClient } from "../http";
2
+ import type { UploadUrlResult } from "../types";
3
+ export declare function createUploadApi(http: HttpClient): {
4
+ getUploadUrl: (fileName: string, contentType: string, purpose?: "chat" | "avatar" | "kyc") => Promise<UploadUrlResult>;
5
+ };
6
+ //# sourceMappingURL=upload.d.ts.map