@ttt-productions/chat-react 0.13.15 → 0.13.16
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/context/ChatNameResolverContext.d.ts +3 -3
- package/dist/context/ChatNameResolverContext.d.ts.map +1 -1
- package/dist/context/ChatNameResolverContext.js +3 -3
- package/dist/context/ChatNameResolverContext.js.map +1 -1
- package/dist/firestore/queries.js +1 -1
- package/dist/firestore/queries.js.map +1 -1
- package/dist/hooks/useChatMessages.d.ts +3 -3
- package/dist/hooks/useChatMessages.d.ts.map +1 -1
- package/dist/hooks/useChatMessages.js +20 -12
- package/dist/hooks/useChatMessages.js.map +1 -1
- package/dist/index.d.ts +32 -18
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +18 -11
- package/dist/index.js.map +1 -1
- package/dist/realtime/channel-client.d.ts.map +1 -1
- package/dist/realtime/channel-client.js +97 -32
- package/dist/realtime/channel-client.js.map +1 -1
- package/dist/realtime/inbox-client.d.ts.map +1 -1
- package/dist/realtime/inbox-client.js +29 -11
- package/dist/realtime/inbox-client.js.map +1 -1
- package/dist/realtime/initial-load-budget.d.ts.map +1 -1
- package/dist/realtime/initial-load-budget.js.map +1 -1
- package/dist/realtime/map.d.ts +0 -2
- package/dist/realtime/map.d.ts.map +1 -1
- package/dist/realtime/map.js +3 -4
- package/dist/realtime/map.js.map +1 -1
- package/dist/realtime/shared.d.ts +0 -1
- package/dist/realtime/shared.d.ts.map +1 -1
- package/dist/realtime/shared.js +2 -6
- package/dist/realtime/shared.js.map +1 -1
- package/dist/realtime/socket.d.ts.map +1 -1
- package/dist/realtime/socket.js +1 -11
- package/dist/realtime/socket.js.map +1 -1
- package/dist/realtime/transport.d.ts +1 -2
- package/dist/realtime/transport.d.ts.map +1 -1
- package/dist/realtime/transport.js +1 -1
- package/dist/realtime/transport.js.map +1 -1
- package/dist/realtime/useRealtimeChatMessages.d.ts +4 -4
- package/dist/realtime/useRealtimeChatMessages.d.ts.map +1 -1
- package/dist/realtime/useRealtimeChatMessages.js +5 -5
- package/dist/realtime/useRealtimeChatMessages.js.map +1 -1
- package/dist/realtime/wire.d.ts +0 -2
- package/dist/realtime/wire.d.ts.map +1 -1
- package/dist/realtime/wire.js +0 -6
- package/dist/realtime/wire.js.map +1 -1
- package/dist/types.d.ts +3 -3
- package/dist/types.js.map +1 -1
- package/dist/ui/ChatShell.d.ts +5 -5
- package/dist/ui/ChatShell.d.ts.map +1 -1
- package/dist/ui/ChatShell.js +37 -34
- package/dist/ui/ChatShell.js.map +1 -1
- package/dist/ui/Composer.d.ts +1 -1
- package/dist/ui/Composer.js +12 -12
- package/dist/ui/Composer.js.map +1 -1
- package/dist/ui/MessageItemDefault.d.ts +2 -2
- package/dist/ui/MessageItemDefault.d.ts.map +1 -1
- package/dist/ui/MessageItemDefault.js +26 -24
- package/dist/ui/MessageItemDefault.js.map +1 -1
- package/dist/ui/MessageList.d.ts +3 -3
- package/dist/ui/MessageList.d.ts.map +1 -1
- package/dist/ui/MessageList.js +13 -16
- package/dist/ui/MessageList.js.map +1 -1
- package/dist/ui/SenderName.js +3 -3
- package/dist/ui/SenderName.js.map +1 -1
- package/dist/ui/menus.d.ts +1 -1
- package/dist/ui/menus.d.ts.map +1 -1
- package/dist/ui/menus.js +4 -4
- package/dist/ui/menus.js.map +1 -1
- package/package.json +10 -10
- package/dist/realtime/index.d.ts +0 -19
- package/dist/realtime/index.d.ts.map +0 -1
- package/dist/realtime/index.js +0 -15
- package/dist/realtime/index.js.map +0 -1
|
@@ -14,11 +14,12 @@
|
|
|
14
14
|
// - error codes incl. 4401 AUTH_EXPIRED -> re-mint a grant ONCE via grantProvider
|
|
15
15
|
//
|
|
16
16
|
// Timers are injected (setTimeout/clearTimeout/now) so tests run deterministically.
|
|
17
|
-
import { createReconnectController } from '@ttt-productions/realtime-core';
|
|
18
|
-
import {
|
|
17
|
+
import { createReconnectController, } from '@ttt-productions/realtime-core';
|
|
18
|
+
import { CLIENT_KINDS, CHAT_CLOSE_CODES, ChatSendRejectedPayloadSchema, HEARTBEAT_MS, TYPING_COALESCE_MS, HISTORY_PAGE_MAX, } from '@ttt-productions/chat-schemas';
|
|
19
|
+
import { buildFrame, parseFrame, isInboxSnapshot, } from './wire.js';
|
|
19
20
|
import { wireRowToMessage, optimisticMessage, seqToMessageId, applyModerationOverlay, overlayFromRow, } from './map.js';
|
|
20
21
|
import { createDiagnosticsEmitter, safeDiagnosticLabel, CHAT_CLIENT_DIAGNOSTIC_EVENTS as DIAG, } from './diagnostics.js';
|
|
21
|
-
import { defaultTimers, isChatAccessDeniedError, isTerminalErrorCode, TERMINAL_ERROR_CODE,
|
|
22
|
+
import { defaultTimers, isChatAccessDeniedError, isTerminalErrorCode, TERMINAL_ERROR_CODE, MAX_PENDING_SEND_ATTEMPTS, PENDING_SEND_MAX_AGE_MS, SERVER_RETRYABLE_MAX_AGE_MS, SEND_RETRY_MIN_DELAY_MS, SEND_RETRY_MAX_DELAY_MS, } from './shared.js';
|
|
22
23
|
import { InitialLoadBudget } from './initial-load-budget.js';
|
|
23
24
|
const INITIAL = {
|
|
24
25
|
status: 'idle',
|
|
@@ -335,15 +336,15 @@ export class ChannelClient {
|
|
|
335
336
|
// Resume: ask the DO for the authoritative snapshot, then live deltas flow. The
|
|
336
337
|
// field name MUST be `afterSeq` — the Channel DO reads `payload.afterSeq`; any other
|
|
337
338
|
// name reads as cursorless and forces a full resync every reconnect.
|
|
338
|
-
this.sendFrame(
|
|
339
|
+
this.sendFrame(CLIENT_KINDS.RESUME, { afterSeq: this.resumeSeq });
|
|
339
340
|
this.diag?.(DIAG.RESUME_REQUEST, { afterSeq: this.resumeSeq, attempt: this.connectAttempt });
|
|
340
341
|
// If the UI had presence open, re-subscribe after a reconnect.
|
|
341
342
|
if (this.presenceSubscribed)
|
|
342
|
-
this.sendFrame(
|
|
343
|
+
this.sendFrame(CLIENT_KINDS.PRESENCE_SUBSCRIBE, {});
|
|
343
344
|
// Re-send the last read cursor so a read-ack dropped during the disconnect window
|
|
344
345
|
// (sendFrame returns false on a closed socket) is not lost until the next message.
|
|
345
346
|
if (this.lastReadAck)
|
|
346
|
-
this.sendFrame(
|
|
347
|
+
this.sendFrame(CLIENT_KINDS.READ_ACK, { ...this.lastReadAck });
|
|
347
348
|
// Re-send every un-acked pending send with its ORIGINAL clientMessageId so a send
|
|
348
349
|
// that lost its socket before the ack is not silently dropped. The DO dedups
|
|
349
350
|
// (re-acks, no re-broadcast, no flood-token) — this is the client half of the
|
|
@@ -390,7 +391,7 @@ export class ChannelClient {
|
|
|
390
391
|
continue;
|
|
391
392
|
}
|
|
392
393
|
}
|
|
393
|
-
const sent = this.sendFrame(
|
|
394
|
+
const sent = this.sendFrame(CLIENT_KINDS.SEND, {
|
|
394
395
|
clientMessageId: pending.clientMessageId,
|
|
395
396
|
text: pending.text,
|
|
396
397
|
});
|
|
@@ -501,7 +502,10 @@ export class ChannelClient {
|
|
|
501
502
|
if (typeof rawId === 'string' && rawId.length > 0 && rawId.length <= 200) {
|
|
502
503
|
const orphaned = this.pendingSends.get(rawId);
|
|
503
504
|
if (orphaned && !orphaned.failed) {
|
|
504
|
-
this.diag?.(DIAG.SEND_REJECTED, {
|
|
505
|
+
this.diag?.(DIAG.SEND_REJECTED, {
|
|
506
|
+
clientMessageId: rawId,
|
|
507
|
+
outcome: 'unparseable-fail-closed',
|
|
508
|
+
});
|
|
505
509
|
this.failPendingSend(rawId);
|
|
506
510
|
return;
|
|
507
511
|
}
|
|
@@ -514,13 +518,23 @@ export class ChannelClient {
|
|
|
514
518
|
// No matching pending send — already acked/reconciled, or never ours. A late
|
|
515
519
|
// ack/echo (which may have already landed) always wins; nothing to correlate.
|
|
516
520
|
if (!pending || pending.failed) {
|
|
517
|
-
this.diag?.(DIAG.SEND_REJECTED, {
|
|
521
|
+
this.diag?.(DIAG.SEND_REJECTED, {
|
|
522
|
+
clientMessageId,
|
|
523
|
+
code,
|
|
524
|
+
retryable,
|
|
525
|
+
outcome: 'no-pending-send',
|
|
526
|
+
});
|
|
518
527
|
return;
|
|
519
528
|
}
|
|
520
529
|
if (!retryable) {
|
|
521
530
|
// Terminal: an unchanged resend cannot succeed (blocked-word / archived /
|
|
522
531
|
// deleted). Fail ONLY this bubble, recording the reason + terminal class.
|
|
523
|
-
this.diag?.(DIAG.SEND_REJECTED, {
|
|
532
|
+
this.diag?.(DIAG.SEND_REJECTED, {
|
|
533
|
+
clientMessageId,
|
|
534
|
+
code,
|
|
535
|
+
retryable: false,
|
|
536
|
+
outcome: 'terminal',
|
|
537
|
+
});
|
|
524
538
|
this.failPendingSend(clientMessageId, code, false);
|
|
525
539
|
return;
|
|
526
540
|
}
|
|
@@ -532,12 +546,23 @@ export class ChannelClient {
|
|
|
532
546
|
// If the budget already elapsed (a long stream of retryable rejections), fail
|
|
533
547
|
// now rather than scheduling yet another doomed resend.
|
|
534
548
|
if (this.timers.now() - pending.sentAt >= SERVER_RETRYABLE_MAX_AGE_MS) {
|
|
535
|
-
this.diag?.(DIAG.SEND_REJECTED, {
|
|
549
|
+
this.diag?.(DIAG.SEND_REJECTED, {
|
|
550
|
+
clientMessageId,
|
|
551
|
+
code,
|
|
552
|
+
retryable: true,
|
|
553
|
+
outcome: 'budget-exhausted',
|
|
554
|
+
});
|
|
536
555
|
this.failPendingSend(clientMessageId, code, true);
|
|
537
556
|
return;
|
|
538
557
|
}
|
|
539
558
|
const delayMs = this.clampRetryDelay(retryAfterMs);
|
|
540
|
-
this.diag?.(DIAG.SEND_REJECTED, {
|
|
559
|
+
this.diag?.(DIAG.SEND_REJECTED, {
|
|
560
|
+
clientMessageId,
|
|
561
|
+
code,
|
|
562
|
+
retryable: true,
|
|
563
|
+
outcome: 'retry-scheduled',
|
|
564
|
+
delayMs,
|
|
565
|
+
});
|
|
541
566
|
this.scheduleSendRetry(clientMessageId, delayMs);
|
|
542
567
|
}
|
|
543
568
|
/**
|
|
@@ -564,7 +589,7 @@ export class ChannelClient {
|
|
|
564
589
|
}
|
|
565
590
|
// A server-retryable resend does NOT count toward the attempt cap (that cap is
|
|
566
591
|
// for blind reconnect resends only) — leave `attempts` untouched.
|
|
567
|
-
const sent = this.sendFrame(
|
|
592
|
+
const sent = this.sendFrame(CLIENT_KINDS.SEND, {
|
|
568
593
|
clientMessageId: current.clientMessageId,
|
|
569
594
|
text: current.text,
|
|
570
595
|
});
|
|
@@ -581,7 +606,9 @@ export class ChannelClient {
|
|
|
581
606
|
// contract violation), NOTHING else would ever re-check the budget on a
|
|
582
607
|
// stable socket — so arm a deadline check that fails the send at budget end.
|
|
583
608
|
// This makes SERVER_RETRYABLE_MAX_AGE_MS a hard ceiling, not a best effort.
|
|
584
|
-
const remainingMs = SERVER_RETRYABLE_MAX_AGE_MS -
|
|
609
|
+
const remainingMs = SERVER_RETRYABLE_MAX_AGE_MS -
|
|
610
|
+
(this.timers.now() - current.sentAt) +
|
|
611
|
+
SEND_RETRY_MIN_DELAY_MS;
|
|
585
612
|
current.retryTimer = this.timers.setTimeout(() => {
|
|
586
613
|
current.retryTimer = null;
|
|
587
614
|
if (this.closedByUs)
|
|
@@ -632,7 +659,7 @@ export class ChannelClient {
|
|
|
632
659
|
pending.serverRetryable = false;
|
|
633
660
|
pending.lastRejectionCode = undefined;
|
|
634
661
|
pending.sentAt = this.timers.now();
|
|
635
|
-
const sent = this.sendFrame(
|
|
662
|
+
const sent = this.sendFrame(CLIENT_KINDS.SEND, {
|
|
636
663
|
clientMessageId: pending.clientMessageId,
|
|
637
664
|
text: pending.text,
|
|
638
665
|
});
|
|
@@ -646,7 +673,9 @@ export class ChannelClient {
|
|
|
646
673
|
sent,
|
|
647
674
|
});
|
|
648
675
|
const next = this.state.messages.map((m) => {
|
|
649
|
-
if (!(m.meta?.optimistic &&
|
|
676
|
+
if (!(m.meta?.optimistic &&
|
|
677
|
+
m.meta?.clientMessageId === clientMessageId &&
|
|
678
|
+
m.meta?.sendFailed)) {
|
|
650
679
|
return m;
|
|
651
680
|
}
|
|
652
681
|
// Back to the normal pending look — drop every failure marker.
|
|
@@ -668,7 +697,12 @@ export class ChannelClient {
|
|
|
668
697
|
if (this.state.isFetchingOlder)
|
|
669
698
|
this.setState({ isFetchingOlder: false });
|
|
670
699
|
if (this.closedByUs) {
|
|
671
|
-
this.diag?.(DIAG.SOCKET_CLOSE, {
|
|
700
|
+
this.diag?.(DIAG.SOCKET_CLOSE, {
|
|
701
|
+
code,
|
|
702
|
+
closedByUs: true,
|
|
703
|
+
outcome: 'closed',
|
|
704
|
+
resumeSeq: this.resumeSeq,
|
|
705
|
+
});
|
|
672
706
|
this.setState({ status: 'closed' });
|
|
673
707
|
return;
|
|
674
708
|
}
|
|
@@ -676,7 +710,12 @@ export class ChannelClient {
|
|
|
676
710
|
if (code === CHAT_CLOSE_CODES.AUTH_EXPIRED && !this.reauthAttempted) {
|
|
677
711
|
this.reauthAttempted = true;
|
|
678
712
|
this.reconnectCause = 'auth-expired';
|
|
679
|
-
this.diag?.(DIAG.SOCKET_CLOSE, {
|
|
713
|
+
this.diag?.(DIAG.SOCKET_CLOSE, {
|
|
714
|
+
code,
|
|
715
|
+
closedByUs: false,
|
|
716
|
+
outcome: 're-grant',
|
|
717
|
+
resumeSeq: this.resumeSeq,
|
|
718
|
+
});
|
|
680
719
|
this.setState({ status: 'reconnecting' });
|
|
681
720
|
void this.openSocket();
|
|
682
721
|
return;
|
|
@@ -688,7 +727,12 @@ export class ChannelClient {
|
|
|
688
727
|
if (code === CHAT_CLOSE_CODES.REVOKED) {
|
|
689
728
|
this.closedByUs = true;
|
|
690
729
|
this.controller.close();
|
|
691
|
-
this.diag?.(DIAG.SOCKET_CLOSE, {
|
|
730
|
+
this.diag?.(DIAG.SOCKET_CLOSE, {
|
|
731
|
+
code,
|
|
732
|
+
closedByUs: false,
|
|
733
|
+
outcome: 'revoked',
|
|
734
|
+
resumeSeq: this.resumeSeq,
|
|
735
|
+
});
|
|
692
736
|
this.failAllPendingSends();
|
|
693
737
|
this.setState({ status: 'closed', lastErrorCode: TERMINAL_ERROR_CODE.REVOKED });
|
|
694
738
|
// Revoked before anything loaded: the first open has failed, and nothing retries it.
|
|
@@ -698,7 +742,12 @@ export class ChannelClient {
|
|
|
698
742
|
this.reconnectCause = 'transient-close';
|
|
699
743
|
if (!this.state.hasLoadedInitialData)
|
|
700
744
|
this.initialLoad.noteFailedAttempt();
|
|
701
|
-
this.diag?.(DIAG.SOCKET_CLOSE, {
|
|
745
|
+
this.diag?.(DIAG.SOCKET_CLOSE, {
|
|
746
|
+
code,
|
|
747
|
+
closedByUs: false,
|
|
748
|
+
outcome: 'reconnect',
|
|
749
|
+
resumeSeq: this.resumeSeq,
|
|
750
|
+
});
|
|
702
751
|
this.scheduleReconnect();
|
|
703
752
|
}
|
|
704
753
|
scheduleReconnect() {
|
|
@@ -815,7 +864,10 @@ export class ChannelClient {
|
|
|
815
864
|
return;
|
|
816
865
|
default:
|
|
817
866
|
// unknown/optional types tolerated (forward compat)
|
|
818
|
-
this.diag?.(DIAG.FRAME_DROPPED, {
|
|
867
|
+
this.diag?.(DIAG.FRAME_DROPPED, {
|
|
868
|
+
kind: safeDiagnosticLabel(frame.type),
|
|
869
|
+
reason: 'unknown-type',
|
|
870
|
+
});
|
|
819
871
|
return;
|
|
820
872
|
}
|
|
821
873
|
}
|
|
@@ -937,7 +989,10 @@ export class ChannelClient {
|
|
|
937
989
|
// A non-resync snapshot IS the first authoritative data (even an empty delta =
|
|
938
990
|
// caught up) — end initial loading. A resync snapshot instead defers to the
|
|
939
991
|
// re-paged first history page below, so it must NOT end it here.
|
|
940
|
-
this.setState({
|
|
992
|
+
this.setState({
|
|
993
|
+
...(this.state.hasLoadedInitialData ? {} : this.initialDataLoaded()),
|
|
994
|
+
isSynced: true,
|
|
995
|
+
});
|
|
941
996
|
return;
|
|
942
997
|
}
|
|
943
998
|
// Resync: drop the local tail and re-page from the top per the pinned contract.
|
|
@@ -999,7 +1054,11 @@ export class ChannelClient {
|
|
|
999
1054
|
seq: row.seq,
|
|
1000
1055
|
hasClientMessageId: Boolean(row.clientMessageId),
|
|
1001
1056
|
moderationKind: row.moderationKind ?? null,
|
|
1002
|
-
op: prior.reconciledOptimistic
|
|
1057
|
+
op: prior.reconciledOptimistic
|
|
1058
|
+
? 'optimistic-reconcile'
|
|
1059
|
+
: prior.hadRow
|
|
1060
|
+
? 'replace-by-seq'
|
|
1061
|
+
: 'insert',
|
|
1003
1062
|
cursorBefore,
|
|
1004
1063
|
cursorAfter: this.resumeSeq,
|
|
1005
1064
|
});
|
|
@@ -1062,7 +1121,11 @@ export class ChannelClient {
|
|
|
1062
1121
|
// correlated rejection was superseded) means the send actually landed, so the
|
|
1063
1122
|
// row must render as delivered, not failed.
|
|
1064
1123
|
const { sendFailed: _sendFailed, sendFailureCode: _sendFailureCode, sendRetryable: _sendRetryable, ...restMeta } = m.meta;
|
|
1065
|
-
return {
|
|
1124
|
+
return {
|
|
1125
|
+
...m,
|
|
1126
|
+
messageId: seqToMessageId(seq),
|
|
1127
|
+
meta: { ...restMeta, optimistic: false, seq },
|
|
1128
|
+
};
|
|
1066
1129
|
});
|
|
1067
1130
|
next.sort(sortBySeq);
|
|
1068
1131
|
if (seq > this.resumeSeq)
|
|
@@ -1089,7 +1152,9 @@ export class ChannelClient {
|
|
|
1089
1152
|
}
|
|
1090
1153
|
const bySeq = new Map();
|
|
1091
1154
|
for (const m of this.state.messages) {
|
|
1092
|
-
if (m.meta?.optimistic &&
|
|
1155
|
+
if (m.meta?.optimistic &&
|
|
1156
|
+
typeof m.meta?.clientMessageId === 'string' &&
|
|
1157
|
+
incomingCmids.has(m.meta.clientMessageId)) {
|
|
1093
1158
|
continue; // reconciled by the incoming server row
|
|
1094
1159
|
}
|
|
1095
1160
|
bySeq.set(m.messageId, m);
|
|
@@ -1153,7 +1218,7 @@ export class ChannelClient {
|
|
|
1153
1218
|
// actually sent over an open socket. A closed socket returns false — no phantom
|
|
1154
1219
|
// "sent" bubble, and the caller keeps the composer text — so a disconnect between
|
|
1155
1220
|
// render and click can't silently swallow the message.
|
|
1156
|
-
const sent = this.sendFrame(
|
|
1221
|
+
const sent = this.sendFrame(CLIENT_KINDS.SEND, {
|
|
1157
1222
|
clientMessageId: args.clientMessageId,
|
|
1158
1223
|
text: args.text,
|
|
1159
1224
|
});
|
|
@@ -1203,7 +1268,7 @@ export class ChannelClient {
|
|
|
1203
1268
|
*/
|
|
1204
1269
|
readAck(readSeq, focused) {
|
|
1205
1270
|
this.lastReadAck = { readSeq, focused };
|
|
1206
|
-
return this.sendFrame(
|
|
1271
|
+
return this.sendFrame(CLIENT_KINDS.READ_ACK, { readSeq, focused });
|
|
1207
1272
|
}
|
|
1208
1273
|
/** Coalesced typing signal: at most one frame per TYPING_COALESCE_MS. */
|
|
1209
1274
|
typing() {
|
|
@@ -1211,15 +1276,15 @@ export class ChannelClient {
|
|
|
1211
1276
|
if (now - this.lastTypingSentAt < TYPING_COALESCE_MS)
|
|
1212
1277
|
return;
|
|
1213
1278
|
this.lastTypingSentAt = now;
|
|
1214
|
-
this.sendFrame(
|
|
1279
|
+
this.sendFrame(CLIENT_KINDS.TYPING, {});
|
|
1215
1280
|
}
|
|
1216
1281
|
presenceSubscribe() {
|
|
1217
1282
|
this.presenceSubscribed = true;
|
|
1218
|
-
this.sendFrame(
|
|
1283
|
+
this.sendFrame(CLIENT_KINDS.PRESENCE_SUBSCRIBE, {});
|
|
1219
1284
|
}
|
|
1220
1285
|
presenceUnsubscribe() {
|
|
1221
1286
|
this.presenceSubscribed = false;
|
|
1222
|
-
this.sendFrame(
|
|
1287
|
+
this.sendFrame(CLIENT_KINDS.PRESENCE_UNSUBSCRIBE, {});
|
|
1223
1288
|
this.setState({ presence: [] });
|
|
1224
1289
|
}
|
|
1225
1290
|
/** Fetch the page of history older than the oldest loaded message (epoch-aware by seq). */
|
|
@@ -1234,7 +1299,7 @@ export class ChannelClient {
|
|
|
1234
1299
|
this.setState({ isFetchingOlder: false });
|
|
1235
1300
|
}
|
|
1236
1301
|
requestHistory(beforeSeq, kind) {
|
|
1237
|
-
const sent = this.sendFrame(
|
|
1302
|
+
const sent = this.sendFrame(CLIENT_KINDS.HISTORY, { beforeSeq, limit: HISTORY_PAGE_MAX });
|
|
1238
1303
|
if (sent)
|
|
1239
1304
|
this.historyRequests.push(kind);
|
|
1240
1305
|
this.diag?.(DIAG.HISTORY_REQUEST, { beforeSeq, limit: HISTORY_PAGE_MAX, sent });
|
|
@@ -1251,7 +1316,7 @@ export class ChannelClient {
|
|
|
1251
1316
|
// stops the heartbeat and owns what happens next.
|
|
1252
1317
|
if (this.isInboundSilent())
|
|
1253
1318
|
return this.closeDeadSocket();
|
|
1254
|
-
this.sendFrame(
|
|
1319
|
+
this.sendFrame(CLIENT_KINDS.HEARTBEAT, {});
|
|
1255
1320
|
this.heartbeatTimer = this.timers.setTimeout(tick, HEARTBEAT_MS);
|
|
1256
1321
|
};
|
|
1257
1322
|
this.heartbeatTimer = this.timers.setTimeout(tick, HEARTBEAT_MS);
|