@mailoo/chat 0.0.0-stage → 1.32.2

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 (58) hide show
  1. package/LICENSE +16 -0
  2. package/README.md +150 -2
  3. package/dist/chat-message-Dw6tKBEb.d.cts +10 -0
  4. package/dist/chat-message-Dw6tKBEb.d.ts +10 -0
  5. package/dist/chunk-2Y37RYEL.js +141 -0
  6. package/dist/chunk-2Y37RYEL.js.map +1 -0
  7. package/dist/chunk-4TNHJERE.js +19 -0
  8. package/dist/chunk-4TNHJERE.js.map +1 -0
  9. package/dist/chunk-77E6HUHY.js +41 -0
  10. package/dist/chunk-77E6HUHY.js.map +1 -0
  11. package/dist/chunk-AU6EFFLV.js +431 -0
  12. package/dist/chunk-AU6EFFLV.js.map +1 -0
  13. package/dist/chunk-QZ7HWQDO.js +839 -0
  14. package/dist/chunk-QZ7HWQDO.js.map +1 -0
  15. package/dist/client/index.cjs +1658 -0
  16. package/dist/client/index.cjs.map +1 -0
  17. package/dist/client/index.d.cts +33 -0
  18. package/dist/client/index.d.ts +33 -0
  19. package/dist/client/index.js +597 -0
  20. package/dist/client/index.js.map +1 -0
  21. package/dist/hooks/index.cjs +751 -0
  22. package/dist/hooks/index.cjs.map +1 -0
  23. package/dist/hooks/index.d.cts +79 -0
  24. package/dist/hooks/index.d.ts +79 -0
  25. package/dist/hooks/index.js +32 -0
  26. package/dist/hooks/index.js.map +1 -0
  27. package/dist/index.cjs +213 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +53 -0
  30. package/dist/index.d.ts +53 -0
  31. package/dist/index.js +27 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/mailoo-site-chat-widget-C7B797Lu.d.ts +130 -0
  34. package/dist/mailoo-site-chat-widget-h_cBqsJw.d.cts +130 -0
  35. package/dist/routes/index.cjs +204 -0
  36. package/dist/routes/index.cjs.map +1 -0
  37. package/dist/routes/index.d.cts +30 -0
  38. package/dist/routes/index.d.ts +30 -0
  39. package/dist/routes/index.js +174 -0
  40. package/dist/routes/index.js.map +1 -0
  41. package/dist/shared-glVmghqk.d.cts +40 -0
  42. package/dist/shared-glVmghqk.d.ts +40 -0
  43. package/dist/styles/widget.css +122 -0
  44. package/dist/welcome-presets/index.cjs +479 -0
  45. package/dist/welcome-presets/index.cjs.map +1 -0
  46. package/dist/welcome-presets/index.d.cts +53 -0
  47. package/dist/welcome-presets/index.d.ts +53 -0
  48. package/dist/welcome-presets/index.js +38 -0
  49. package/dist/welcome-presets/index.js.map +1 -0
  50. package/dist/welcome-presets/parse.cjs +44 -0
  51. package/dist/welcome-presets/parse.cjs.map +1 -0
  52. package/dist/welcome-presets/parse.d.cts +11 -0
  53. package/dist/welcome-presets/parse.d.ts +11 -0
  54. package/dist/welcome-presets/parse.js +9 -0
  55. package/dist/welcome-presets/parse.js.map +1 -0
  56. package/dist/widget-config-B5Tf7gqn.d.cts +32 -0
  57. package/dist/widget-config-B5Tf7gqn.d.ts +32 -0
  58. package/package.json +85 -3
@@ -0,0 +1,751 @@
1
+ "use strict";
2
+ "use client";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/hooks/index.ts
22
+ var hooks_exports = {};
23
+ __export(hooks_exports, {
24
+ DEFAULT_CHAT_GUEST_PROFILE_KEY: () => DEFAULT_CHAT_GUEST_PROFILE_KEY,
25
+ DEFAULT_CHAT_MESSAGES_ENDPOINT: () => DEFAULT_CHAT_MESSAGES_ENDPOINT,
26
+ DEFAULT_CHAT_POLL_INTERVAL_MS: () => DEFAULT_CHAT_POLL_INTERVAL_MS,
27
+ DEFAULT_CHAT_SESSION_STORAGE_KEY: () => DEFAULT_CHAT_SESSION_STORAGE_KEY,
28
+ DEFAULT_CHAT_STATUS_ENDPOINT: () => DEFAULT_CHAT_STATUS_ENDPOINT,
29
+ DEFAULT_CHAT_SUBMIT_ENDPOINT: () => DEFAULT_CHAT_SUBMIT_ENDPOINT,
30
+ classifyChatFault: () => classifyChatFault,
31
+ isChatSessionNotFound: () => isChatSessionNotFound,
32
+ messageFromPayload: () => messageFromPayload,
33
+ useMailooChatMessages: () => useMailooChatMessages,
34
+ useMailooChatSession: () => useMailooChatSession,
35
+ useMailooChatStatus: () => useMailooChatStatus,
36
+ useMailooChatSubmit: () => useMailooChatSubmit
37
+ });
38
+ module.exports = __toCommonJS(hooks_exports);
39
+ var import_react2 = require("react");
40
+
41
+ // src/lib/chat-message.ts
42
+ function parseChatMessages(rows) {
43
+ const parsed = [];
44
+ for (const row of rows) {
45
+ if (!row || typeof row !== "object") continue;
46
+ const r = row;
47
+ if (typeof r.id !== "string" || typeof r.content !== "string") continue;
48
+ parsed.push({
49
+ id: r.id,
50
+ messageType: typeof r.messageType === "string" ? r.messageType : "INBOUND",
51
+ content: r.content,
52
+ senderEmail: typeof r.senderEmail === "string" ? r.senderEmail : null,
53
+ senderName: typeof r.senderName === "string" ? r.senderName : null,
54
+ createdAt: typeof r.createdAt === "string" ? r.createdAt : (/* @__PURE__ */ new Date()).toISOString()
55
+ });
56
+ }
57
+ return parsed;
58
+ }
59
+ function messagesEqual(a, b) {
60
+ if (a.length !== b.length) return false;
61
+ for (let i = 0; i < a.length; i++) {
62
+ const x = a[i];
63
+ const y = b[i];
64
+ if (x.id !== y.id || x.content !== y.content || x.messageType !== y.messageType || x.createdAt !== y.createdAt || x.senderEmail !== y.senderEmail || x.senderName !== y.senderName) {
65
+ return false;
66
+ }
67
+ }
68
+ return true;
69
+ }
70
+ function mergeChatMessages(prev, incoming) {
71
+ if (incoming.length === 0) return prev;
72
+ const byId = /* @__PURE__ */ new Map();
73
+ for (const m of prev) byId.set(m.id, m);
74
+ for (const m of incoming) byId.set(m.id, m);
75
+ const next = Array.from(byId.values()).sort(
76
+ (a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()
77
+ );
78
+ return messagesEqual(prev, next) ? prev : next;
79
+ }
80
+ function latestMessageCreatedAt(messages) {
81
+ if (messages.length === 0) return void 0;
82
+ let latest = messages[0].createdAt;
83
+ for (const m of messages) {
84
+ if (new Date(m.createdAt).getTime() > new Date(latest).getTime()) {
85
+ latest = m.createdAt;
86
+ }
87
+ }
88
+ return latest;
89
+ }
90
+
91
+ // src/hooks/shared.ts
92
+ var DEFAULT_CHAT_SUBMIT_ENDPOINT = "/api/v1/webhooks/chat/submit";
93
+ var DEFAULT_CHAT_MESSAGES_ENDPOINT = "/api/v1/webhooks/chat/messages";
94
+ var DEFAULT_CHAT_STATUS_ENDPOINT = "/api/v1/webhooks/chat/status";
95
+ var DEFAULT_CHAT_SESSION_STORAGE_KEY = "mailoo_site_chat_session_public_id";
96
+ var DEFAULT_CHAT_GUEST_PROFILE_KEY = "mailoo_site_chat_guest_profile";
97
+ var DEFAULT_CHAT_POLL_INTERVAL_MS = 5e3;
98
+ function messageFromPayload(data, fallback) {
99
+ if (!data || typeof data !== "object") return fallback;
100
+ const d = data;
101
+ if (typeof d.message === "string") return d.message;
102
+ if (typeof d.error === "string") return d.error;
103
+ return fallback;
104
+ }
105
+ function classifyChatFault(status, payload, fallbackMessage = "Request failed") {
106
+ if (status === 429) {
107
+ return {
108
+ kind: "rate_limited",
109
+ status,
110
+ message: messageFromPayload(payload, fallbackMessage)
111
+ };
112
+ }
113
+ if (status === 0 || status === 502 || status === 503 || status >= 500) {
114
+ return {
115
+ kind: "unavailable",
116
+ status,
117
+ message: messageFromPayload(payload, fallbackMessage)
118
+ };
119
+ }
120
+ return null;
121
+ }
122
+ function isChatSessionNotFound(status, data) {
123
+ if (status !== 404) return false;
124
+ const text = messageFromPayload(data, "").toLowerCase();
125
+ return text.includes("chat session not found");
126
+ }
127
+ function persistSessionPublicId(id, storageKey, ctx) {
128
+ const created = !ctx.sessionPublicIdRef.current;
129
+ ctx.setSessionPublicId(id);
130
+ ctx.sessionPublicIdRef.current = id;
131
+ try {
132
+ sessionStorage.setItem(storageKey, id);
133
+ } catch {
134
+ }
135
+ if (created) ctx.onCreated?.(id);
136
+ }
137
+
138
+ // src/hooks/use-mailoo-chat-session.ts
139
+ var import_react = require("react");
140
+
141
+ // src/lib/guest-copy.ts
142
+ function isValidEmail(s) {
143
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s.trim());
144
+ }
145
+
146
+ // src/lib/visitor-ws-url.ts
147
+ function buildVisitorWsUrl(apiOrigin, projectUid, integrationId, sessionPublicId) {
148
+ const u = new URL(apiOrigin);
149
+ const wsProto = u.protocol === "https:" ? "wss:" : "ws:";
150
+ const qs = new URLSearchParams({
151
+ projectUid,
152
+ integrationId,
153
+ sessionPublicId
154
+ });
155
+ return `${wsProto}//${u.host}/api/v1/chat/visitor-ws?${qs.toString()}`;
156
+ }
157
+
158
+ // src/hooks/use-mailoo-chat-session.ts
159
+ function useMailooChatSession(options) {
160
+ const {
161
+ apiOrigin,
162
+ projectUid,
163
+ integrationId,
164
+ isAuthenticated,
165
+ submitEndpoint = DEFAULT_CHAT_SUBMIT_ENDPOINT,
166
+ messagesEndpoint = DEFAULT_CHAT_MESSAGES_ENDPOINT,
167
+ storageKey = DEFAULT_CHAT_SESSION_STORAGE_KEY,
168
+ guestProfileKey = DEFAULT_CHAT_GUEST_PROFILE_KEY,
169
+ pollIntervalMs = DEFAULT_CHAT_POLL_INTERVAL_MS,
170
+ enableRealtime = true,
171
+ enablePolling = true,
172
+ enableWebSocket = true,
173
+ enabled = true,
174
+ onSessionCreated,
175
+ onMessageSent,
176
+ onError,
177
+ onFault
178
+ } = options;
179
+ const [sessionPublicId, setSessionPublicId] = (0, import_react.useState)(null);
180
+ const [messages, setMessages] = (0, import_react.useState)([]);
181
+ const [sending, setSending] = (0, import_react.useState)(false);
182
+ const [error, setError] = (0, import_react.useState)(null);
183
+ const [fault, setFault] = (0, import_react.useState)(null);
184
+ const [connectionState, setConnectionState] = (0, import_react.useState)("idle");
185
+ const [guestEmail, setGuestEmail] = (0, import_react.useState)("");
186
+ const [guestName, setGuestName] = (0, import_react.useState)("");
187
+ const [guestContactOk, setGuestContactOk] = (0, import_react.useState)(isAuthenticated);
188
+ const [guestProfileHydrated, setGuestProfileHydrated] = (0, import_react.useState)(false);
189
+ const wsRef = (0, import_react.useRef)(null);
190
+ const pollRef = (0, import_react.useRef)(null);
191
+ const sessionPublicIdRef = (0, import_react.useRef)(null);
192
+ const sinceRef = (0, import_react.useRef)(void 0);
193
+ const faultRef = (0, import_react.useRef)(null);
194
+ const onSessionCreatedRef = (0, import_react.useRef)(onSessionCreated);
195
+ const onMessageSentRef = (0, import_react.useRef)(onMessageSent);
196
+ const onErrorRef = (0, import_react.useRef)(onError);
197
+ const onFaultRef = (0, import_react.useRef)(onFault);
198
+ sessionPublicIdRef.current = sessionPublicId;
199
+ faultRef.current = fault;
200
+ onSessionCreatedRef.current = onSessionCreated;
201
+ onMessageSentRef.current = onMessageSent;
202
+ onErrorRef.current = onError;
203
+ onFaultRef.current = onFault;
204
+ const clearFault = (0, import_react.useCallback)(() => {
205
+ faultRef.current = null;
206
+ setFault(null);
207
+ }, []);
208
+ const reportFault = (0, import_react.useCallback)((next) => {
209
+ faultRef.current = next;
210
+ setFault(next);
211
+ setError(null);
212
+ onFaultRef.current?.(next);
213
+ onErrorRef.current?.(next.message);
214
+ }, []);
215
+ const reportError = (0, import_react.useCallback)((message) => {
216
+ setError(message);
217
+ if (message) {
218
+ clearFault();
219
+ onErrorRef.current?.(message);
220
+ }
221
+ }, [clearFault]);
222
+ (0, import_react.useEffect)(() => {
223
+ if (typeof window === "undefined") return;
224
+ const existing = sessionStorage.getItem(storageKey);
225
+ if (existing) setSessionPublicId(existing);
226
+ }, [storageKey]);
227
+ (0, import_react.useEffect)(() => {
228
+ if (typeof window === "undefined" || isAuthenticated) {
229
+ setGuestProfileHydrated(true);
230
+ return;
231
+ }
232
+ try {
233
+ const raw = sessionStorage.getItem(guestProfileKey);
234
+ if (raw) {
235
+ const j = JSON.parse(raw);
236
+ const em = typeof j.email === "string" ? j.email.trim().toLowerCase() : "";
237
+ if (em && isValidEmail(em)) {
238
+ setGuestEmail(em);
239
+ setGuestName(typeof j.name === "string" ? j.name.trim() : "");
240
+ setGuestContactOk(true);
241
+ }
242
+ }
243
+ } catch {
244
+ }
245
+ setGuestProfileHydrated(true);
246
+ }, [isAuthenticated, guestProfileKey]);
247
+ (0, import_react.useEffect)(() => {
248
+ if (isAuthenticated) {
249
+ setGuestContactOk(true);
250
+ }
251
+ }, [isAuthenticated]);
252
+ const persistGuestProfile = (0, import_react.useCallback)(
253
+ (email, name) => {
254
+ try {
255
+ sessionStorage.setItem(
256
+ guestProfileKey,
257
+ JSON.stringify({
258
+ email: email.trim().toLowerCase(),
259
+ name: name.trim()
260
+ })
261
+ );
262
+ } catch {
263
+ }
264
+ },
265
+ [guestProfileKey]
266
+ );
267
+ const confirmGuestContact = (0, import_react.useCallback)(
268
+ (invalidEmailMessage) => {
269
+ reportError(null);
270
+ if (isAuthenticated) {
271
+ setGuestContactOk(true);
272
+ return true;
273
+ }
274
+ if (!isValidEmail(guestEmail)) {
275
+ reportError(
276
+ invalidEmailMessage ?? "Please enter a valid email address."
277
+ );
278
+ return false;
279
+ }
280
+ persistGuestProfile(guestEmail, guestName);
281
+ setGuestContactOk(true);
282
+ return true;
283
+ },
284
+ [
285
+ isAuthenticated,
286
+ guestEmail,
287
+ guestName,
288
+ persistGuestProfile,
289
+ reportError
290
+ ]
291
+ );
292
+ const resetGuestContact = (0, import_react.useCallback)(() => {
293
+ setGuestContactOk(false);
294
+ reportError(null);
295
+ }, [reportError]);
296
+ const mergeIncoming = (0, import_react.useCallback)((rows) => {
297
+ const parsed = parseChatMessages(rows);
298
+ setMessages((prev) => {
299
+ const next = mergeChatMessages(prev, parsed);
300
+ sinceRef.current = latestMessageCreatedAt(next);
301
+ return next;
302
+ });
303
+ }, []);
304
+ const mergeMessage = (0, import_react.useCallback)((message) => {
305
+ setMessages((prev) => {
306
+ const next = mergeChatMessages(prev, [message]);
307
+ sinceRef.current = latestMessageCreatedAt(next);
308
+ return next;
309
+ });
310
+ }, []);
311
+ const clearSession = (0, import_react.useCallback)(() => {
312
+ try {
313
+ sessionStorage.removeItem(storageKey);
314
+ } catch {
315
+ }
316
+ setSessionPublicId(null);
317
+ sessionPublicIdRef.current = null;
318
+ setMessages([]);
319
+ sinceRef.current = void 0;
320
+ }, [storageKey]);
321
+ const stopRealtime = (0, import_react.useCallback)(() => {
322
+ if (pollRef.current) {
323
+ clearInterval(pollRef.current);
324
+ pollRef.current = null;
325
+ }
326
+ wsRef.current?.close();
327
+ wsRef.current = null;
328
+ setConnectionState("closed");
329
+ }, []);
330
+ const refreshMessages = (0, import_react.useCallback)(
331
+ async (sid, opts) => {
332
+ const id = sid ?? sessionPublicIdRef.current;
333
+ if (!id) return;
334
+ if (faultRef.current) return;
335
+ const params = new URLSearchParams({ sessionPublicId: id });
336
+ if (!opts?.full && sinceRef.current) {
337
+ params.set("since", sinceRef.current);
338
+ }
339
+ let res;
340
+ try {
341
+ res = await fetch(`${messagesEndpoint}?${params.toString()}`);
342
+ } catch {
343
+ const next = classifyChatFault(0, null, "Network error");
344
+ if (next) {
345
+ reportFault(next);
346
+ stopRealtime();
347
+ }
348
+ return;
349
+ }
350
+ const data = await res.json().catch(() => ({}));
351
+ if (!res.ok) {
352
+ if (isChatSessionNotFound(res.status, data)) {
353
+ clearSession();
354
+ reportError(null);
355
+ return;
356
+ }
357
+ const classified = classifyChatFault(
358
+ res.status,
359
+ data,
360
+ "Failed to load messages"
361
+ );
362
+ if (classified) {
363
+ reportFault(classified);
364
+ stopRealtime();
365
+ return;
366
+ }
367
+ reportError(messageFromPayload(data, "Failed to load messages"));
368
+ return;
369
+ }
370
+ if (Array.isArray(data.data)) {
371
+ mergeIncoming(data.data);
372
+ }
373
+ },
374
+ [
375
+ messagesEndpoint,
376
+ mergeIncoming,
377
+ clearSession,
378
+ reportError,
379
+ reportFault,
380
+ stopRealtime
381
+ ]
382
+ );
383
+ (0, import_react.useEffect)(() => {
384
+ const realtimeOn = enabled && enableRealtime && guestContactOk && !fault;
385
+ if (!realtimeOn || !sessionPublicId) {
386
+ return;
387
+ }
388
+ const connectWs = () => {
389
+ if (!enableWebSocket || !apiOrigin || !projectUid || !integrationId) {
390
+ return;
391
+ }
392
+ try {
393
+ const url = buildVisitorWsUrl(
394
+ apiOrigin,
395
+ projectUid,
396
+ integrationId,
397
+ sessionPublicId
398
+ );
399
+ setConnectionState("connecting");
400
+ const ws = new WebSocket(url);
401
+ wsRef.current = ws;
402
+ ws.onopen = () => setConnectionState("open");
403
+ ws.onmessage = (ev) => {
404
+ try {
405
+ const msg = JSON.parse(ev.data);
406
+ if (msg.type !== "chat.message") return;
407
+ const p = msg.payload;
408
+ if (p && typeof p.messageId === "string" && typeof p.content === "string") {
409
+ mergeMessage({
410
+ id: p.messageId,
411
+ content: p.content,
412
+ messageType: typeof p.messageType === "string" ? p.messageType : "OUTBOUND",
413
+ senderEmail: typeof p.senderEmail === "string" ? p.senderEmail : null,
414
+ senderName: typeof p.senderName === "string" ? p.senderName : null,
415
+ createdAt: typeof p.createdAt === "string" ? p.createdAt : (/* @__PURE__ */ new Date()).toISOString()
416
+ });
417
+ }
418
+ void refreshMessages(sessionPublicId);
419
+ } catch {
420
+ }
421
+ };
422
+ ws.onclose = () => {
423
+ wsRef.current = null;
424
+ setConnectionState("closed");
425
+ };
426
+ } catch {
427
+ setConnectionState("closed");
428
+ }
429
+ };
430
+ connectWs();
431
+ void refreshMessages(sessionPublicId, { full: true });
432
+ if (enablePolling) {
433
+ pollRef.current = setInterval(() => {
434
+ void refreshMessages(sessionPublicId);
435
+ }, pollIntervalMs);
436
+ }
437
+ return () => {
438
+ if (pollRef.current) clearInterval(pollRef.current);
439
+ pollRef.current = null;
440
+ wsRef.current?.close();
441
+ wsRef.current = null;
442
+ setConnectionState("idle");
443
+ };
444
+ }, [
445
+ enabled,
446
+ enableRealtime,
447
+ enablePolling,
448
+ enableWebSocket,
449
+ guestContactOk,
450
+ fault,
451
+ sessionPublicId,
452
+ apiOrigin,
453
+ projectUid,
454
+ integrationId,
455
+ pollIntervalMs,
456
+ refreshMessages,
457
+ mergeMessage
458
+ ]);
459
+ const sendMessage = (0, import_react.useCallback)(
460
+ async (content, opts) => {
461
+ const text = content.trim();
462
+ if (!text || sending) return false;
463
+ if (faultRef.current) return false;
464
+ if (!isAuthenticated && !isValidEmail(guestEmail)) {
465
+ reportError(
466
+ opts?.invalidEmailMessage ?? "Please enter a valid email address."
467
+ );
468
+ return false;
469
+ }
470
+ setSending(true);
471
+ reportError(null);
472
+ clearFault();
473
+ try {
474
+ const buildBody = (includeSession) => {
475
+ const body = { content: text };
476
+ const pageUrl = opts?.pageUrl ?? (typeof window !== "undefined" ? window.location.href : void 0);
477
+ if (pageUrl) body.pageUrl = pageUrl;
478
+ const sid = includeSession ? sessionPublicIdRef.current : null;
479
+ if (sid) body.sessionPublicId = sid;
480
+ else if (opts?.topicId?.trim()) {
481
+ body.topicId = opts.topicId.trim();
482
+ }
483
+ if (!isAuthenticated) {
484
+ body.senderEmail = guestEmail.trim().toLowerCase();
485
+ if (guestName.trim()) body.senderName = guestName.trim();
486
+ }
487
+ return body;
488
+ };
489
+ const post = async (includeSession) => {
490
+ let res2;
491
+ try {
492
+ res2 = await fetch(submitEndpoint, {
493
+ method: "POST",
494
+ headers: { "Content-Type": "application/json" },
495
+ body: JSON.stringify(buildBody(includeSession))
496
+ });
497
+ } catch {
498
+ return {
499
+ res: null,
500
+ data: {},
501
+ networkError: true
502
+ };
503
+ }
504
+ const data2 = await res2.json().catch(() => ({}));
505
+ return { res: res2, data: data2, networkError: false };
506
+ };
507
+ let result = await post(true);
508
+ if (result.networkError) {
509
+ const next = classifyChatFault(0, null, "Network error");
510
+ if (next) {
511
+ reportFault(next);
512
+ stopRealtime();
513
+ }
514
+ return false;
515
+ }
516
+ let { res, data } = result;
517
+ if (!res) return false;
518
+ if (!res.ok && isChatSessionNotFound(res.status, data)) {
519
+ clearSession();
520
+ result = await post(false);
521
+ if (result.networkError) {
522
+ const next = classifyChatFault(0, null, "Network error");
523
+ if (next) {
524
+ reportFault(next);
525
+ stopRealtime();
526
+ }
527
+ return false;
528
+ }
529
+ ;
530
+ ({ res, data } = result);
531
+ if (!res) return false;
532
+ }
533
+ if (!res.ok) {
534
+ const classified = classifyChatFault(
535
+ res.status,
536
+ data,
537
+ "Send failed"
538
+ );
539
+ if (classified) {
540
+ reportFault(classified);
541
+ stopRealtime();
542
+ return false;
543
+ }
544
+ reportError(messageFromPayload(data, "Send failed"));
545
+ return false;
546
+ }
547
+ if (data.sessionPublicId) {
548
+ persistSessionPublicId(data.sessionPublicId, storageKey, {
549
+ sessionPublicIdRef,
550
+ setSessionPublicId,
551
+ onCreated: onSessionCreatedRef.current
552
+ });
553
+ }
554
+ if (!isAuthenticated) {
555
+ persistGuestProfile(guestEmail, guestName);
556
+ }
557
+ if (typeof data.messageId === "string" && data.messageId) {
558
+ mergeMessage({
559
+ id: data.messageId,
560
+ content: text,
561
+ messageType: "INBOUND",
562
+ senderEmail: isAuthenticated ? null : guestEmail.trim().toLowerCase(),
563
+ senderName: isAuthenticated ? null : guestName.trim() || null,
564
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
565
+ });
566
+ }
567
+ const nextSid = data.sessionPublicId ?? sessionPublicIdRef.current;
568
+ if (nextSid) {
569
+ await refreshMessages(nextSid, { full: true });
570
+ }
571
+ onMessageSentRef.current?.(text);
572
+ return true;
573
+ } catch (e) {
574
+ reportError(e instanceof Error ? e.message : "Send failed");
575
+ return false;
576
+ } finally {
577
+ setSending(false);
578
+ }
579
+ },
580
+ [
581
+ sending,
582
+ isAuthenticated,
583
+ guestEmail,
584
+ guestName,
585
+ submitEndpoint,
586
+ storageKey,
587
+ persistGuestProfile,
588
+ mergeMessage,
589
+ refreshMessages,
590
+ clearSession,
591
+ reportError,
592
+ reportFault,
593
+ clearFault,
594
+ stopRealtime
595
+ ]
596
+ );
597
+ const retryAfterFault = (0, import_react.useCallback)(() => {
598
+ clearFault();
599
+ reportError(null);
600
+ }, [clearFault, reportError]);
601
+ const canCompose = isAuthenticated || guestProfileHydrated && guestContactOk;
602
+ return {
603
+ sessionPublicId,
604
+ messages,
605
+ sending,
606
+ error,
607
+ fault,
608
+ setError: reportError,
609
+ clearFault,
610
+ retryAfterFault,
611
+ connectionState,
612
+ guestEmail,
613
+ guestName,
614
+ setGuestEmail,
615
+ setGuestName,
616
+ guestContactOk,
617
+ guestProfileHydrated,
618
+ confirmGuestContact,
619
+ resetGuestContact,
620
+ canCompose,
621
+ sendMessage,
622
+ refreshMessages,
623
+ clearSession
624
+ };
625
+ }
626
+
627
+ // src/hooks/index.ts
628
+ function useMailooChatSubmit(options) {
629
+ const endpoint = options?.endpoint ?? DEFAULT_CHAT_SUBMIT_ENDPOINT;
630
+ const [pending, setPending] = (0, import_react2.useState)(false);
631
+ const submit = (0, import_react2.useCallback)(
632
+ async (body) => {
633
+ setPending(true);
634
+ try {
635
+ const res = await fetch(endpoint, {
636
+ method: "POST",
637
+ headers: { "Content-Type": "application/json" },
638
+ body: JSON.stringify(body)
639
+ });
640
+ const data = await res.json().catch(() => ({}));
641
+ if (!res.ok) {
642
+ return {
643
+ ok: false,
644
+ status: res.status,
645
+ message: messageFromPayload(data, "Send failed")
646
+ };
647
+ }
648
+ return { ok: true, status: res.status, data };
649
+ } catch {
650
+ return { ok: false, status: 0, message: "Network error" };
651
+ } finally {
652
+ setPending(false);
653
+ }
654
+ },
655
+ [endpoint]
656
+ );
657
+ return { submit, pending };
658
+ }
659
+ function useMailooChatMessages(options) {
660
+ const endpoint = options?.endpoint ?? DEFAULT_CHAT_MESSAGES_ENDPOINT;
661
+ const [pending, setPending] = (0, import_react2.useState)(false);
662
+ const refresh = (0, import_react2.useCallback)(
663
+ async (sessionPublicId, since) => {
664
+ setPending(true);
665
+ try {
666
+ const params = new URLSearchParams({ sessionPublicId });
667
+ if (since) params.set("since", since);
668
+ const res = await fetch(`${endpoint}?${params.toString()}`);
669
+ const data = await res.json().catch(() => ({}));
670
+ if (!res.ok) {
671
+ return {
672
+ ok: false,
673
+ status: res.status,
674
+ message: messageFromPayload(data, "Fetch failed")
675
+ };
676
+ }
677
+ const rows = Array.isArray(data.data) ? data.data : [];
678
+ return {
679
+ ok: true,
680
+ status: res.status,
681
+ data,
682
+ messages: parseChatMessages(rows)
683
+ };
684
+ } catch {
685
+ return { ok: false, status: 0, message: "Network error" };
686
+ } finally {
687
+ setPending(false);
688
+ }
689
+ },
690
+ [endpoint]
691
+ );
692
+ return { refresh, pending };
693
+ }
694
+ function useMailooChatStatus(options) {
695
+ const endpoint = options?.endpoint ?? DEFAULT_CHAT_STATUS_ENDPOINT;
696
+ const enabled = options?.enabled ?? true;
697
+ const [status, setStatus] = (0, import_react2.useState)(null);
698
+ const [pending, setPending] = (0, import_react2.useState)(false);
699
+ const [error, setError] = (0, import_react2.useState)(null);
700
+ const refresh = (0, import_react2.useCallback)(async () => {
701
+ setPending(true);
702
+ setError(null);
703
+ try {
704
+ const res = await fetch(endpoint);
705
+ const data = await res.json().catch(() => ({}));
706
+ if (!res.ok) {
707
+ setError(messageFromPayload(data, "Status fetch failed"));
708
+ return null;
709
+ }
710
+ if (typeof data.status !== "string" || typeof data.name !== "string" || typeof data.type !== "string" || typeof data.lastActivity !== "string") {
711
+ setError("Invalid status payload");
712
+ return null;
713
+ }
714
+ const parsed = {
715
+ status: data.status,
716
+ name: data.name,
717
+ type: data.type,
718
+ lastActivity: data.lastActivity
719
+ };
720
+ setStatus(parsed);
721
+ return parsed;
722
+ } catch {
723
+ setError("Network error");
724
+ return null;
725
+ } finally {
726
+ setPending(false);
727
+ }
728
+ }, [endpoint]);
729
+ (0, import_react2.useEffect)(() => {
730
+ if (!enabled) return;
731
+ void refresh();
732
+ }, [enabled, refresh]);
733
+ return { status, pending, error, refresh };
734
+ }
735
+ // Annotate the CommonJS export names for ESM import in node:
736
+ 0 && (module.exports = {
737
+ DEFAULT_CHAT_GUEST_PROFILE_KEY,
738
+ DEFAULT_CHAT_MESSAGES_ENDPOINT,
739
+ DEFAULT_CHAT_POLL_INTERVAL_MS,
740
+ DEFAULT_CHAT_SESSION_STORAGE_KEY,
741
+ DEFAULT_CHAT_STATUS_ENDPOINT,
742
+ DEFAULT_CHAT_SUBMIT_ENDPOINT,
743
+ classifyChatFault,
744
+ isChatSessionNotFound,
745
+ messageFromPayload,
746
+ useMailooChatMessages,
747
+ useMailooChatSession,
748
+ useMailooChatStatus,
749
+ useMailooChatSubmit
750
+ });
751
+ //# sourceMappingURL=index.cjs.map