@scalemule/chat 0.0.5 → 0.0.7
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.
- package/dist/{ChatClient-COmdEJ11.d.ts → ChatClient-DQPHdUHX.d.cts} +21 -2
- package/dist/{ChatClient-BoZaTtyM.d.cts → ChatClient-DtUKF-4c.d.ts} +21 -2
- package/dist/chat.embed.global.js +1 -1
- package/dist/chat.umd.global.js +288 -12
- package/dist/{chunk-ZLMMNFZL.js → chunk-5O5YLRJL.js} +386 -16
- package/dist/chunk-GTMAK3IA.js +285 -0
- package/dist/chunk-TRCELAZQ.cjs +287 -0
- package/dist/{chunk-YDLRISR7.cjs → chunk-W2PWFS3E.cjs} +386 -15
- package/dist/constants.d.ts +9 -0
- package/dist/constants.d.ts.map +1 -0
- package/dist/core/ChatClient.d.ts +96 -0
- package/dist/core/ChatClient.d.ts.map +1 -0
- package/dist/core/EventEmitter.d.ts +11 -0
- package/dist/core/EventEmitter.d.ts.map +1 -0
- package/dist/core/MessageCache.d.ts +18 -0
- package/dist/core/MessageCache.d.ts.map +1 -0
- package/dist/core/OfflineQueue.d.ts +19 -0
- package/dist/core/OfflineQueue.d.ts.map +1 -0
- package/dist/element.cjs +542 -51
- package/dist/element.d.ts +2 -2
- package/dist/element.d.ts.map +1 -0
- package/dist/element.js +541 -50
- package/dist/embed/index.d.ts +2 -0
- package/dist/embed/index.d.ts.map +1 -0
- package/dist/factory.d.ts +8 -0
- package/dist/factory.d.ts.map +1 -0
- package/dist/iframe.d.cts +1 -1
- package/dist/iframe.d.ts +3 -5
- package/dist/iframe.d.ts.map +1 -0
- package/dist/index.cjs +34 -5
- package/dist/index.d.cts +93 -4
- package/dist/index.d.ts +8 -77
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -4
- package/dist/react-components/ChatInput.d.ts +16 -0
- package/dist/react-components/ChatInput.d.ts.map +1 -0
- package/dist/react-components/ChatMessageItem.d.ts +13 -0
- package/dist/react-components/ChatMessageItem.d.ts.map +1 -0
- package/dist/react-components/ChatMessageList.d.ts +15 -0
- package/dist/react-components/ChatMessageList.d.ts.map +1 -0
- package/dist/react-components/ChatThread.d.ts +12 -0
- package/dist/react-components/ChatThread.d.ts.map +1 -0
- package/dist/react-components/ConversationList.d.ts +13 -0
- package/dist/react-components/ConversationList.d.ts.map +1 -0
- package/dist/react-components/EmojiPicker.d.ts +8 -0
- package/dist/react-components/EmojiPicker.d.ts.map +1 -0
- package/dist/react-components/index.d.ts +8 -0
- package/dist/react-components/index.d.ts.map +1 -0
- package/dist/react-components/theme.d.ts +19 -0
- package/dist/react-components/theme.d.ts.map +1 -0
- package/dist/react-components/utils.d.ts +4 -0
- package/dist/react-components/utils.d.ts.map +1 -0
- package/dist/react.cjs +1212 -50
- package/dist/react.d.cts +100 -4
- package/dist/react.d.ts +38 -15
- package/dist/react.d.ts.map +1 -0
- package/dist/react.js +1164 -13
- package/dist/shared/ChatController.d.ts +65 -0
- package/dist/shared/ChatController.d.ts.map +1 -0
- package/dist/shared/upload.d.ts +3 -0
- package/dist/shared/upload.d.ts.map +1 -0
- package/dist/support-widget.global.js +485 -157
- package/dist/support.d.ts +99 -0
- package/dist/support.d.ts.map +1 -0
- package/dist/transport/HttpTransport.d.ts +20 -0
- package/dist/transport/HttpTransport.d.ts.map +1 -0
- package/dist/transport/WebSocketTransport.d.ts +78 -0
- package/dist/transport/WebSocketTransport.d.ts.map +1 -0
- package/dist/{types-BmD7f1gV.d.cts → types-COPVrm3K.d.cts} +25 -1
- package/dist/{types-BmD7f1gV.d.ts → types-COPVrm3K.d.ts} +25 -1
- package/dist/types.d.ts +271 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/umd.d.ts +6 -0
- package/dist/umd.d.ts.map +1 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/widget/icons.d.ts +8 -0
- package/dist/widget/icons.d.ts.map +1 -0
- package/dist/widget/index.d.ts +2 -0
- package/dist/widget/index.d.ts.map +1 -0
- package/dist/widget/storage.d.ts +5 -0
- package/dist/widget/storage.d.ts.map +1 -0
- package/dist/widget/styles.d.ts +3 -0
- package/dist/widget/styles.d.ts.map +1 -0
- package/package.json +5 -2
|
@@ -63,6 +63,9 @@ var MessageCache = class {
|
|
|
63
63
|
getMessages(conversationId) {
|
|
64
64
|
return this.cache.get(conversationId) ?? [];
|
|
65
65
|
}
|
|
66
|
+
getMessage(conversationId, messageId) {
|
|
67
|
+
return this.getMessages(conversationId).find((message) => message.id === messageId);
|
|
68
|
+
}
|
|
66
69
|
setMessages(conversationId, messages) {
|
|
67
70
|
this.cache.set(conversationId, messages.slice(0, this.maxMessages));
|
|
68
71
|
this.evictOldConversations();
|
|
@@ -77,6 +80,13 @@ var MessageCache = class {
|
|
|
77
80
|
}
|
|
78
81
|
this.cache.set(conversationId, messages);
|
|
79
82
|
}
|
|
83
|
+
upsertMessage(conversationId, message) {
|
|
84
|
+
if (this.getMessage(conversationId, message.id)) {
|
|
85
|
+
this.updateMessage(conversationId, message);
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
this.addMessage(conversationId, message);
|
|
89
|
+
}
|
|
80
90
|
updateMessage(conversationId, message) {
|
|
81
91
|
const messages = this.cache.get(conversationId);
|
|
82
92
|
if (!messages) return;
|
|
@@ -85,6 +95,23 @@ var MessageCache = class {
|
|
|
85
95
|
messages[idx] = message;
|
|
86
96
|
}
|
|
87
97
|
}
|
|
98
|
+
reconcileOptimisticMessage(conversationId, message) {
|
|
99
|
+
const messages = this.cache.get(conversationId);
|
|
100
|
+
if (!messages) return message;
|
|
101
|
+
const incomingAttachmentIds = (message.attachments ?? []).map((attachment) => attachment.file_id).sort();
|
|
102
|
+
const pendingIndex = messages.findIndex((cached) => {
|
|
103
|
+
if (!cached.id.startsWith("pending-")) return false;
|
|
104
|
+
if (cached.sender_id !== message.sender_id) return false;
|
|
105
|
+
if (cached.content !== message.content) return false;
|
|
106
|
+
const cachedAttachmentIds = (cached.attachments ?? []).map((attachment) => attachment.file_id).sort();
|
|
107
|
+
if (cachedAttachmentIds.length !== incomingAttachmentIds.length) return false;
|
|
108
|
+
return cachedAttachmentIds.every((fileId, index) => fileId === incomingAttachmentIds[index]);
|
|
109
|
+
});
|
|
110
|
+
if (pendingIndex >= 0) {
|
|
111
|
+
messages[pendingIndex] = message;
|
|
112
|
+
}
|
|
113
|
+
return message;
|
|
114
|
+
}
|
|
88
115
|
removeMessage(conversationId, messageId) {
|
|
89
116
|
const messages = this.cache.get(conversationId);
|
|
90
117
|
if (!messages) return;
|
|
@@ -200,8 +227,7 @@ var HttpTransport = class {
|
|
|
200
227
|
method,
|
|
201
228
|
headers,
|
|
202
229
|
body: body ? JSON.stringify(body) : void 0,
|
|
203
|
-
signal: controller.signal
|
|
204
|
-
credentials: "include"
|
|
230
|
+
signal: controller.signal
|
|
205
231
|
});
|
|
206
232
|
clearTimeout(timeoutId);
|
|
207
233
|
if (response.status === 204) {
|
|
@@ -348,8 +374,7 @@ var WebSocketTransport = class extends EventEmitter {
|
|
|
348
374
|
try {
|
|
349
375
|
const response = await fetch(this.ticketUrl, {
|
|
350
376
|
method: "POST",
|
|
351
|
-
headers
|
|
352
|
-
credentials: "include"
|
|
377
|
+
headers
|
|
353
378
|
});
|
|
354
379
|
if (!response.ok) return null;
|
|
355
380
|
const json = await response.json();
|
|
@@ -431,14 +456,14 @@ var WebSocketTransport = class extends EventEmitter {
|
|
|
431
456
|
}
|
|
432
457
|
this.setStatus("reconnecting");
|
|
433
458
|
this.emit("reconnecting", { attempt: this.reconnectAttempt + 1 });
|
|
434
|
-
const
|
|
459
|
+
const delay2 = Math.min(
|
|
435
460
|
this.baseDelay * Math.pow(2, this.reconnectAttempt) + Math.random() * this.baseDelay * 0.3,
|
|
436
461
|
this.maxDelay
|
|
437
462
|
);
|
|
438
463
|
this.reconnectTimer = setTimeout(() => {
|
|
439
464
|
this.reconnectAttempt++;
|
|
440
465
|
this.connect();
|
|
441
|
-
},
|
|
466
|
+
}, delay2);
|
|
442
467
|
}
|
|
443
468
|
setStatus(status) {
|
|
444
469
|
if (this.status !== status) {
|
|
@@ -448,6 +473,173 @@ var WebSocketTransport = class extends EventEmitter {
|
|
|
448
473
|
}
|
|
449
474
|
};
|
|
450
475
|
|
|
476
|
+
// src/shared/upload.ts
|
|
477
|
+
var RETRY_DELAYS_MS = [0, 1e3, 3e3];
|
|
478
|
+
var STALL_TIMEOUT_MS = 45e3;
|
|
479
|
+
var RETRYABLE_STATUS_CODES = /* @__PURE__ */ new Set([0, 408, 429, 500, 502, 503, 504]);
|
|
480
|
+
function delay(ms) {
|
|
481
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
482
|
+
}
|
|
483
|
+
function shouldRetry(status, code) {
|
|
484
|
+
if (code === "aborted") {
|
|
485
|
+
return false;
|
|
486
|
+
}
|
|
487
|
+
return RETRYABLE_STATUS_CODES.has(status) || code === "upload_stalled";
|
|
488
|
+
}
|
|
489
|
+
function uploadOnce(url, file, onProgress, signal) {
|
|
490
|
+
return new Promise((resolve) => {
|
|
491
|
+
if (typeof XMLHttpRequest === "undefined") {
|
|
492
|
+
resolve({
|
|
493
|
+
data: null,
|
|
494
|
+
error: {
|
|
495
|
+
code: "unsupported_environment",
|
|
496
|
+
message: "XMLHttpRequest is not available in this environment",
|
|
497
|
+
status: 0
|
|
498
|
+
}
|
|
499
|
+
});
|
|
500
|
+
return;
|
|
501
|
+
}
|
|
502
|
+
const xhr = new XMLHttpRequest();
|
|
503
|
+
let settled = false;
|
|
504
|
+
let stallTimer = null;
|
|
505
|
+
let lastLoaded = 0;
|
|
506
|
+
let totalBytes = file.size;
|
|
507
|
+
const finish = (result) => {
|
|
508
|
+
if (settled) return;
|
|
509
|
+
settled = true;
|
|
510
|
+
if (stallTimer) {
|
|
511
|
+
clearTimeout(stallTimer);
|
|
512
|
+
stallTimer = null;
|
|
513
|
+
}
|
|
514
|
+
resolve(result);
|
|
515
|
+
};
|
|
516
|
+
const resetStallTimer = () => {
|
|
517
|
+
if (stallTimer) {
|
|
518
|
+
clearTimeout(stallTimer);
|
|
519
|
+
}
|
|
520
|
+
stallTimer = setTimeout(() => {
|
|
521
|
+
xhr.abort();
|
|
522
|
+
finish({
|
|
523
|
+
data: null,
|
|
524
|
+
error: {
|
|
525
|
+
code: "upload_stalled",
|
|
526
|
+
message: `Upload stalled (no progress for ${STALL_TIMEOUT_MS / 1e3}s)`,
|
|
527
|
+
status: 0,
|
|
528
|
+
details: {
|
|
529
|
+
bytes_sent: lastLoaded,
|
|
530
|
+
total_bytes: totalBytes
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
});
|
|
534
|
+
}, STALL_TIMEOUT_MS);
|
|
535
|
+
};
|
|
536
|
+
if (signal) {
|
|
537
|
+
if (signal.aborted) {
|
|
538
|
+
finish({
|
|
539
|
+
data: null,
|
|
540
|
+
error: { code: "aborted", message: "Upload aborted", status: 0 }
|
|
541
|
+
});
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
signal.addEventListener(
|
|
545
|
+
"abort",
|
|
546
|
+
() => {
|
|
547
|
+
xhr.abort();
|
|
548
|
+
finish({
|
|
549
|
+
data: null,
|
|
550
|
+
error: { code: "aborted", message: "Upload aborted", status: 0 }
|
|
551
|
+
});
|
|
552
|
+
},
|
|
553
|
+
{ once: true }
|
|
554
|
+
);
|
|
555
|
+
}
|
|
556
|
+
xhr.upload.addEventListener("progress", (event) => {
|
|
557
|
+
resetStallTimer();
|
|
558
|
+
lastLoaded = event.loaded;
|
|
559
|
+
totalBytes = event.total || totalBytes;
|
|
560
|
+
if (event.lengthComputable) {
|
|
561
|
+
onProgress?.(Math.round(event.loaded / event.total * 100));
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
xhr.addEventListener("load", () => {
|
|
565
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
566
|
+
onProgress?.(100);
|
|
567
|
+
finish({ data: null, error: null });
|
|
568
|
+
return;
|
|
569
|
+
}
|
|
570
|
+
finish({
|
|
571
|
+
data: null,
|
|
572
|
+
error: {
|
|
573
|
+
code: "upload_error",
|
|
574
|
+
message: `S3 upload failed: ${xhr.status}`,
|
|
575
|
+
status: xhr.status,
|
|
576
|
+
details: {
|
|
577
|
+
bytes_sent: lastLoaded,
|
|
578
|
+
total_bytes: totalBytes
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
});
|
|
582
|
+
});
|
|
583
|
+
xhr.addEventListener("error", () => {
|
|
584
|
+
finish({
|
|
585
|
+
data: null,
|
|
586
|
+
error: {
|
|
587
|
+
code: "upload_error",
|
|
588
|
+
message: "S3 upload failed",
|
|
589
|
+
status: xhr.status || 0,
|
|
590
|
+
details: {
|
|
591
|
+
bytes_sent: lastLoaded,
|
|
592
|
+
total_bytes: totalBytes
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
});
|
|
596
|
+
});
|
|
597
|
+
xhr.addEventListener("abort", () => {
|
|
598
|
+
if (settled) return;
|
|
599
|
+
finish({
|
|
600
|
+
data: null,
|
|
601
|
+
error: { code: "aborted", message: "Upload aborted", status: 0 }
|
|
602
|
+
});
|
|
603
|
+
});
|
|
604
|
+
xhr.open("PUT", url, true);
|
|
605
|
+
if (file.type) {
|
|
606
|
+
xhr.setRequestHeader("Content-Type", file.type);
|
|
607
|
+
}
|
|
608
|
+
resetStallTimer();
|
|
609
|
+
xhr.send(file);
|
|
610
|
+
});
|
|
611
|
+
}
|
|
612
|
+
async function uploadToPresignedUrl(url, file, onProgress, signal) {
|
|
613
|
+
let lastError = null;
|
|
614
|
+
for (const [attempt, delayMs] of RETRY_DELAYS_MS.entries()) {
|
|
615
|
+
if (delayMs > 0) {
|
|
616
|
+
await delay(delayMs);
|
|
617
|
+
}
|
|
618
|
+
const result = await uploadOnce(url, file, onProgress, signal);
|
|
619
|
+
if (!result.error) {
|
|
620
|
+
return result;
|
|
621
|
+
}
|
|
622
|
+
lastError = {
|
|
623
|
+
...result.error,
|
|
624
|
+
details: {
|
|
625
|
+
...result.error.details,
|
|
626
|
+
attempt: attempt + 1
|
|
627
|
+
}
|
|
628
|
+
};
|
|
629
|
+
if (!shouldRetry(result.error.status, result.error.code)) {
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
return {
|
|
634
|
+
data: null,
|
|
635
|
+
error: lastError ?? {
|
|
636
|
+
code: "upload_error",
|
|
637
|
+
message: "Upload failed",
|
|
638
|
+
status: 0
|
|
639
|
+
}
|
|
640
|
+
};
|
|
641
|
+
}
|
|
642
|
+
|
|
451
643
|
// src/core/ChatClient.ts
|
|
452
644
|
var ChatClient = class extends EventEmitter {
|
|
453
645
|
constructor(config) {
|
|
@@ -455,6 +647,7 @@ var ChatClient = class extends EventEmitter {
|
|
|
455
647
|
this.conversationSubs = /* @__PURE__ */ new Map();
|
|
456
648
|
this.conversationTypes = /* @__PURE__ */ new Map();
|
|
457
649
|
const baseUrl = config.apiBaseUrl ?? DEFAULT_API_BASE_URL;
|
|
650
|
+
this.currentUserId = config.userId;
|
|
458
651
|
this.http = new HttpTransport({
|
|
459
652
|
baseUrl,
|
|
460
653
|
apiKey: config.apiKey,
|
|
@@ -513,6 +706,9 @@ var ChatClient = class extends EventEmitter {
|
|
|
513
706
|
get status() {
|
|
514
707
|
return this.ws.getStatus();
|
|
515
708
|
}
|
|
709
|
+
get userId() {
|
|
710
|
+
return this.currentUserId;
|
|
711
|
+
}
|
|
516
712
|
connect() {
|
|
517
713
|
this.ws.connect();
|
|
518
714
|
}
|
|
@@ -564,9 +760,15 @@ var ChatClient = class extends EventEmitter {
|
|
|
564
760
|
}
|
|
565
761
|
);
|
|
566
762
|
if (result.data) {
|
|
567
|
-
this.cache.
|
|
763
|
+
const reconciled = this.cache.reconcileOptimisticMessage(conversationId, result.data);
|
|
764
|
+
this.cache.upsertMessage(conversationId, reconciled);
|
|
568
765
|
} else if (result.error?.status === 0) {
|
|
569
|
-
this.offlineQueue.enqueue(
|
|
766
|
+
this.offlineQueue.enqueue(
|
|
767
|
+
conversationId,
|
|
768
|
+
options.content,
|
|
769
|
+
options.message_type ?? "text",
|
|
770
|
+
options.attachments
|
|
771
|
+
);
|
|
570
772
|
}
|
|
571
773
|
return result;
|
|
572
774
|
}
|
|
@@ -595,9 +797,60 @@ var ChatClient = class extends EventEmitter {
|
|
|
595
797
|
async deleteMessage(messageId) {
|
|
596
798
|
return this.http.del(`/v1/chat/messages/${messageId}`);
|
|
597
799
|
}
|
|
800
|
+
async uploadAttachment(file, onProgress, signal) {
|
|
801
|
+
const filename = typeof File !== "undefined" && file instanceof File ? file.name : "attachment";
|
|
802
|
+
const contentType = file.type || "application/octet-stream";
|
|
803
|
+
const initResult = await this.http.post("/v1/storage/signed-url/upload", {
|
|
804
|
+
filename,
|
|
805
|
+
content_type: contentType,
|
|
806
|
+
size_bytes: file.size,
|
|
807
|
+
is_public: false,
|
|
808
|
+
metadata: {
|
|
809
|
+
source: "chat_sdk"
|
|
810
|
+
}
|
|
811
|
+
});
|
|
812
|
+
if (initResult.error || !initResult.data) {
|
|
813
|
+
return { data: null, error: initResult.error };
|
|
814
|
+
}
|
|
815
|
+
const uploadResult = await uploadToPresignedUrl(
|
|
816
|
+
initResult.data.upload_url,
|
|
817
|
+
file,
|
|
818
|
+
onProgress,
|
|
819
|
+
signal
|
|
820
|
+
);
|
|
821
|
+
if (uploadResult.error) {
|
|
822
|
+
return { data: null, error: uploadResult.error };
|
|
823
|
+
}
|
|
824
|
+
const completeResult = await this.http.post("/v1/storage/signed-url/complete", {
|
|
825
|
+
file_id: initResult.data.file_id,
|
|
826
|
+
completion_token: initResult.data.completion_token
|
|
827
|
+
});
|
|
828
|
+
if (completeResult.error || !completeResult.data) {
|
|
829
|
+
return { data: null, error: completeResult.error };
|
|
830
|
+
}
|
|
831
|
+
return {
|
|
832
|
+
data: {
|
|
833
|
+
file_id: completeResult.data.file_id,
|
|
834
|
+
file_name: completeResult.data.filename,
|
|
835
|
+
file_size: completeResult.data.size_bytes,
|
|
836
|
+
mime_type: completeResult.data.content_type,
|
|
837
|
+
presigned_url: completeResult.data.url
|
|
838
|
+
},
|
|
839
|
+
error: null
|
|
840
|
+
};
|
|
841
|
+
}
|
|
842
|
+
async refreshAttachmentUrl(messageId, fileId) {
|
|
843
|
+
return this.http.get(
|
|
844
|
+
`/v1/chat/messages/${messageId}/attachment/${fileId}/url`
|
|
845
|
+
);
|
|
846
|
+
}
|
|
598
847
|
getCachedMessages(conversationId) {
|
|
599
848
|
return this.cache.getMessages(conversationId);
|
|
600
849
|
}
|
|
850
|
+
stageOptimisticMessage(conversationId, message) {
|
|
851
|
+
this.cache.upsertMessage(conversationId, message);
|
|
852
|
+
return message;
|
|
853
|
+
}
|
|
601
854
|
// ============ Reactions ============
|
|
602
855
|
async addReaction(messageId, emoji) {
|
|
603
856
|
return this.http.post(`/v1/chat/messages/${messageId}/reactions`, { emoji });
|
|
@@ -605,6 +858,20 @@ var ChatClient = class extends EventEmitter {
|
|
|
605
858
|
async removeReaction(messageId, emoji) {
|
|
606
859
|
return this.http.del(`/v1/chat/messages/${messageId}/reactions/${encodeURIComponent(emoji)}`);
|
|
607
860
|
}
|
|
861
|
+
async reportMessage(messageId, reason, description) {
|
|
862
|
+
return this.http.post(`/v1/chat/messages/${messageId}/report`, {
|
|
863
|
+
reason,
|
|
864
|
+
description
|
|
865
|
+
});
|
|
866
|
+
}
|
|
867
|
+
async muteConversation(conversationId, mutedUntil) {
|
|
868
|
+
return this.http.post(`/v1/chat/conversations/${conversationId}/mute`, {
|
|
869
|
+
muted_until: mutedUntil
|
|
870
|
+
});
|
|
871
|
+
}
|
|
872
|
+
async unmuteConversation(conversationId) {
|
|
873
|
+
return this.http.del(`/v1/chat/conversations/${conversationId}/mute`);
|
|
874
|
+
}
|
|
608
875
|
// ============ Unread Count ============
|
|
609
876
|
async getUnreadTotal() {
|
|
610
877
|
return this.http.get("/v1/chat/conversations/unread-total");
|
|
@@ -780,6 +1047,91 @@ var ChatClient = class extends EventEmitter {
|
|
|
780
1047
|
}
|
|
781
1048
|
}
|
|
782
1049
|
}
|
|
1050
|
+
normalizeMessage(payload) {
|
|
1051
|
+
return {
|
|
1052
|
+
id: payload.id ?? payload.message_id ?? "",
|
|
1053
|
+
content: payload.content ?? "",
|
|
1054
|
+
message_type: payload.message_type ?? "text",
|
|
1055
|
+
sender_id: payload.sender_id ?? payload.sender_user_id ?? "",
|
|
1056
|
+
sender_type: payload.sender_type,
|
|
1057
|
+
sender_agent_model: payload.sender_agent_model,
|
|
1058
|
+
attachments: payload.attachments,
|
|
1059
|
+
reactions: payload.reactions,
|
|
1060
|
+
is_edited: Boolean(payload.is_edited ?? false),
|
|
1061
|
+
created_at: payload.created_at ?? payload.updated_at ?? payload.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
buildEditedMessage(conversationId, update) {
|
|
1065
|
+
const messageId = update.message_id ?? update.id;
|
|
1066
|
+
if (!messageId) {
|
|
1067
|
+
return null;
|
|
1068
|
+
}
|
|
1069
|
+
const existing = this.cache.getMessage(conversationId, messageId);
|
|
1070
|
+
return {
|
|
1071
|
+
id: existing?.id ?? messageId,
|
|
1072
|
+
content: update.content ?? update.new_content ?? existing?.content ?? "",
|
|
1073
|
+
message_type: existing?.message_type ?? "text",
|
|
1074
|
+
sender_id: existing?.sender_id ?? "",
|
|
1075
|
+
sender_type: existing?.sender_type,
|
|
1076
|
+
sender_agent_model: existing?.sender_agent_model,
|
|
1077
|
+
attachments: existing?.attachments,
|
|
1078
|
+
reactions: existing?.reactions,
|
|
1079
|
+
is_edited: true,
|
|
1080
|
+
created_at: existing?.created_at ?? update.updated_at ?? update.timestamp ?? (/* @__PURE__ */ new Date()).toISOString()
|
|
1081
|
+
};
|
|
1082
|
+
}
|
|
1083
|
+
applyReactionEvent(conversationId, reactionEvent) {
|
|
1084
|
+
const reaction = {
|
|
1085
|
+
id: `${reactionEvent.message_id}:${reactionEvent.user_id}:${reactionEvent.emoji}`,
|
|
1086
|
+
message_id: reactionEvent.message_id,
|
|
1087
|
+
user_id: reactionEvent.user_id,
|
|
1088
|
+
emoji: reactionEvent.emoji,
|
|
1089
|
+
action: reactionEvent.action,
|
|
1090
|
+
timestamp: reactionEvent.timestamp
|
|
1091
|
+
};
|
|
1092
|
+
const existingMessage = this.cache.getMessage(conversationId, reactionEvent.message_id);
|
|
1093
|
+
if (!existingMessage) {
|
|
1094
|
+
return reaction;
|
|
1095
|
+
}
|
|
1096
|
+
const nextReactions = [...existingMessage.reactions ?? []];
|
|
1097
|
+
const reactionIndex = nextReactions.findIndex((entry) => entry.emoji === reactionEvent.emoji);
|
|
1098
|
+
if (reactionEvent.action === "added") {
|
|
1099
|
+
if (reactionIndex >= 0) {
|
|
1100
|
+
const current = nextReactions[reactionIndex];
|
|
1101
|
+
if (!current.user_ids.includes(reactionEvent.user_id)) {
|
|
1102
|
+
const user_ids = [...current.user_ids, reactionEvent.user_id];
|
|
1103
|
+
nextReactions[reactionIndex] = {
|
|
1104
|
+
...current,
|
|
1105
|
+
user_ids,
|
|
1106
|
+
count: user_ids.length
|
|
1107
|
+
};
|
|
1108
|
+
}
|
|
1109
|
+
} else {
|
|
1110
|
+
nextReactions.push({
|
|
1111
|
+
emoji: reactionEvent.emoji,
|
|
1112
|
+
count: 1,
|
|
1113
|
+
user_ids: [reactionEvent.user_id]
|
|
1114
|
+
});
|
|
1115
|
+
}
|
|
1116
|
+
} else if (reactionIndex >= 0) {
|
|
1117
|
+
const current = nextReactions[reactionIndex];
|
|
1118
|
+
const user_ids = current.user_ids.filter((userId) => userId !== reactionEvent.user_id);
|
|
1119
|
+
if (user_ids.length === 0) {
|
|
1120
|
+
nextReactions.splice(reactionIndex, 1);
|
|
1121
|
+
} else {
|
|
1122
|
+
nextReactions[reactionIndex] = {
|
|
1123
|
+
...current,
|
|
1124
|
+
user_ids,
|
|
1125
|
+
count: user_ids.length
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
this.cache.updateMessage(conversationId, {
|
|
1130
|
+
...existingMessage,
|
|
1131
|
+
reactions: nextReactions
|
|
1132
|
+
});
|
|
1133
|
+
return reaction;
|
|
1134
|
+
}
|
|
783
1135
|
handleConversationMessage(channel, data) {
|
|
784
1136
|
const conversationId = channel.replace(/^conversation:(?:lr:|bc:|support:)?/, "");
|
|
785
1137
|
const raw = data;
|
|
@@ -788,15 +1140,32 @@ var ChatClient = class extends EventEmitter {
|
|
|
788
1140
|
const payload = raw.data ?? raw;
|
|
789
1141
|
switch (event) {
|
|
790
1142
|
case "new_message": {
|
|
791
|
-
const message = payload;
|
|
792
|
-
this.cache.
|
|
793
|
-
this.
|
|
1143
|
+
const message = this.normalizeMessage(payload);
|
|
1144
|
+
const reconciled = this.cache.reconcileOptimisticMessage(conversationId, message);
|
|
1145
|
+
this.cache.upsertMessage(conversationId, reconciled);
|
|
1146
|
+
this.emit("message", { message: reconciled, conversationId });
|
|
794
1147
|
break;
|
|
795
1148
|
}
|
|
796
1149
|
case "message_edited": {
|
|
797
|
-
const
|
|
798
|
-
this.
|
|
799
|
-
|
|
1150
|
+
const update = payload;
|
|
1151
|
+
const message = this.buildEditedMessage(conversationId, update);
|
|
1152
|
+
if (message) {
|
|
1153
|
+
this.cache.upsertMessage(conversationId, message);
|
|
1154
|
+
this.emit("message:updated", { message, conversationId, update });
|
|
1155
|
+
}
|
|
1156
|
+
break;
|
|
1157
|
+
}
|
|
1158
|
+
case "reaction": {
|
|
1159
|
+
const reactionEvent = payload;
|
|
1160
|
+
if (!reactionEvent.message_id || !reactionEvent.user_id || !reactionEvent.emoji) {
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
const reaction = this.applyReactionEvent(conversationId, reactionEvent);
|
|
1164
|
+
this.emit("reaction", {
|
|
1165
|
+
reaction,
|
|
1166
|
+
conversationId,
|
|
1167
|
+
action: reactionEvent.action
|
|
1168
|
+
});
|
|
800
1169
|
break;
|
|
801
1170
|
}
|
|
802
1171
|
case "message_deleted": {
|
|
@@ -857,10 +1226,12 @@ var ChatClient = class extends EventEmitter {
|
|
|
857
1226
|
for (const item of queued) {
|
|
858
1227
|
await this.sendMessage(item.conversationId, {
|
|
859
1228
|
content: item.content,
|
|
860
|
-
message_type: item.message_type
|
|
1229
|
+
message_type: item.message_type,
|
|
1230
|
+
attachments: item.attachments
|
|
861
1231
|
});
|
|
862
1232
|
}
|
|
863
1233
|
}
|
|
864
1234
|
};
|
|
865
1235
|
|
|
866
1236
|
exports.ChatClient = ChatClient;
|
|
1237
|
+
exports.EventEmitter = EventEmitter;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare const DEFAULT_API_BASE_URL = "https://api.scalemule.com";
|
|
2
|
+
export declare const DEFAULT_WS_RECONNECT_BASE_DELAY = 1000;
|
|
3
|
+
export declare const DEFAULT_WS_RECONNECT_MAX_DELAY = 30000;
|
|
4
|
+
export declare const DEFAULT_WS_RECONNECT_MAX_RETRIES: number;
|
|
5
|
+
export declare const DEFAULT_WS_HEARTBEAT_INTERVAL = 30000;
|
|
6
|
+
export declare const DEFAULT_MESSAGE_CACHE_SIZE = 200;
|
|
7
|
+
export declare const DEFAULT_MAX_CONVERSATIONS_CACHED = 50;
|
|
8
|
+
export declare const DEFAULT_REQUEST_TIMEOUT = 10000;
|
|
9
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,oBAAoB,8BAA8B,CAAC;AAChE,eAAO,MAAM,+BAA+B,OAAO,CAAC;AACpD,eAAO,MAAM,8BAA8B,QAAQ,CAAC;AACpD,eAAO,MAAM,gCAAgC,QAAW,CAAC;AACzD,eAAO,MAAM,6BAA6B,QAAQ,CAAC;AACnD,eAAO,MAAM,0BAA0B,MAAM,CAAC;AAC9C,eAAO,MAAM,gCAAgC,KAAK,CAAC;AACnD,eAAO,MAAM,uBAAuB,QAAQ,CAAC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { EventEmitter } from './EventEmitter';
|
|
2
|
+
import type { Attachment, ApiResponse, ChatConfig, ChatEventMap, ChatMessage, ChannelWithSettings, ConnectionStatus, Conversation, CreateConversationOptions, CreateEphemeralChannelOptions, CreateLargeRoomOptions, GetMessagesOptions, ListConversationsOptions, MessagesResponse, ReadStatus, SendMessageOptions, ChannelSettings, UnreadTotalResponse } from '../types';
|
|
3
|
+
export declare class ChatClient extends EventEmitter<ChatEventMap> {
|
|
4
|
+
private http;
|
|
5
|
+
private ws;
|
|
6
|
+
private cache;
|
|
7
|
+
private offlineQueue;
|
|
8
|
+
private conversationSubs;
|
|
9
|
+
private conversationTypes;
|
|
10
|
+
private currentUserId?;
|
|
11
|
+
constructor(config: ChatConfig);
|
|
12
|
+
get status(): ConnectionStatus;
|
|
13
|
+
get userId(): string | undefined;
|
|
14
|
+
connect(): void;
|
|
15
|
+
disconnect(): void;
|
|
16
|
+
createConversation(options: CreateConversationOptions): Promise<ApiResponse<Conversation>>;
|
|
17
|
+
listConversations(options?: ListConversationsOptions): Promise<ApiResponse<Conversation[]>>;
|
|
18
|
+
getConversation(id: string): Promise<ApiResponse<Conversation>>;
|
|
19
|
+
private trackConversationType;
|
|
20
|
+
sendMessage(conversationId: string, options: SendMessageOptions): Promise<ApiResponse<ChatMessage>>;
|
|
21
|
+
getMessages(conversationId: string, options?: GetMessagesOptions): Promise<ApiResponse<MessagesResponse>>;
|
|
22
|
+
editMessage(messageId: string, content: string): Promise<ApiResponse<void>>;
|
|
23
|
+
deleteMessage(messageId: string): Promise<ApiResponse<void>>;
|
|
24
|
+
uploadAttachment(file: File | Blob, onProgress?: (percent: number) => void, signal?: AbortSignal): Promise<ApiResponse<Attachment>>;
|
|
25
|
+
refreshAttachmentUrl(messageId: string, fileId: string): Promise<ApiResponse<{
|
|
26
|
+
url: string;
|
|
27
|
+
}>>;
|
|
28
|
+
getCachedMessages(conversationId: string): ChatMessage[];
|
|
29
|
+
stageOptimisticMessage(conversationId: string, message: ChatMessage): ChatMessage;
|
|
30
|
+
addReaction(messageId: string, emoji: string): Promise<ApiResponse<void>>;
|
|
31
|
+
removeReaction(messageId: string, emoji: string): Promise<ApiResponse<void>>;
|
|
32
|
+
reportMessage(messageId: string, reason: 'spam' | 'harassment' | 'hate' | 'violence' | 'other', description?: string): Promise<ApiResponse<{
|
|
33
|
+
reported: boolean;
|
|
34
|
+
}>>;
|
|
35
|
+
muteConversation(conversationId: string, mutedUntil?: string): Promise<ApiResponse<{
|
|
36
|
+
muted: boolean;
|
|
37
|
+
}>>;
|
|
38
|
+
unmuteConversation(conversationId: string): Promise<ApiResponse<{
|
|
39
|
+
muted: boolean;
|
|
40
|
+
}>>;
|
|
41
|
+
getUnreadTotal(): Promise<ApiResponse<UnreadTotalResponse>>;
|
|
42
|
+
sendTyping(conversationId: string, isTyping?: boolean): Promise<void>;
|
|
43
|
+
markRead(conversationId: string): Promise<void>;
|
|
44
|
+
getReadStatus(conversationId: string): Promise<ApiResponse<{
|
|
45
|
+
statuses: ReadStatus[];
|
|
46
|
+
}>>;
|
|
47
|
+
addParticipant(conversationId: string, userId: string): Promise<ApiResponse<void>>;
|
|
48
|
+
removeParticipant(conversationId: string, userId: string): Promise<ApiResponse<void>>;
|
|
49
|
+
joinPresence(conversationId: string, userData?: unknown): void;
|
|
50
|
+
leavePresence(conversationId: string): void;
|
|
51
|
+
/** Update presence status (online/away/dnd) without leaving the channel. */
|
|
52
|
+
updatePresence(conversationId: string, status: 'online' | 'away' | 'dnd', userData?: unknown): void;
|
|
53
|
+
getChannelSettings(channelId: string): Promise<ApiResponse<ChannelSettings>>;
|
|
54
|
+
/**
|
|
55
|
+
* Set the conversation type for channel name routing.
|
|
56
|
+
* Large rooms use `conversation:lr:` prefix to skip MySQL in the realtime service.
|
|
57
|
+
*/
|
|
58
|
+
setConversationType(conversationId: string, type: Conversation['conversation_type']): void;
|
|
59
|
+
/**
|
|
60
|
+
* Find an active ephemeral or large_room channel by linked session ID.
|
|
61
|
+
* Returns null (not an error) if no active channel exists.
|
|
62
|
+
*/
|
|
63
|
+
findChannelBySessionId(linkedSessionId: string): Promise<ChannelWithSettings | null>;
|
|
64
|
+
/**
|
|
65
|
+
* Self-join an ephemeral or large_room channel. Idempotent.
|
|
66
|
+
*/
|
|
67
|
+
joinChannel(channelId: string): Promise<ApiResponse<{
|
|
68
|
+
participant_id: string;
|
|
69
|
+
role: string;
|
|
70
|
+
joined_at: string;
|
|
71
|
+
}>>;
|
|
72
|
+
/**
|
|
73
|
+
* Create an ephemeral channel tied to a session (e.g., a video snap).
|
|
74
|
+
*/
|
|
75
|
+
createEphemeralChannel(options: CreateEphemeralChannelOptions): Promise<ApiResponse<ChannelWithSettings>>;
|
|
76
|
+
/**
|
|
77
|
+
* Create a large room channel (high-concurrency, skips MySQL tracking in realtime).
|
|
78
|
+
*/
|
|
79
|
+
createLargeRoom(options: CreateLargeRoomOptions): Promise<ApiResponse<ChannelWithSettings>>;
|
|
80
|
+
/**
|
|
81
|
+
* Get the global concurrent subscriber count for a conversation.
|
|
82
|
+
*/
|
|
83
|
+
getSubscriberCount(conversationId: string): Promise<number>;
|
|
84
|
+
subscribeToConversation(conversationId: string): () => void;
|
|
85
|
+
/** Build channel name with correct prefix based on conversation type. */
|
|
86
|
+
private channelName;
|
|
87
|
+
destroy(): void;
|
|
88
|
+
private handleRealtimeMessage;
|
|
89
|
+
private handlePrivateMessage;
|
|
90
|
+
private normalizeMessage;
|
|
91
|
+
private buildEditedMessage;
|
|
92
|
+
private applyReactionEvent;
|
|
93
|
+
private handleConversationMessage;
|
|
94
|
+
private flushOfflineQueue;
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=ChatClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ChatClient.d.ts","sourceRoot":"","sources":["../../src/core/ChatClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO9C,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,YAAY,EACZ,WAAW,EAEX,mBAAmB,EACnB,gBAAgB,EAChB,YAAY,EACZ,yBAAyB,EACzB,6BAA6B,EAC7B,sBAAsB,EACtB,kBAAkB,EAClB,wBAAwB,EAExB,gBAAgB,EAGhB,UAAU,EACV,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EAEpB,MAAM,UAAU,CAAC;AAElB,qBAAa,UAAW,SAAQ,YAAY,CAAC,YAAY,CAAC;IACxD,OAAO,CAAC,IAAI,CAAgB;IAC5B,OAAO,CAAC,EAAE,CAAqB;IAC/B,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,YAAY,CAAe;IACnC,OAAO,CAAC,gBAAgB,CAAiC;IACzD,OAAO,CAAC,iBAAiB,CAAwD;IACjF,OAAO,CAAC,aAAa,CAAC,CAAS;gBAEnB,MAAM,EAAE,UAAU;IA2E9B,IAAI,MAAM,IAAI,gBAAgB,CAE7B;IAED,IAAI,MAAM,IAAI,MAAM,GAAG,SAAS,CAE/B;IAED,OAAO,IAAI,IAAI;IAIf,UAAU,IAAI,IAAI;IAUZ,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IAU1F,iBAAiB,CAAC,OAAO,CAAC,EAAE,wBAAwB,GAAG,OAAO,CAAC,WAAW,CAAC,YAAY,EAAE,CAAC,CAAC;IAW3F,eAAe,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,YAAY,CAAC,CAAC;IAMrE,OAAO,CAAC,qBAAqB;IAQvB,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,WAAW,CAAC,CAAC;IA0BnG,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC,gBAAgB,CAAC,CAAC;IA2BzG,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAI3E,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAI5D,gBAAgB,CACpB,IAAI,EAAE,IAAI,GAAG,IAAI,EACjB,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,EACtC,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;IAkD7B,oBAAoB,CACxB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,CAAC;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAMxC,iBAAiB,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,EAAE;IAIxD,sBAAsB,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,WAAW;IAO3E,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAIzE,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAI5E,aAAa,CACjB,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,MAAM,GAAG,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,OAAO,EAC7D,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,WAAW,CAAC;QAAE,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAOxC,gBAAgB,CACpB,cAAc,EAAE,MAAM,EACtB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,WAAW,CAAC;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAMrC,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;QAAE,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAMpF,cAAc,IAAI,OAAO,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;IAM3D,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC;IAIlE,QAAQ,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAI/C,aAAa,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;QAAE,QAAQ,EAAE,UAAU,EAAE,CAAA;KAAE,CAAC,CAAC;IAMvF,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAMlF,iBAAiB,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC;IAM3F,YAAY,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI;IAK9D,aAAa,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI;IAK3C,4EAA4E;IAC5E,cAAc,CAAC,cAAc,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,GAAG,MAAM,GAAG,KAAK,EAAE,QAAQ,CAAC,EAAE,OAAO,GAAG,IAAI;IAO7F,kBAAkB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,eAAe,CAAC,CAAC;IAIlF;;;OAGG;IACH,mBAAmB,CAAC,cAAc,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,CAAC,mBAAmB,CAAC,GAAG,IAAI;IAM1F;;;OAGG;IACG,sBAAsB,CAAC,eAAe,EAAE,MAAM,GAAG,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC;IAW1F;;OAEG;IACG,WAAW,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC;QAAE,cAAc,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAIvH;;OAEG;IACG,sBAAsB,CAAC,OAAO,EAAE,6BAA6B,GAAG,OAAO,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;IAQ/G;;OAEG;IACG,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,OAAO,CAAC,WAAW,CAAC,mBAAmB,CAAC,CAAC;IAQjG;;OAEG;IACG,kBAAkB,CAAC,cAAc,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAOjE,uBAAuB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,IAAI;IAa3D,yEAAyE;IACzE,OAAO,CAAC,WAAW;IAgBnB,OAAO,IAAI,IAAI;IAQf,OAAO,CAAC,qBAAqB;IAW7B,OAAO,CAAC,oBAAoB;IAwC5B,OAAO,CAAC,gBAAgB;IAmBxB,OAAO,CAAC,kBAAkB;IAyB1B,OAAO,CAAC,kBAAkB;IA0D1B,OAAO,CAAC,yBAAyB;YAoGnB,iBAAiB;CAUhC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
type Listener<T> = (data: T) => void;
|
|
2
|
+
export declare class EventEmitter<EventMap extends Record<string, any>> {
|
|
3
|
+
private listeners;
|
|
4
|
+
on<K extends keyof EventMap>(event: K, callback: Listener<EventMap[K]>): () => void;
|
|
5
|
+
off<K extends keyof EventMap>(event: K, callback: Listener<EventMap[K]>): void;
|
|
6
|
+
once<K extends keyof EventMap>(event: K, callback: Listener<EventMap[K]>): () => void;
|
|
7
|
+
emit<K extends keyof EventMap>(event: K, ...[data]: EventMap[K] extends void ? [] : [EventMap[K]]): void;
|
|
8
|
+
removeAllListeners(event?: keyof EventMap): void;
|
|
9
|
+
}
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=EventEmitter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EventEmitter.d.ts","sourceRoot":"","sources":["../../src/core/EventEmitter.ts"],"names":[],"mappings":"AAAA,KAAK,QAAQ,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,KAAK,IAAI,CAAC;AAGrC,qBAAa,YAAY,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC;IAC5D,OAAO,CAAC,SAAS,CAAqD;IAEtE,EAAE,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAQnF,GAAG,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI;IAI9E,IAAI,CAAC,CAAC,SAAS,MAAM,QAAQ,EAAE,KAAK,EAAE,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAQrF,IAAI,CAAC,CAAC,SAAS,MAAM,QAAQ,EAC3B,KAAK,EAAE,CAAC,EACR,GAAG,CAAC,IAAI,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,SAAS,IAAI,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,GACvD,IAAI;IAaP,kBAAkB,CAAC,KAAK,CAAC,EAAE,MAAM,QAAQ,GAAG,IAAI;CAOjD"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import type { ChatMessage } from '../types';
|
|
2
|
+
export declare class MessageCache {
|
|
3
|
+
private cache;
|
|
4
|
+
private maxMessages;
|
|
5
|
+
private maxConversations;
|
|
6
|
+
constructor(maxMessages?: number, maxConversations?: number);
|
|
7
|
+
getMessages(conversationId: string): ChatMessage[];
|
|
8
|
+
getMessage(conversationId: string, messageId: string): ChatMessage | undefined;
|
|
9
|
+
setMessages(conversationId: string, messages: ChatMessage[]): void;
|
|
10
|
+
addMessage(conversationId: string, message: ChatMessage): void;
|
|
11
|
+
upsertMessage(conversationId: string, message: ChatMessage): void;
|
|
12
|
+
updateMessage(conversationId: string, message: ChatMessage): void;
|
|
13
|
+
reconcileOptimisticMessage(conversationId: string, message: ChatMessage): ChatMessage;
|
|
14
|
+
removeMessage(conversationId: string, messageId: string): void;
|
|
15
|
+
clear(conversationId?: string): void;
|
|
16
|
+
private evictOldConversations;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=MessageCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"MessageCache.d.ts","sourceRoot":"","sources":["../../src/core/MessageCache.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,UAAU,CAAC;AAG5C,qBAAa,YAAY;IACvB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,WAAW,CAAS;IAC5B,OAAO,CAAC,gBAAgB,CAAS;gBAErB,WAAW,CAAC,EAAE,MAAM,EAAE,gBAAgB,CAAC,EAAE,MAAM;IAK3D,WAAW,CAAC,cAAc,EAAE,MAAM,GAAG,WAAW,EAAE;IAIlD,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAI9E,WAAW,CAAC,cAAc,EAAE,MAAM,EAAE,QAAQ,EAAE,WAAW,EAAE,GAAG,IAAI;IAKlE,UAAU,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IAc9D,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IASjE,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI;IASjE,0BAA0B,CAAC,cAAc,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,WAAW;IAyBrF,aAAa,CAAC,cAAc,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,IAAI;IAS9D,KAAK,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,IAAI;IAQpC,OAAO,CAAC,qBAAqB;CAM9B"}
|