@vkontakte/calls-sdk 2.8.13-dev.7195a780.0 → 2.8.13-dev.7452edd3.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 (68) hide show
  1. package/CallsSDK.d.ts +40 -14
  2. package/abstract/BaseApi.d.ts +21 -19
  3. package/calls-sdk.cjs.js +7 -7
  4. package/calls-sdk.esm.js +5075 -3335
  5. package/classes/AudioFix.d.ts +2 -2
  6. package/classes/AudioOutput.d.ts +5 -3
  7. package/classes/CallRegistry.d.ts +17 -4
  8. package/classes/ExternalIdCache.d.ts +9 -13
  9. package/classes/MediaSource.d.ts +11 -2
  10. package/classes/SpecListener.d.ts +2 -2
  11. package/classes/StatsLogger.d.ts +1 -1
  12. package/classes/VolumesDetector.d.ts +0 -1
  13. package/classes/codec/LibVPxDecoder.d.ts +2 -2
  14. package/classes/codec/LibVPxEncoder.d.ts +2 -2
  15. package/classes/codec/WebCodecsDecoder.d.ts +2 -2
  16. package/classes/codec/WebCodecsEncoder.d.ts +2 -2
  17. package/classes/codec/WorkerBase.d.ts +3 -3
  18. package/classes/conversation/Conversation.d.ts +36 -11
  19. package/classes/conversation/Conversation.testUtils.d.ts +3 -0
  20. package/classes/conversation/DisplayLayoutRequester.d.ts +3 -1
  21. package/classes/screenshare/ScreenCaptureReceiver.d.ts +2 -2
  22. package/classes/screenshare/ScreenCaptureSender.d.ts +2 -2
  23. package/classes/screenshare/StreamBuilder.d.ts +3 -2
  24. package/classes/stat/ApiCallStats.d.ts +12 -0
  25. package/classes/stat/BatteryStats.d.ts +25 -0
  26. package/classes/stat/CallLifecycleStats.d.ts +7 -2
  27. package/classes/stat/CallStatTail.d.ts +76 -0
  28. package/classes/stat/ClientResourceStats.d.ts +41 -0
  29. package/classes/stat/CodecStatsAggregator.d.ts +5 -2
  30. package/classes/stat/ConversationStats.d.ts +24 -3
  31. package/classes/stat/NetworkAdaptationStats.d.ts +35 -0
  32. package/classes/stat/PeerConnectionStats.d.ts +22 -0
  33. package/classes/stat/SdpStats.d.ts +40 -0
  34. package/classes/stat/Stat.testUtils.d.ts +17 -0
  35. package/classes/stat/StatAggregator.d.ts +12 -3
  36. package/classes/stat/StatClock.d.ts +28 -0
  37. package/classes/stat/StatDrainScheduler.d.ts +113 -0
  38. package/classes/stat/StatEventCollector.d.ts +57 -0
  39. package/classes/stat/StatPings.d.ts +6 -2
  40. package/classes/stat/StatQueue.d.ts +119 -0
  41. package/classes/stat/StatSignalingCommands.d.ts +4 -2
  42. package/classes/transport/DirectTransport.d.ts +13 -2
  43. package/classes/transport/PerfStatReporter.d.ts +7 -1
  44. package/classes/transport/ServerTransport.d.ts +6 -2
  45. package/classes/transport/SimulcastSender.d.ts +2 -2
  46. package/classes/transport/Transport.d.ts +2 -2
  47. package/default/Api.d.ts +21 -11
  48. package/default/Signaling.d.ts +11 -3
  49. package/default/SignalingTransport.d.ts +5 -1
  50. package/enums/ActiveFeature.d.ts +19 -0
  51. package/enums/SessionError.d.ts +10 -0
  52. package/enums/Stat.d.ts +26 -1
  53. package/package.json +1 -1
  54. package/static/ApiTransport.d.ts +4 -0
  55. package/static/AuthData.d.ts +11 -1
  56. package/static/BrowserEnvironment.d.ts +90 -0
  57. package/static/External.d.ts +21 -0
  58. package/static/Params.d.ts +79 -3
  59. package/static/SessionStore.d.ts +29 -0
  60. package/static/SimulcastInfo.d.ts +1 -0
  61. package/static/WebRTCUtils.d.ts +5 -76
  62. package/types/ClientStatsEvent.d.ts +12 -0
  63. package/types/FastHangup.d.ts +7 -5
  64. package/types/IEventualStatLog.d.ts +1 -1
  65. package/types/Params.d.ts +1 -1
  66. package/types/PerfStatReporter.d.ts +26 -1
  67. package/types/SignalingMessage.d.ts +1 -0
  68. package/types/Statistics.d.ts +22 -7
@@ -0,0 +1,76 @@
1
+ import type StatEventCollector from './StatEventCollector';
2
+ import type { ClientStatsPayload } from './StatEventCollector';
3
+ export interface CallStatTailClock {
4
+ wallNow(): number;
5
+ }
6
+ /**
7
+ * Хвост звонка — заготовленный финиш на случай, если вкладка умрёт посреди
8
+ * звонка: call_finish с причиной terminated и итоги звонка. Лежит в отдельной
9
+ * таблице, не в очереди отправки, и раз в секунду перезаписывается. Время в
10
+ * нём — на шаг таймера позже текущего.
11
+ *
12
+ * Звонок закончился как обычно — хвост удаляется, а в очередь пишется
13
+ * настоящий финиш. Вкладку закрыли или убили — хвост остаётся, его перенесёт
14
+ * в очередь другая вкладка или следующий старт SDK.
15
+ *
16
+ * Пока звонок идёт, вкладка держит замок звонка. Свободный замок значит, что
17
+ * вкладки больше нет и хвост можно забирать.
18
+ */
19
+ export declare class CallStatTail {
20
+ private static _tabId;
21
+ /** id вкладки, создаётся при первом обращении */
22
+ static get tabId(): string;
23
+ static lockName(key: string): string;
24
+ static isSupported(): boolean;
25
+ private readonly _logger;
26
+ private readonly _summaries;
27
+ private readonly _clock;
28
+ private _status;
29
+ private _vcid;
30
+ private _key;
31
+ /** Первая запись хвоста; null — ещё не было или не удалась */
32
+ private _creation;
33
+ private _timer;
34
+ /** Отпускает замок звонка */
35
+ private _releaseLock;
36
+ /** Запросы замка подряд, которые закончились без выдачи */
37
+ private _lockFailures;
38
+ /** Вызывается, когда хвост забрала другая вкладка */
39
+ private _onAdopted;
40
+ /** @param summaries итоги звонка на текущий момент: codec_usage и сводки сигналинга */
41
+ constructor(logger: StatEventCollector, summaries: () => ClientStatsPayload[], clock?: CallStatTailClock);
42
+ /** Начинает вести хвост звонка: сначала замок, потом запись. */
43
+ start(vcid: string, onAdopted: () => void): void;
44
+ /**
45
+ * Звонок закончился: снимает хвост и вызывает write, который пишет
46
+ * настоящий финиш. Если хвост уже забрала другая вкладка, write не
47
+ * вызывается.
48
+ */
49
+ complete(write: () => void): void;
50
+ /** Снимает хвост без финиша. */
51
+ stop(): void;
52
+ /**
53
+ * Вкладка закрывается: хвост остаётся в таблице как готовый финиш, замок
54
+ * снимаем. Писать что-то в IndexedDB из pagehide бесполезно: запись может
55
+ * не успеть.
56
+ */
57
+ abandon(): void;
58
+ private _requestLock;
59
+ /** Замок держится, пока не разрешён возвращённый промис. */
60
+ private _onLockGranted;
61
+ /**
62
+ * Замок отпустили мы, его отняли (Safari при приостановке страницы) или
63
+ * не выдали. Отнятый просим снова: пока его нет, хвост может забрать
64
+ * другая вкладка, и следующая запись это заметит.
65
+ */
66
+ private _onLockGone;
67
+ private _onTimer;
68
+ /** Первая запись создаёт хвост, остальные обновляют его, только если он ещё лежит. */
69
+ private _write;
70
+ private _buildTail;
71
+ private _collectSummaries;
72
+ private _adopt;
73
+ private _finish;
74
+ private _release;
75
+ private _clearTimer;
76
+ }
@@ -0,0 +1,41 @@
1
+ import ActiveFeature from '../../enums/ActiveFeature';
2
+ import { type DebugLogger } from '../../static/Debug';
3
+ /** Метрики ресурсов клиента за интервал между отправками callStat */
4
+ export type ClientResourceReport = {
5
+ /** Максимум участников за интервал */
6
+ participants_max?: number;
7
+ /** Фичи, включенные в любой момент интервала */
8
+ active_features?: string[];
9
+ /** Максимальная площадь окна за интервал, в мегапикселях */
10
+ app_total_area_mpx_max?: number;
11
+ };
12
+ export type ClientResourceSources = {
13
+ getParticipantsCount: () => number;
14
+ getActiveFeatures: () => ActiveFeature[];
15
+ };
16
+ /**
17
+ * Копит ресурсные метрики между отправками callStat: репортер забирает накопленное
18
+ * через takeReport() и начинает копить заново.
19
+ *
20
+ * Замеряем чаще, чем отправляем, потому что нас интересует не состояние в момент отправки,
21
+ * а что происходило за интервал: пик участников и все фичи, которые успели поработать.
22
+ */
23
+ export declare class ClientResourceStats {
24
+ private readonly _sources;
25
+ private readonly _debug;
26
+ private _timer;
27
+ private _participantsMax;
28
+ private _areaMpxMax;
29
+ private _features;
30
+ constructor(sources: ClientResourceSources, debug?: DebugLogger);
31
+ /** Забрать накопленное за интервал и начать копить заново */
32
+ takeReport(): ClientResourceReport;
33
+ destroy(): void;
34
+ private _sample;
35
+ /**
36
+ * Площадь окна в физических пикселях: CSS-пиксели умножаем на devicePixelRatio,
37
+ * иначе одно и то же окно на экране с ретиной и без давало бы одинаковую цифру.
38
+ * Приложение в вебе — это одна вкладка, суммировать нечего.
39
+ */
40
+ private _measureAreaMpx;
41
+ }
@@ -1,5 +1,5 @@
1
1
  import { StatItem } from '../../types/Statistics';
2
- import StatsLogger from '../StatsLogger';
2
+ import StatEventCollector, { ClientStatsPayload } from './StatEventCollector';
3
3
  import TransportTopology from '../../enums/TransportTopology';
4
4
  export type CodecKind = 'audio' | 'video';
5
5
  /**
@@ -11,9 +11,12 @@ export declare class CodecStatsAggregator {
11
11
  private readonly _logger;
12
12
  private readonly _codecUsages;
13
13
  private readonly getCurrentTransportTopology;
14
- constructor(logger: StatsLogger, getCurrentTransportTopology: () => TransportTopology | undefined);
14
+ constructor(logger: StatEventCollector, getCurrentTransportTopology: () => TransportTopology | undefined);
15
15
  reportUsage(stat: StatItem): void;
16
+ /** События codec_usage, которые ушли бы сейчас при конце звонка: по текущему кодеку аудио и видео. */
17
+ usageReports(): ClientStatsPayload[];
16
18
  private saveUsage;
17
19
  private report;
20
+ private _toLog;
18
21
  destroy(): void;
19
22
  }
@@ -2,21 +2,42 @@ import BaseApi from '../../abstract/BaseApi';
2
2
  import BaseLogger from '../../abstract/BaseLogger';
3
3
  import TransportTopology from '../../enums/TransportTopology';
4
4
  import HangupReason from '../HangupReason';
5
- import StatsLogger from '../StatsLogger';
5
+ import StatEventCollector from './StatEventCollector';
6
+ import { ApiCallStats } from './ApiCallStats';
7
+ import { BatteryStats } from './BatteryStats';
6
8
  import { CallLifecycleStats } from './CallLifecycleStats';
9
+ import { ClientResourceSources, ClientResourceStats } from './ClientResourceStats';
7
10
  import { CodecStatsAggregator } from './CodecStatsAggregator';
8
11
  import { StatAggregator } from './StatAggregator';
9
12
  import { StatPings } from './StatPings';
10
13
  import { StatSignalingCommands } from './StatSignalingCommands';
11
14
  export declare class ConversationStats {
12
- readonly logger: StatsLogger;
15
+ readonly logger: StatEventCollector;
13
16
  readonly lifecycle: CallLifecycleStats;
14
17
  readonly statAggregator: StatAggregator;
15
18
  readonly codecStatsAggregator: CodecStatsAggregator;
16
19
  readonly pings: StatPings;
17
20
  readonly signalingCommands: StatSignalingCommands;
18
- constructor(api: BaseApi, externalLogger: BaseLogger | null, getConversationId: () => string | null, getTopology: () => TransportTopology | undefined);
21
+ readonly apiCalls: ApiCallStats;
22
+ readonly resources: ClientResourceStats;
23
+ readonly battery: BatteryStats;
24
+ private readonly _api;
25
+ /** Хвост звонка в очереди; null — выключен */
26
+ private readonly _tail;
27
+ private _unloading;
28
+ constructor(api: BaseApi, externalLogger: BaseLogger | null, getConversationId: () => string | null, getTopology: () => TransportTopology | undefined, resourceSources: ClientResourceSources);
29
+ /**
30
+ * Вкладка закрывается (pagehide). Финиш и итоги звонка уже лежат в хвосте
31
+ * звонка, поэтому в очередь больше не пишем. Сводки пингов и команд
32
+ * логируем только ради onClientStats: destroy() очищает их без записи.
33
+ */
34
+ prepareForUnload(): void;
35
+ /** Вкладка пережила pagehide: снова пишем в очередь. */
36
+ restoreAfterUnload(): void;
37
+ resetPingBaseline(): void;
19
38
  logHangup(reason: HangupReason): void;
20
39
  flushCallMetrics(): void;
21
40
  destroy(): void;
41
+ /** Итоги звонка, которые уйдут в хвосте, если вкладка умрёт посреди звонка. */
42
+ private _callSummaries;
22
43
  }
@@ -0,0 +1,35 @@
1
+ import { StatAggregator } from './StatAggregator';
2
+ /** Причина события client_requested_server_topology */
3
+ export declare const enum ServerTopologyReason {
4
+ NO_CONNECTION_TIMEOUT = "no_connection_timeout",
5
+ CONNECTION_FAILED = "connection_failed",
6
+ SUCCESS_CONNECTION = "success_connection",
7
+ /** Возможна на бэке, в вебе пока не поддержана */
8
+ NO_DATA_TIMEOUT = "no_data_timeout",
9
+ /** Возможна на бэке, в вебе пока не поддержана */
10
+ SUCCESS_AUDIO = "success_audio",
11
+ /** Возможна на бэке, в вебе пока не поддержана */
12
+ CONNECTION_ACCEPTED_FAILED = "connection_accepted_failed"
13
+ }
14
+ export declare const enum NetworkQuality {
15
+ GOOD = "good",
16
+ BAD_1 = "bad_1",
17
+ BAD_2 = "bad_2"
18
+ }
19
+ export type VideoEncoderChange = {
20
+ /** Круговая задержка, мс */
21
+ rtt: number;
22
+ /** Потери, проценты */
23
+ loss: number;
24
+ networkQuality: NetworkQuality;
25
+ /** mimeType исходящего видео, например video/VP8 */
26
+ mimeType: string | undefined;
27
+ source: 'camera' | 'screencast' | 'all';
28
+ };
29
+ /** Отправляет в стату решения клиента по смене топологии и настроек видеоэнкодера */
30
+ export declare class NetworkAdaptationStats {
31
+ private readonly _statAggregator;
32
+ constructor(statAggregator: StatAggregator | null);
33
+ logServerTopologyRequested(reason: ServerTopologyReason, elapsed: number): void;
34
+ logVideoEncoderChanged(change: VideoEncoderChange): void;
35
+ }
@@ -0,0 +1,22 @@
1
+ import { StatAggregator } from './StatAggregator';
2
+ /** Стата жизненного цикла RTCPeerConnection */
3
+ export declare class PeerConnectionStats {
4
+ private readonly _statAggregator;
5
+ private _pc;
6
+ private _lastSignalingStateChange;
7
+ private _lastGatheringStateChange;
8
+ private _lastConnectionState;
9
+ private readonly _handleConnectionStateChange;
10
+ private readonly _handleSignalingStateChange;
11
+ private readonly _handleGatheringStateChange;
12
+ private readonly _handleIceCandidateError;
13
+ constructor(statAggregator: StatAggregator | null);
14
+ observe(pc: RTCPeerConnection): void;
15
+ /** Вызывать перед закрытием соединения: отсюда уходят последнее состояние и closed */
16
+ stop(): void;
17
+ logIceRestart(): void;
18
+ logIceCandidateAddFailed(candidate: RTCIceCandidateInit, error: unknown): void;
19
+ private _sinceLastChange;
20
+ private _logConnectionState;
21
+ private _log;
22
+ }
@@ -0,0 +1,40 @@
1
+ import { StatAggregator } from './StatAggregator';
2
+ type SdpDescription = {
3
+ type: RTCSdpType;
4
+ sdp?: string;
5
+ };
6
+ /**
7
+ * Отправляет в стату согласование SDP: что сгенерировали, что получили от собеседника
8
+ * и на какой операции сломались.
9
+ */
10
+ export declare class SdpStats {
11
+ private readonly _statAggregator;
12
+ private _pc;
13
+ private _gatheringStartedAt;
14
+ private _remoteSdpReceivedAt;
15
+ private readonly _handleGatheringStateChange;
16
+ constructor(statAggregator: StatAggregator | null);
17
+ /** Начинает следить за сбором кандидатов, предыдущее соединение перестает слушать */
18
+ observe(pc: RTCPeerConnection): void;
19
+ stop(): void;
20
+ /** Сгенерировали свой SDP */
21
+ logSdpGenerated(type: RTCSdpType): void;
22
+ /** Собрали свой ICE-кандидат */
23
+ logCandidateGenerated(candidate: RTCIceCandidate): void;
24
+ /** Получили SDP от собеседника */
25
+ logSdpReceived(type: RTCSdpType): void;
26
+ /** Получили ICE-кандидата от собеседника */
27
+ logCandidateReceived(candidate: RTCIceCandidateInit): void;
28
+ /** Не смогли сгенерировать SDP */
29
+ logCreateFailed(type: RTCSdpType, error: unknown): void;
30
+ /** Не смогли применить свой SDP */
31
+ logSetLocalFailed(sdp: SdpDescription, error: unknown): void;
32
+ /** Не смогли применить SDP собеседника */
33
+ logSetRemoteFailed(sdp: SdpDescription, error: unknown): void;
34
+ destroy(): void;
35
+ private _logFailure;
36
+ private _log;
37
+ }
38
+ /** Браузер может спрятать локальный адрес за именем вида <uuid>.local, такой кандидат в стату не шлём */
39
+ export declare function isIpAddress(address: string): boolean;
40
+ export {};
@@ -0,0 +1,17 @@
1
+ import type { MockInstance } from 'vitest';
2
+ import { StatTail } from './StatQueue';
3
+ /**
4
+ * Дожидается работы, которую код запускает в фоне и не отдаёт промис: ждёт всё,
5
+ * что вернули вызовы шпионов, пока не перестанут появляться новые вызовы.
6
+ *
7
+ * Замена опросу vi.waitFor: тот сдаётся через секунду, а IndexedDB на
8
+ * загруженной машине отвечает дольше. Шпионы должны пропускать вызов к
9
+ * настоящему методу (vi.spyOn без подмены).
10
+ */
11
+ export declare function settleCalls(...spies: MockInstance[]): Promise<void>;
12
+ /** Ключ хвоста звонка этой вкладки. */
13
+ export declare function tailKey(vcid: string): string;
14
+ /** Все хвосты звонков на диске. Чтения хвостов у StatQueue нет: таблицу читаем напрямую. */
15
+ export declare function readTails(): Promise<StatTail[]>;
16
+ /** Хвост звонка этой вкладки. */
17
+ export declare function readTail(vcid: string): Promise<StatTail | undefined>;
@@ -1,14 +1,23 @@
1
1
  import { ICallStatLog } from '../../types/PerfStatReporter';
2
2
  import { IEventualStatLog } from '../../types/IEventualStatLog';
3
- import StatsLogger from '../StatsLogger';
3
+ import StatEventCollector from './StatEventCollector';
4
+ import { ClientResourceStats } from './ClientResourceStats';
4
5
  export declare class StatAggregator {
5
6
  private readonly _logger;
7
+ private readonly _resources;
6
8
  private readonly _eventualLogs;
7
9
  private _lastCallStatContext;
8
- constructor(logger: StatsLogger);
10
+ constructor(logger: StatEventCollector, resources?: ClientResourceStats | null);
9
11
  logCallStat(params: ICallStatLog): void;
10
- /** @link https://wiki.odkl.ru/pages/viewpage.action?pageId=100892588 */
12
+ /**
13
+ * Событие уходит с теми полями, что переданы. Без value — уходит без value.
14
+ * @link https://wiki.odkl.ru/pages/viewpage.action?pageId=100892588
15
+ */
11
16
  logEventualStat(params: IEventualStatLog): void;
17
+ /** value — время от метки с тем же именем, что у события. Метки нет — событие не отправляем. */
18
+ logEventualStatSinceMark(params: IEventualStatLog & {
19
+ value?: never;
20
+ }): void;
12
21
  destroy(): void;
13
22
  private _getCallStatContext;
14
23
  private _flushEventualStats;
@@ -0,0 +1,28 @@
1
+ import BaseApi from '../../abstract/BaseApi';
2
+ /**
3
+ * Поправка часов устройства к серверному времени: delta = Date.now() -
4
+ * serverTime, меряется раз за сессию. Свои записи, сделанные до измерения,
5
+ * получают поправку задним числом.
6
+ */
7
+ export default abstract class StatClock {
8
+ private static _api;
9
+ private static _delta;
10
+ private static _pending;
11
+ /** Ключи своих записей, ушедших в очередь до измерения. */
12
+ private static _idsAwaitingDelta;
13
+ private static _lastFailureAt;
14
+ static setApi(value: BaseApi): void;
15
+ /** Дельта из кэша, без запроса. null — ещё не измерена. */
16
+ static delta(): number | null;
17
+ /** Запускает измерение, если его ещё нет. Результата не ждёт. */
18
+ static ensure(): void;
19
+ /** Дельта для отправки: измеренная, иначе 0 — время устройства. */
20
+ static get(): Promise<number>;
21
+ /**
22
+ * Вызывается коллектором после записи события без дельты. Ключ приходит
23
+ * после append, поэтому измерение могло закончиться раньше.
24
+ */
25
+ static noteAppended(id: number): void;
26
+ private static _hasCredentials;
27
+ private static _measure;
28
+ }
@@ -0,0 +1,113 @@
1
+ import BaseApi from '../../abstract/BaseApi';
2
+ import { StatQueueEntry } from './StatQueue';
3
+ /**
4
+ * Отправитель очереди статистики: один на вкладку, живёт от CallsSDK.init()
5
+ * и не завязан на активный звонок.
6
+ */
7
+ export default abstract class StatDrainScheduler {
8
+ private static _api;
9
+ private static _state;
10
+ private static _timer;
11
+ private static _localCount;
12
+ /** Звонок закончился во время отправки: его события уйдут через секунду после неё, а не через 30 с тишины. */
13
+ private static _callFinishedDuringDrain;
14
+ private static _blockedSession;
15
+ /** Буфер прямой отправки: события, которые не легли в хранилище. */
16
+ private static _directBuffer;
17
+ private static _directTimer;
18
+ private static _tailRecheckTimer;
19
+ static setApi(value: BaseApi): void;
20
+ /** Первая отправка: забирает события, не ушедшие в прошлые запуски, и хвосты звонков закрытых вкладок. */
21
+ static start(value: BaseApi): void;
22
+ /** Вызывается коллектором после записи события в очередь. */
23
+ static notifyAppended(): void;
24
+ /**
25
+ * Звонок закончился: его события уходят через секунду, а не через 30 с
26
+ * тишины. Секунда нужна, чтобы успели записаться все события конца звонка.
27
+ */
28
+ static notifyCallFinished(): void;
29
+ /**
30
+ * Один проход отправки по всем пользователям в очереди: текущий — через
31
+ * живую сессию, остальные — через сохранённый session_key. События
32
+ * пользователя X уходят только под сессией X.
33
+ *
34
+ * @returns true, если у кого-то был полный батч и имеет смысл повторить.
35
+ */
36
+ static drainOnce(): Promise<boolean>;
37
+ /**
38
+ * FALLBACK для событий, не попавших в хранилище: копятся в памяти и уходят
39
+ * пачкой, без подтверждения и повтора.
40
+ *
41
+ * Чужие события не отправляются: запрос уходит под текущей сессией.
42
+ */
43
+ static sendDirect(entry: StatQueueEntry): void;
44
+ private static _currentUserId;
45
+ private static _firstDrain;
46
+ /**
47
+ * Удаляет журналы закрытия вкладки из бет 2.8.13-beta.2…beta.11: читать
48
+ * их больше некому, а ключи остались бы в localStorage навсегда.
49
+ *
50
+ * TODO: удалить через пару релизов после 2.8.13.
51
+ */
52
+ private static _removeUnloadJournals;
53
+ /**
54
+ * Переносит в очередь хвосты звонков, вкладок которых больше нет:
55
+ * свободный замок звонка значит, что вкладку закрыли или убили. Если
56
+ * вкладка жива, проверяем снова через TAIL_RECHECK_DELAY_MS. Иначе
57
+ * вкладка, которой самой нечего отправлять, забрала бы хвост только при
58
+ * следующей своей отправке. Вызывается под замком отправки, не бросает.
59
+ *
60
+ * @returns число перенесённых хвостов.
61
+ */
62
+ private static _adoptCallTails;
63
+ private static _armTailRecheck;
64
+ private static _clearTailRecheckTimer;
65
+ /** @returns число записей в батче. */
66
+ private static _drainCurrent;
67
+ /**
68
+ * События другого пользователя уходят под его сохранённым session_key:
69
+ * одна попытка, без перелогина. Неотправленное не удаляем, пользователь
70
+ * может снова войти в этой вкладке.
71
+ *
72
+ * @returns число записей в батче.
73
+ */
74
+ private static _drainStored;
75
+ /**
76
+ * Взаимное исключение между вкладками одного origin.
77
+ *
78
+ * Транзакцию IndexedDB нельзя держать открытой на время запроса, поэтому
79
+ * чтение и удаление разнесены, и без блокировки соседняя вкладка отправила
80
+ * бы тот же батч повторно.
81
+ */
82
+ private static _withLock;
83
+ /**
84
+ * Ретраятся ошибки, в которых виновато не содержимое батча: сеть, 5xx,
85
+ * http-429, ошибка сессии. Остальное непроходимо и приводит к удалению
86
+ * батча, иначе одно всегда отвергаемое событие залипило бы очередь.
87
+ */
88
+ private static _isSessionError;
89
+ /**
90
+ * Транспорт не смог переавторизоваться, чаще всего из-за протухшего
91
+ * authToken хоста. События не удаляем: уйдут после новой сессии.
92
+ */
93
+ private static _isReauthorizeFailure;
94
+ /**
95
+ * Останавливает отправку за текущего пользователя до смены session_key:
96
+ * другой ключ означает, что авторизация прошла заново. События других
97
+ * пользователей не трогает.
98
+ */
99
+ private static _blockSession;
100
+ private static _isSessionBlocked;
101
+ /** Сессия или токен протухли: 102, 103, 104 или 401. */
102
+ private static _isAuthError;
103
+ private static _isRetryable;
104
+ /** @param delta текущая поправка — для записей, у которых своей нет. */
105
+ private static _toClientStats;
106
+ private static _clearDirectTimer;
107
+ private static _armDirect;
108
+ /** Отправляет накопленный буфер прямой отправки одним запросом. */
109
+ private static _flushDirect;
110
+ private static _clearTimer;
111
+ private static _arm;
112
+ private static _drain;
113
+ }
@@ -0,0 +1,57 @@
1
+ import BaseApi from '../../abstract/BaseApi';
2
+ import BaseLogger from '../../abstract/BaseLogger';
3
+ import StatLog from '../../enums/StatLog';
4
+ import TransportTopology from '../../enums/TransportTopology';
5
+ import { StatQueueEntry } from './StatQueue';
6
+ export type ClientStatsPayload = Record<string, string | string[] | number | boolean | undefined | null>;
7
+ export type LogClientStatsOptions = {
8
+ /** Время наступления события. По умолчанию — момент записи. */
9
+ occurredAt?: number;
10
+ /** Явный conversationId. По умолчанию берётся из провайдера. */
11
+ conversationId?: string | null;
12
+ /** Только для старой схемы (`Params.statQueueEnabled === false`): отправить батч сразу. */
13
+ immediately?: boolean;
14
+ /** Только для старой схемы: придержать событие, пока не посчитана поправка на серверное время. */
15
+ deferUntilServerTime?: boolean;
16
+ };
17
+ /**
18
+ * Собирает событие статистики и кладёт его в персистентную очередь. Инстанс на
19
+ * звонок, отправкой занимается StatDrainScheduler.
20
+ */
21
+ export default class StatEventCollector extends BaseLogger {
22
+ /** Общий на вкладку: uid может стать известен уже после смерти коллектора. */
23
+ private static _pending;
24
+ private static _pendingOverflow;
25
+ private static _remember;
26
+ private static _takePending;
27
+ private readonly _externalLogger;
28
+ private readonly _conversationIdProvider;
29
+ private readonly _topologyProvider;
30
+ private _unloadMode;
31
+ /** Старая схема: не null, когда ручка выключена, тогда очередь не используется. */
32
+ private readonly _legacy;
33
+ constructor(api: BaseApi, externalLogger: BaseLogger | null, conversationIdProvider: () => string | null, topologyProvider?: () => TransportTopology | undefined);
34
+ log(name: StatLog, value?: string, immediately?: boolean): void;
35
+ /**
36
+ * Вкладка закрывается (pagehide): в очередь больше не пишем. Запись в
37
+ * IndexedDB отсюда может не успеть. А если страница уйдёт в bfcache,
38
+ * запись допишется после возврата и задвоит финиш из хвоста звонка.
39
+ * Финиш уже лежит в хвосте, его отправит другая вкладка или следующий
40
+ * старт SDK.
41
+ */
42
+ enterUnloadMode(): void;
43
+ /** Вкладка пережила pagehide (bfcache, отменённая навигация): снова пишем в очередь. */
44
+ exitUnloadMode(): void;
45
+ logClientStats(data: ClientStatsPayload, options?: LogClientStatsOptions): void;
46
+ /**
47
+ * Запись очереди для события без записи в очередь и без External.onClientStats.
48
+ * null — старая схема, uid неизвестен или в памяти ждут события без uid.
49
+ */
50
+ buildEntry(data: ClientStatsPayload, options?: LogClientStatsOptions): StatQueueEntry | null;
51
+ destroy(): void;
52
+ /** Собирает запись очереди без владельца: conversationId, время, поправку и данные события. */
53
+ private _makeEntry;
54
+ private _store;
55
+ private _getConversationId;
56
+ private _getCallTopology;
57
+ }
@@ -1,10 +1,10 @@
1
1
  import { SignalingTransportType } from '../../enums/SignalingTransportStat';
2
- import StatsLogger from '../StatsLogger';
2
+ import StatEventCollector, { ClientStatsPayload } from './StatEventCollector';
3
3
  export declare class StatPings {
4
4
  private readonly _logger;
5
5
  private trackerByTransport;
6
6
  private lastSeen;
7
- constructor(logger: StatsLogger);
7
+ constructor(logger: StatEventCollector);
8
8
  /**
9
9
  * Вызывать при входящем ping в сигналингу.
10
10
  * Будет произведен расчет времени между пингами.
@@ -12,5 +12,9 @@ export declare class StatPings {
12
12
  mark(transport: SignalingTransportType): void;
13
13
  /** Отправляем данные в стату */
14
14
  logMetrics(): void;
15
+ /** Сводки на текущий момент, без отправки и без сброса. */
16
+ summaries(): ClientStatsPayload[];
17
+ /** Сбрасываем baseline после скрытия страницы, чтобы фоновой простой не стал интервалом ping. */
18
+ resetBaseline(): void;
15
19
  destroy(): void;
16
20
  }