@pickleball/expo-sdk 0.1.1 → 1.0.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.
- package/CHANGELOG.md +65 -0
- package/README.md +163 -40
- package/dist/advanced-DcbHBCGb.d.ts +300 -0
- package/dist/advanced-DeQwg7MK.d.cts +300 -0
- package/dist/advanced.cjs +47 -0
- package/dist/advanced.d.cts +3 -0
- package/dist/advanced.d.ts +3 -0
- package/dist/advanced.js +47 -0
- package/dist/camera-controls-By8tHigY.d.cts +97 -0
- package/dist/camera-controls-rCBUGGPC.d.ts +97 -0
- package/dist/chunk-2JHULJZ7.js +265 -0
- package/dist/chunk-62YV3PCD.cjs +139 -0
- package/dist/chunk-6F3HN6US.js +50 -0
- package/dist/chunk-6YE5BL3A.cjs +50 -0
- package/dist/chunk-7OX2DSKI.cjs +33 -0
- package/dist/chunk-7Z7TN5N3.js +318 -0
- package/dist/chunk-DHDX3ZOO.js +800 -0
- package/dist/chunk-GXQYMPHE.cjs +9 -0
- package/dist/chunk-IUIRVKJI.cjs +2290 -0
- package/dist/chunk-KFQGP6VL.js +33 -0
- package/dist/chunk-LCWGNGEN.cjs +265 -0
- package/dist/chunk-TBUEXRFQ.cjs +318 -0
- package/dist/chunk-XGP2MN5R.js +2290 -0
- package/dist/chunk-XL4CISVQ.js +9 -0
- package/dist/chunk-YGSQ25UA.cjs +800 -0
- package/dist/chunk-ZSKSXOCZ.js +139 -0
- package/dist/convex-command-subscription-Cabh9tND.d.ts +41 -0
- package/dist/convex-command-subscription-DUgpu75l.d.cts +41 -0
- package/dist/device-agent/index.cjs +1427 -0
- package/dist/device-agent/index.d.cts +493 -0
- package/dist/device-agent/index.d.ts +493 -0
- package/dist/device-agent/index.js +1427 -0
- package/dist/engine-BSvSdCSE.d.cts +741 -0
- package/dist/engine-BSvSdCSE.d.ts +741 -0
- package/dist/index.cjs +426 -6174
- package/dist/index.d.cts +299 -1400
- package/dist/index.d.ts +299 -1400
- package/dist/index.js +413 -6063
- package/dist/match/index.cjs +2456 -0
- package/dist/match/index.d.cts +253 -0
- package/dist/match/index.d.ts +253 -0
- package/dist/match/index.js +2456 -0
- package/dist/match/remote/index.cjs +21 -0
- package/dist/match/remote/index.d.cts +67 -0
- package/dist/match/remote/index.d.ts +67 -0
- package/dist/match/remote/index.js +21 -0
- package/dist/match/voice/index.cjs +23 -0
- package/dist/match/voice/index.d.cts +114 -0
- package/dist/match/voice/index.d.ts +114 -0
- package/dist/match/voice/index.js +23 -0
- package/dist/plugin.cjs +52 -71
- package/dist/plugin.d.cts +28 -1
- package/dist/plugin.d.ts +28 -1
- package/dist/plugin.js +26 -23
- package/dist/realtime/index.cjs +31 -0
- package/dist/realtime/index.d.cts +15 -0
- package/dist/realtime/index.d.ts +15 -0
- package/dist/realtime/index.js +31 -0
- package/dist/types-CrmcX408.d.cts +176 -0
- package/dist/types-CrmcX408.d.ts +176 -0
- package/dist/types-DiKWjwvN.d.cts +96 -0
- package/dist/types-DiKWjwvN.d.ts +96 -0
- package/dist/ui/index.cjs +1297 -0
- package/dist/ui/index.d.cts +493 -0
- package/dist/ui/index.d.ts +493 -0
- package/dist/ui/index.js +1297 -0
- package/dist/version.cjs +4 -32
- package/dist/version.js +2 -1
- package/package.json +95 -15
- package/src/advanced.ts +49 -0
- package/src/auto-resume.tsx +259 -0
- package/src/camera-controls.ts +99 -0
- package/src/camera-grant-provider.ts +392 -0
- package/src/camera-settings.ts +133 -0
- package/src/contracts.ts +73 -0
- package/src/device-agent/agent.ts +696 -0
- package/src/device-agent/beat-policy.ts +46 -0
- package/src/device-agent/component.tsx +60 -0
- package/src/device-agent/convex-beat-transport.ts +140 -0
- package/src/device-agent/convex-command-subscription.ts +101 -0
- package/src/device-agent/convex-control-channel.ts +88 -0
- package/src/device-agent/http-transport.ts +164 -0
- package/src/device-agent/index.ts +26 -0
- package/src/device-agent/mqtt-channel.ts +379 -0
- package/src/device-agent/mqtt-codec.ts +347 -0
- package/src/device-agent/tournament.ts +352 -0
- package/src/engine.ts +601 -20
- package/src/errors.ts +16 -0
- package/src/http-session-provider.ts +21 -183
- package/src/index.ts +117 -14
- package/src/local-video-quality.ts +43 -0
- package/src/match/index.ts +29 -0
- package/src/match/provider.tsx +4 -2
- package/src/match/remote/hid-gesture.ts +10 -2
- package/src/match/remote/types.ts +6 -4
- package/src/match/voice/scheduler.ts +6 -1
- package/src/match/voice/service.ts +16 -4
- package/src/native-runtime.ts +283 -77
- package/src/plugin.ts +54 -20
- package/src/provider.tsx +129 -22
- package/src/realtime/index.ts +56 -0
- package/src/resume-policy.ts +85 -0
- package/src/resume.ts +132 -0
- package/src/rtmp-room-adapter.ts +348 -0
- package/src/session-grant.ts +256 -0
- package/src/ui/camera-screen.tsx +484 -0
- package/src/ui/controls-sheet.tsx +445 -0
- package/src/ui/copy.ts +326 -0
- package/src/ui/helpers.ts +105 -0
- package/src/ui/hooks.ts +59 -0
- package/src/ui/index.ts +42 -0
- package/src/ui/preview-view.tsx +39 -0
- package/src/ui/primitives.tsx +304 -0
- package/src/uplink-check.ts +173 -0
- package/dist/chunk-VHLUJQG5.js +0 -17
- package/src/livekit-room-adapter.ts +0 -353
- package/src/livestream-screen.tsx +0 -980
|
@@ -0,0 +1,741 @@
|
|
|
1
|
+
type LivestreamState = "idle" | "preparing" | "preview" | "connecting"
|
|
2
|
+
/** Đã vào phòng, chưa phát — chờ lệnh phát từ app hoặc dashboard. */
|
|
3
|
+
| "standby" | "live" | "reconnecting" | "ending" | "ended" | "error";
|
|
4
|
+
type SdkErrorCode = "PERMISSION_DENIED" | "NETWORK_UNAVAILABLE" | "TOKEN_EXPIRED" | "SDK_UPGRADE_REQUIRED" | "SESSION_CONFLICT" | "RECORDING_FAILED" | "CONSENT_REQUIRED" | "SDK_DISABLED" | "UNKNOWN";
|
|
5
|
+
/**
|
|
6
|
+
* Vì sao 409 SESSION_CONFLICT bị nhận — ba ca cần app hành xử KHÁC NHAU mà
|
|
7
|
+
* trước đây chỉ phân biệt được bằng đọc chuỗi tiếng Anh trong `message`:
|
|
8
|
+
* - `superseded`: grant này đã bị grant mới thay (đối tác cấp lại camera-grant)
|
|
9
|
+
* → app DỪNG tại chỗ, không `end`, không quay số lại.
|
|
10
|
+
* - `ended`: phiên đã kết thúc → dọn state, không thử lại.
|
|
11
|
+
* - `expired`: phiên đã quá trần thời lượng credential → xin grant/phiên mới.
|
|
12
|
+
*/
|
|
13
|
+
type SdkConflictReason = "superseded" | "ended" | "expired";
|
|
14
|
+
interface SdkError {
|
|
15
|
+
code: SdkErrorCode;
|
|
16
|
+
message: string;
|
|
17
|
+
/** Chỉ có ở 409 SESSION_CONFLICT của namespace /api/v2/camera. */
|
|
18
|
+
reason?: SdkConflictReason;
|
|
19
|
+
/** Chỉ có ở 429 — cùng số với header `Retry-After` (giây, làm tròn lên). */
|
|
20
|
+
retryAfterMs?: number;
|
|
21
|
+
}
|
|
22
|
+
interface BootstrapInput {
|
|
23
|
+
sdkVersion: string;
|
|
24
|
+
platform: "ios" | "android";
|
|
25
|
+
bundleId: string;
|
|
26
|
+
installationId: string;
|
|
27
|
+
}
|
|
28
|
+
interface SdkRemoteConfig {
|
|
29
|
+
enabled: boolean;
|
|
30
|
+
protocolVersion: 1;
|
|
31
|
+
minSdkVersion: string;
|
|
32
|
+
recommendedSdkVersion: string;
|
|
33
|
+
videoQuality: 720 | 1080;
|
|
34
|
+
reconnectTimeoutMs: number;
|
|
35
|
+
backgroundGraceMs: number;
|
|
36
|
+
telemetryIntervalMs: number;
|
|
37
|
+
maxSessionDurationMs: number;
|
|
38
|
+
/** Tự kết thúc phiên nếu ngồi standby quá lâu không phát (mặc định 15 phút). */
|
|
39
|
+
standbyTimeoutMs?: number;
|
|
40
|
+
}
|
|
41
|
+
interface SdkBootstrap {
|
|
42
|
+
config: SdkRemoteConfig;
|
|
43
|
+
serverTime: number;
|
|
44
|
+
rolloutEnabled: boolean;
|
|
45
|
+
message?: string;
|
|
46
|
+
}
|
|
47
|
+
interface StartLivestreamInput {
|
|
48
|
+
externalSessionId: string;
|
|
49
|
+
title: string;
|
|
50
|
+
visibility?: "public" | "private";
|
|
51
|
+
consentVersion: string;
|
|
52
|
+
metadata?: Record<string, string>;
|
|
53
|
+
/**
|
|
54
|
+
* true: vào phòng ở chế độ chờ (không publish) — server giữ status
|
|
55
|
+
* "scheduled" và KHÔNG khởi động recording egress cho tới khi publish.
|
|
56
|
+
* Engine tự đặt cờ này theo options.autoPublish; app không cần truyền.
|
|
57
|
+
*/
|
|
58
|
+
standby?: boolean;
|
|
59
|
+
/**
|
|
60
|
+
* Độ phân giải app THỰC SỰ quay, để server ghi đúng vào phiên. Chỉ app biết
|
|
61
|
+
* con số này: nó là lựa chọn local và app còn override `config.videoQuality`
|
|
62
|
+
* mà server trả về. Không khai thì server dùng mặc định của nó (1080) và bản
|
|
63
|
+
* ghi phiên nói dối — đã cắn 18/08/2026, dashboard báo 1080 trong khi máy
|
|
64
|
+
* phát 720 suốt.
|
|
65
|
+
*/
|
|
66
|
+
quality?: 720 | 1080;
|
|
67
|
+
}
|
|
68
|
+
interface SdkTelemetryCredentials {
|
|
69
|
+
endpoint: string;
|
|
70
|
+
token: string;
|
|
71
|
+
expiresAt: number;
|
|
72
|
+
}
|
|
73
|
+
interface SdkSessionGrant {
|
|
74
|
+
sessionId: string;
|
|
75
|
+
serverUrl: string;
|
|
76
|
+
participantToken: string;
|
|
77
|
+
playbackUrl: string | null;
|
|
78
|
+
tokenExpiresAt: number;
|
|
79
|
+
telemetry: SdkTelemetryCredentials;
|
|
80
|
+
config: SdkRemoteConfig;
|
|
81
|
+
}
|
|
82
|
+
interface SdkSessionRefresh {
|
|
83
|
+
participantToken?: string;
|
|
84
|
+
tokenExpiresAt?: number;
|
|
85
|
+
telemetry?: SdkTelemetryCredentials;
|
|
86
|
+
config: SdkRemoteConfig;
|
|
87
|
+
}
|
|
88
|
+
type CameraGrantStatus = "scheduled" | "live";
|
|
89
|
+
/**
|
|
90
|
+
* Camera grant — điện thoại của user ĐỐI TÁC lên sóng (ADR-0002). Backend đối
|
|
91
|
+
* tác nhận từ REST v1 (POST /api/v1/live-sessions {cameraGrant:true} hoặc
|
|
92
|
+
* POST /api/v1/live-sessions/{id}/camera-grant) và chuyển NGUYÊN TRẠNG cho app;
|
|
93
|
+
* app dùng `telemetry.token` làm Bearer với namespace /api/v2/camera/sessions.
|
|
94
|
+
*
|
|
95
|
+
* Superset nghiêm ngặt của SdkSessionGrant: `serverUrl` là RTMP ingest
|
|
96
|
+
* (`rtmp://host:1935/`), `participantToken` là stream key — SDK 0.2 đã nhận
|
|
97
|
+
* hình dạng này qua isSessionGrant nên không cần validator mới ở lớp engine.
|
|
98
|
+
*/
|
|
99
|
+
interface CameraGrant extends SdkSessionGrant {
|
|
100
|
+
/** Không bao giờ "ended" — endpoint trả 409 thay vì grant. */
|
|
101
|
+
status: CameraGrantStatus;
|
|
102
|
+
matchRef: string | null;
|
|
103
|
+
courtRef: string | null;
|
|
104
|
+
watchUrl: string | null;
|
|
105
|
+
/**
|
|
106
|
+
* Origin websocket Convex (https://<dep>.convex.cloud) để SDK subscribe lệnh
|
|
107
|
+
* thay vì poll HTTP 2s; null khi deployment không lộ origin chuẩn → SDK poll.
|
|
108
|
+
*/
|
|
109
|
+
convexUrl: string | null;
|
|
110
|
+
issuedAt: number;
|
|
111
|
+
/**
|
|
112
|
+
* Hạn dùng của grant. Phiên chờ mà app CHƯA từng kết nối bị dọn sau 30 phút
|
|
113
|
+
* (RTMP_STANDBY_SILENT_MS) — xin grant đúng lúc user mở camera, đừng cấp sớm
|
|
114
|
+
* hàng giờ trước trận.
|
|
115
|
+
*/
|
|
116
|
+
expiresAt: number;
|
|
117
|
+
}
|
|
118
|
+
interface EndLivestreamInput {
|
|
119
|
+
sessionId: string;
|
|
120
|
+
reason: "user" | "background_timeout" | "network_timeout" | "standby_timeout" | "error";
|
|
121
|
+
}
|
|
122
|
+
type EndReason = EndLivestreamInput["reason"];
|
|
123
|
+
declare const SDK_TELEMETRY_EVENT_NAMES: readonly ["state_changed", "connection_quality", "reconnect", "error", "heartbeat", "session_ended"];
|
|
124
|
+
type SdkTelemetryEventName = (typeof SDK_TELEMETRY_EVENT_NAMES)[number];
|
|
125
|
+
interface SdkTelemetryEvent {
|
|
126
|
+
sessionId: string;
|
|
127
|
+
at: number;
|
|
128
|
+
name: SdkTelemetryEventName;
|
|
129
|
+
data?: Record<string, unknown>;
|
|
130
|
+
}
|
|
131
|
+
/** Lệnh điều khiển phiên giao qua kênh poll (mode device-rtmp). */
|
|
132
|
+
type SessionCommandAction = "start" | "stop" | "end";
|
|
133
|
+
interface SessionCommandRecord {
|
|
134
|
+
id: string;
|
|
135
|
+
action: SessionCommandAction;
|
|
136
|
+
/** Con trỏ tăng nghiêm ngặt theo phiên — client gửi lại làm `after`. */
|
|
137
|
+
issuedAt: number;
|
|
138
|
+
}
|
|
139
|
+
interface SessionCommandPollResult {
|
|
140
|
+
commands: SessionCommandRecord[];
|
|
141
|
+
/**
|
|
142
|
+
* Trạng thái phiên phía server — lưới an toàn khi lệnh rơi: "live" mà máy
|
|
143
|
+
* còn standby thì tự phát, "ended" thì tự kết thúc.
|
|
144
|
+
*/
|
|
145
|
+
status: "scheduled" | "live" | "ended";
|
|
146
|
+
}
|
|
147
|
+
interface LivestreamSessionProvider {
|
|
148
|
+
bootstrap(input: BootstrapInput): Promise<SdkBootstrap>;
|
|
149
|
+
start(input: StartLivestreamInput): Promise<SdkSessionGrant>;
|
|
150
|
+
refresh(sessionId: string): Promise<SdkSessionRefresh>;
|
|
151
|
+
end(input: EndLivestreamInput): Promise<void>;
|
|
152
|
+
/**
|
|
153
|
+
* Báo server bắt đầu phát thật (standby → live): server bật recording
|
|
154
|
+
* egress + chuyển status live TRƯỚC khi engine publish track. Optional để
|
|
155
|
+
* giữ tương thích provider cũ — thiếu thì engine bỏ qua bước server.
|
|
156
|
+
*/
|
|
157
|
+
publish?(sessionId: string): Promise<void>;
|
|
158
|
+
/** Báo server tạm dừng phát (live → standby): dừng egress, status về chờ. */
|
|
159
|
+
unpublish?(sessionId: string): Promise<void>;
|
|
160
|
+
}
|
|
161
|
+
declare function isSdkRemoteConfig(value: unknown): value is SdkRemoteConfig;
|
|
162
|
+
declare function compareSemver(left: string, right: string): -1 | 0 | 1;
|
|
163
|
+
declare function isSdkVersionSupported(version: string, minimum: string): boolean;
|
|
164
|
+
|
|
165
|
+
type ExpoSdkErrorCode = SdkErrorCode | "CONFIGURATION_ERROR" | "UNSUPPORTED_RUNTIME" | "INVALID_RESPONSE" | "TIMEOUT";
|
|
166
|
+
declare class PickleballExpoError extends Error {
|
|
167
|
+
readonly code: ExpoSdkErrorCode;
|
|
168
|
+
constructor(message: string, code: ExpoSdkErrorCode, options?: ErrorOptions);
|
|
169
|
+
}
|
|
170
|
+
declare const PermissionDeniedError_base: {
|
|
171
|
+
new (message?: string, options?: ErrorOptions): {
|
|
172
|
+
readonly code: ExpoSdkErrorCode;
|
|
173
|
+
name: string;
|
|
174
|
+
message: string;
|
|
175
|
+
stack?: string;
|
|
176
|
+
cause?: unknown;
|
|
177
|
+
};
|
|
178
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
179
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
180
|
+
stackTraceLimit: number;
|
|
181
|
+
};
|
|
182
|
+
declare class PermissionDeniedError extends PermissionDeniedError_base {
|
|
183
|
+
}
|
|
184
|
+
declare const NetworkUnavailableError_base: {
|
|
185
|
+
new (message?: string, options?: ErrorOptions): {
|
|
186
|
+
readonly code: ExpoSdkErrorCode;
|
|
187
|
+
name: string;
|
|
188
|
+
message: string;
|
|
189
|
+
stack?: string;
|
|
190
|
+
cause?: unknown;
|
|
191
|
+
};
|
|
192
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
193
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
194
|
+
stackTraceLimit: number;
|
|
195
|
+
};
|
|
196
|
+
declare class NetworkUnavailableError extends NetworkUnavailableError_base {
|
|
197
|
+
}
|
|
198
|
+
declare const TokenExpiredError_base: {
|
|
199
|
+
new (message?: string, options?: ErrorOptions): {
|
|
200
|
+
readonly code: ExpoSdkErrorCode;
|
|
201
|
+
name: string;
|
|
202
|
+
message: string;
|
|
203
|
+
stack?: string;
|
|
204
|
+
cause?: unknown;
|
|
205
|
+
};
|
|
206
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
207
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
208
|
+
stackTraceLimit: number;
|
|
209
|
+
};
|
|
210
|
+
declare class TokenExpiredError extends TokenExpiredError_base {
|
|
211
|
+
}
|
|
212
|
+
declare const UnsupportedSdkError_base: {
|
|
213
|
+
new (message?: string, options?: ErrorOptions): {
|
|
214
|
+
readonly code: ExpoSdkErrorCode;
|
|
215
|
+
name: string;
|
|
216
|
+
message: string;
|
|
217
|
+
stack?: string;
|
|
218
|
+
cause?: unknown;
|
|
219
|
+
};
|
|
220
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
221
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
222
|
+
stackTraceLimit: number;
|
|
223
|
+
};
|
|
224
|
+
declare class UnsupportedSdkError extends UnsupportedSdkError_base {
|
|
225
|
+
}
|
|
226
|
+
declare const UnsupportedRuntimeError_base: {
|
|
227
|
+
new (message?: string, options?: ErrorOptions): {
|
|
228
|
+
readonly code: ExpoSdkErrorCode;
|
|
229
|
+
name: string;
|
|
230
|
+
message: string;
|
|
231
|
+
stack?: string;
|
|
232
|
+
cause?: unknown;
|
|
233
|
+
};
|
|
234
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
235
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
236
|
+
stackTraceLimit: number;
|
|
237
|
+
};
|
|
238
|
+
declare class UnsupportedRuntimeError extends UnsupportedRuntimeError_base {
|
|
239
|
+
}
|
|
240
|
+
declare const SessionConflictError_base: {
|
|
241
|
+
new (message?: string, options?: ErrorOptions): {
|
|
242
|
+
readonly code: ExpoSdkErrorCode;
|
|
243
|
+
name: string;
|
|
244
|
+
message: string;
|
|
245
|
+
stack?: string;
|
|
246
|
+
cause?: unknown;
|
|
247
|
+
};
|
|
248
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
249
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
250
|
+
stackTraceLimit: number;
|
|
251
|
+
};
|
|
252
|
+
declare class SessionConflictError extends SessionConflictError_base {
|
|
253
|
+
}
|
|
254
|
+
declare const RecordingFailureError_base: {
|
|
255
|
+
new (message?: string, options?: ErrorOptions): {
|
|
256
|
+
readonly code: ExpoSdkErrorCode;
|
|
257
|
+
name: string;
|
|
258
|
+
message: string;
|
|
259
|
+
stack?: string;
|
|
260
|
+
cause?: unknown;
|
|
261
|
+
};
|
|
262
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
263
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
264
|
+
stackTraceLimit: number;
|
|
265
|
+
};
|
|
266
|
+
declare class RecordingFailureError extends RecordingFailureError_base {
|
|
267
|
+
}
|
|
268
|
+
declare const ConsentRequiredError_base: {
|
|
269
|
+
new (message?: string, options?: ErrorOptions): {
|
|
270
|
+
readonly code: ExpoSdkErrorCode;
|
|
271
|
+
name: string;
|
|
272
|
+
message: string;
|
|
273
|
+
stack?: string;
|
|
274
|
+
cause?: unknown;
|
|
275
|
+
};
|
|
276
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
277
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
278
|
+
stackTraceLimit: number;
|
|
279
|
+
};
|
|
280
|
+
declare class ConsentRequiredError extends ConsentRequiredError_base {
|
|
281
|
+
}
|
|
282
|
+
declare const SdkDisabledError_base: {
|
|
283
|
+
new (message?: string, options?: ErrorOptions): {
|
|
284
|
+
readonly code: ExpoSdkErrorCode;
|
|
285
|
+
name: string;
|
|
286
|
+
message: string;
|
|
287
|
+
stack?: string;
|
|
288
|
+
cause?: unknown;
|
|
289
|
+
};
|
|
290
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
291
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
292
|
+
stackTraceLimit: number;
|
|
293
|
+
};
|
|
294
|
+
declare class SdkDisabledError extends SdkDisabledError_base {
|
|
295
|
+
}
|
|
296
|
+
declare const ExpoSdkConfigurationError_base: {
|
|
297
|
+
new (message?: string, options?: ErrorOptions): {
|
|
298
|
+
readonly code: ExpoSdkErrorCode;
|
|
299
|
+
name: string;
|
|
300
|
+
message: string;
|
|
301
|
+
stack?: string;
|
|
302
|
+
cause?: unknown;
|
|
303
|
+
};
|
|
304
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
305
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
306
|
+
stackTraceLimit: number;
|
|
307
|
+
};
|
|
308
|
+
declare class ExpoSdkConfigurationError extends ExpoSdkConfigurationError_base {
|
|
309
|
+
}
|
|
310
|
+
declare const InvalidResponseError_base: {
|
|
311
|
+
new (message?: string, options?: ErrorOptions): {
|
|
312
|
+
readonly code: ExpoSdkErrorCode;
|
|
313
|
+
name: string;
|
|
314
|
+
message: string;
|
|
315
|
+
stack?: string;
|
|
316
|
+
cause?: unknown;
|
|
317
|
+
};
|
|
318
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
319
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
320
|
+
stackTraceLimit: number;
|
|
321
|
+
};
|
|
322
|
+
declare class InvalidResponseError extends InvalidResponseError_base {
|
|
323
|
+
}
|
|
324
|
+
declare const ExpoSdkTimeoutError_base: {
|
|
325
|
+
new (message?: string, options?: ErrorOptions): {
|
|
326
|
+
readonly code: ExpoSdkErrorCode;
|
|
327
|
+
name: string;
|
|
328
|
+
message: string;
|
|
329
|
+
stack?: string;
|
|
330
|
+
cause?: unknown;
|
|
331
|
+
};
|
|
332
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
333
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
334
|
+
stackTraceLimit: number;
|
|
335
|
+
};
|
|
336
|
+
declare class ExpoSdkTimeoutError extends ExpoSdkTimeoutError_base {
|
|
337
|
+
}
|
|
338
|
+
declare const UnknownSdkError_base: {
|
|
339
|
+
new (message?: string, options?: ErrorOptions): {
|
|
340
|
+
readonly code: ExpoSdkErrorCode;
|
|
341
|
+
name: string;
|
|
342
|
+
message: string;
|
|
343
|
+
stack?: string;
|
|
344
|
+
cause?: unknown;
|
|
345
|
+
};
|
|
346
|
+
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
347
|
+
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
348
|
+
stackTraceLimit: number;
|
|
349
|
+
};
|
|
350
|
+
declare class UnknownSdkError extends UnknownSdkError_base {
|
|
351
|
+
}
|
|
352
|
+
declare function errorFromCode(code: string, message?: string): PickleballExpoError;
|
|
353
|
+
declare function normalizeError(error: unknown): PickleballExpoError;
|
|
354
|
+
/**
|
|
355
|
+
* Phiên đã biến mất hẳn phía server (HTTP 404): bị watchdog dọn, thuộc
|
|
356
|
+
* deployment khác, hoặc ID không hợp lệ. Khác hẳn lỗi mạng/5xx — thử lại không
|
|
357
|
+
* bao giờ cứu được, nên caller phải bỏ dấu khôi phục thay vì chặn vĩnh viễn.
|
|
358
|
+
*
|
|
359
|
+
* Mã HTTP được gắn vào lỗi ở http-session-provider vì envelope trả về chỉ có
|
|
360
|
+
* code "UNKNOWN", không phân biệt được 404 với 5xx.
|
|
361
|
+
*/
|
|
362
|
+
declare function isSessionGoneError(error: unknown): boolean;
|
|
363
|
+
|
|
364
|
+
interface RoomConnection {
|
|
365
|
+
serverUrl: string;
|
|
366
|
+
participantToken: string;
|
|
367
|
+
microphoneEnabled: boolean;
|
|
368
|
+
/** false: vào phòng nhưng chưa publish track (trạng thái standby). */
|
|
369
|
+
publish?: boolean;
|
|
370
|
+
}
|
|
371
|
+
type RoomEvent = {
|
|
372
|
+
type: "reconnecting";
|
|
373
|
+
} | {
|
|
374
|
+
type: "reconnected";
|
|
375
|
+
} | {
|
|
376
|
+
type: "disconnected";
|
|
377
|
+
reason?: string;
|
|
378
|
+
} | {
|
|
379
|
+
type: "media-error";
|
|
380
|
+
error: unknown;
|
|
381
|
+
}
|
|
382
|
+
/** Lệnh điều khiển từ dashboard qua LiveKit data channel ({"type":"cmd"}). */
|
|
383
|
+
| {
|
|
384
|
+
type: "command";
|
|
385
|
+
action: string;
|
|
386
|
+
};
|
|
387
|
+
interface RoomAdapter {
|
|
388
|
+
/**
|
|
389
|
+
* Xin quyền camera/mic theo cách của mode. Không có thì engine dùng
|
|
390
|
+
* runtime.requestPermissions() (getUserMedia của WebRTC — mở camera thật
|
|
391
|
+
* ~400ms). Mode device-rtmp PHẢI tự xin bằng API quyền thuần: máy Android
|
|
392
|
+
* MediaTek chỉ cho một camera mở, camera WebRTC chưa nhả là HaishinKit dính
|
|
393
|
+
* "Too many users (-87)" và preview đen.
|
|
394
|
+
*/
|
|
395
|
+
requestPermissions?(): Promise<boolean>;
|
|
396
|
+
/**
|
|
397
|
+
* true = adapter KHÔNG tự nối lại khi rớt (RTMP: native chỉ báo
|
|
398
|
+
* "disconnected" rồi đứng im) — engine phải chủ động quay số lại qua retry().
|
|
399
|
+
* LiveKit bỏ trống: SDK của nó tự hồi và tự bắn "reconnected".
|
|
400
|
+
*/
|
|
401
|
+
readonly needsManualReconnect?: boolean;
|
|
402
|
+
/** Bật/tắt publish track khi đã ở trong phòng (standby ↔ live). */
|
|
403
|
+
setPublishing?(enabled: boolean): Promise<void>;
|
|
404
|
+
setCaptureOrientation?(orientation: CaptureOrientation): Promise<void>;
|
|
405
|
+
listCameras?(): Promise<CameraDeviceInfo[]>;
|
|
406
|
+
selectCamera?(deviceId: string, facing: CameraDeviceInfo["facing"]): Promise<void>;
|
|
407
|
+
preparePreview(quality: 720 | 1080): Promise<unknown>;
|
|
408
|
+
connect(input: RoomConnection): Promise<void>;
|
|
409
|
+
disconnect(stopTracks?: boolean): Promise<void>;
|
|
410
|
+
switchCamera(): Promise<void>;
|
|
411
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
412
|
+
subscribe(listener: (event: RoomEvent) => void): () => void;
|
|
413
|
+
dispose(): Promise<void>;
|
|
414
|
+
}
|
|
415
|
+
interface SdkRuntime {
|
|
416
|
+
assertSupported(): void;
|
|
417
|
+
requestPermissions(): Promise<boolean>;
|
|
418
|
+
activateKeepAwake(): void | Promise<void>;
|
|
419
|
+
deactivateKeepAwake(): void | Promise<void>;
|
|
420
|
+
}
|
|
421
|
+
interface SdkStorage {
|
|
422
|
+
getItem(key: string): Promise<string | null>;
|
|
423
|
+
setItem(key: string, value: string): Promise<void>;
|
|
424
|
+
removeItem(key: string): Promise<void>;
|
|
425
|
+
}
|
|
426
|
+
interface SdkNetwork {
|
|
427
|
+
isOnline(): boolean;
|
|
428
|
+
subscribe(listener: (online: boolean) => void): () => void;
|
|
429
|
+
}
|
|
430
|
+
/**
|
|
431
|
+
* Khi app khởi động lại và thấy dấu của một phiên chưa được dừng có chủ ý, hàm
|
|
432
|
+
* này quyết định: GIỮ phiên đó để nhận lại, hay kết thúc nó như trước.
|
|
433
|
+
*
|
|
434
|
+
* Mặc định (không truyền) = kết thúc, giữ nguyên hành vi cho mọi SDK đối tác.
|
|
435
|
+
* App first-party trả true khi máy đang gán sân: lúc đó máy sụp nguồn giữa trận
|
|
436
|
+
* là chuyện của thiết bị gác sân, và nối lại đúng phiên cũ mới giữ được link xem
|
|
437
|
+
* và VOD liền mạch. Máy không gán sân thì vẫn dọn sạch như cũ.
|
|
438
|
+
*/
|
|
439
|
+
/**
|
|
440
|
+
* Được phép async có chủ đích: câu trả lời đúng nằm TRÊN ĐĨA (sân đã gán có
|
|
441
|
+
* persist), còn React state lúc engine khởi động thường CHƯA restore xong.
|
|
442
|
+
* Bản sync-đọc-state đã gây lỗi thật (18/08/2026): recover chạy trước restore
|
|
443
|
+
* → keep=false oan → app tự kết án phiên đáng giữ rồi arm phiên chờ THỨ HAI.
|
|
444
|
+
*/
|
|
445
|
+
type KeepSessionOnRestart = () => boolean | Promise<boolean>;
|
|
446
|
+
interface TelemetrySink {
|
|
447
|
+
send(credentials: SdkTelemetryCredentials, events: readonly SdkTelemetryEvent[]): Promise<void>;
|
|
448
|
+
dispose?(): void;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Kênh nhận lệnh điều khiển cho mode device-rtmp: RTMP không có data channel
|
|
452
|
+
* nên start/stop/end từ server (match-start của trọng tài, dashboard) về qua
|
|
453
|
+
* HTTP poll, auth bằng chính telemetry token trong grant.
|
|
454
|
+
*/
|
|
455
|
+
interface SessionCommandChannel {
|
|
456
|
+
poll(credentials: SdkTelemetryCredentials, after: number): Promise<SessionCommandPollResult>;
|
|
457
|
+
dispose?(): void;
|
|
458
|
+
}
|
|
459
|
+
/**
|
|
460
|
+
* Kênh nhận lệnh device-rtmp qua WEBSOCKET (Convex subscription) — thay poll
|
|
461
|
+
* HTTP 2s cho máy first-party nối thẳng Convex. Lệnh về tức thì, lúc rảnh gần
|
|
462
|
+
* như 0 function call (poll 2s ở 200 máy là ~518M call/tháng). Có kênh này thì
|
|
463
|
+
* engine hạ poll về nhịp reconcile chậm làm lưới an toàn; thiếu thì poll như cũ.
|
|
464
|
+
*/
|
|
465
|
+
interface SessionCommandSubscription {
|
|
466
|
+
/**
|
|
467
|
+
* Mở subscription cho phiên; `onCommands` được gọi mỗi lần hàng lệnh đổi
|
|
468
|
+
* (kể cả lần đầu). `onError` báo websocket/token hỏng để engine tăng lại nhịp
|
|
469
|
+
* poll. Trả hàm huỷ subscription (KHÔNG đóng client — tái dùng qua phiên).
|
|
470
|
+
*/
|
|
471
|
+
subscribe(credentials: SdkTelemetryCredentials, sessionId: string, onCommands: (commands: SessionCommandRecord[]) => void, onError?: (error: Error) => void): () => void;
|
|
472
|
+
dispose?(): void;
|
|
473
|
+
}
|
|
474
|
+
interface EngineDependencies {
|
|
475
|
+
sessionProvider: LivestreamSessionProvider;
|
|
476
|
+
bootstrapInput: BootstrapInput;
|
|
477
|
+
roomFactory: () => RoomAdapter;
|
|
478
|
+
keepSessionOnRestart?: KeepSessionOnRestart;
|
|
479
|
+
runtime: SdkRuntime;
|
|
480
|
+
storage: SdkStorage;
|
|
481
|
+
network: SdkNetwork;
|
|
482
|
+
telemetry?: TelemetrySink;
|
|
483
|
+
/** Kênh poll lệnh cho phiên device-rtmp; thiếu thì mode này điếc lệnh server. */
|
|
484
|
+
commandChannel?: SessionCommandChannel;
|
|
485
|
+
/**
|
|
486
|
+
* Kênh nhận lệnh qua websocket (first-party). Có thì lệnh về tức thì và poll
|
|
487
|
+
* tụt về nhịp reconcile; thiếu (máy đối tác qua proxy) thì chỉ dùng poll.
|
|
488
|
+
*/
|
|
489
|
+
commandSubscription?: SessionCommandSubscription;
|
|
490
|
+
/** Thu chỉ số thiết bị; gọi mỗi nhịp heartbeat khi đang phát. */
|
|
491
|
+
deviceMetrics?: () => Promise<DeviceMetricsSample | null>;
|
|
492
|
+
now?: () => number;
|
|
493
|
+
}
|
|
494
|
+
type CaptureOrientation = "portrait" | "landscape";
|
|
495
|
+
interface CameraDeviceInfo {
|
|
496
|
+
deviceId: string;
|
|
497
|
+
label: string;
|
|
498
|
+
facing: "front" | "environment" | "unknown";
|
|
499
|
+
}
|
|
500
|
+
/** Chỉ số thiết bị gửi kèm heartbeat (trường trùng schema deviceMetrics). */
|
|
501
|
+
interface DeviceMetricsSample {
|
|
502
|
+
batteryPct?: number;
|
|
503
|
+
batteryTempC?: number;
|
|
504
|
+
thermal?: number;
|
|
505
|
+
ramFreeMb?: number;
|
|
506
|
+
ramTotalMb?: number;
|
|
507
|
+
storageFreeGb?: number;
|
|
508
|
+
netType?: string;
|
|
509
|
+
linkMbps?: number;
|
|
510
|
+
/** Cường độ sóng WiFi 0-100% — CHỈ Android (iOS không mở cho app). */
|
|
511
|
+
signalPct?: number;
|
|
512
|
+
/** IP nội bộ — phân biệt máy đang bám router nào, không cần quyền vị trí. */
|
|
513
|
+
localIp?: string;
|
|
514
|
+
/**
|
|
515
|
+
* Tần số kênh WiFi (MHz) — CHỈ Android. 2412–2484 = băng 2.4 GHz, ≥ 5000 =
|
|
516
|
+
* 5 GHz. Thay cho SSID (bị khoá sau quyền vị trí): 22/08/2026 một máy rơi từ
|
|
517
|
+
* AP 5 GHz của giải sang WiFi quán 2.4 GHz mà heartbeat không phân biệt được.
|
|
518
|
+
*/
|
|
519
|
+
freqMhz?: number;
|
|
520
|
+
rssi?: number;
|
|
521
|
+
connQuality?: string;
|
|
522
|
+
charging?: boolean;
|
|
523
|
+
}
|
|
524
|
+
interface LivestreamSnapshot {
|
|
525
|
+
state: LivestreamState;
|
|
526
|
+
error: PickleballExpoError | null;
|
|
527
|
+
config: SdkRemoteConfig | null;
|
|
528
|
+
sessionId: string | null;
|
|
529
|
+
previewTrack: unknown;
|
|
530
|
+
isMicrophoneEnabled: boolean;
|
|
531
|
+
isCameraFront: boolean;
|
|
532
|
+
/** Explicit pre-live choice; null follows the window orientation. */
|
|
533
|
+
captureOrientation: CaptureOrientation | null;
|
|
534
|
+
/** Mẫu chỉ số thiết bị mới nhất (cập nhật mỗi heartbeat khi live). */
|
|
535
|
+
deviceMetrics: DeviceMetricsSample | null;
|
|
536
|
+
/** Cameras reported by the platform after preview was prepared. */
|
|
537
|
+
cameras: CameraDeviceInfo[];
|
|
538
|
+
/** Explicitly chosen camera; null when following facing (front/back). */
|
|
539
|
+
selectedCameraId: string | null;
|
|
540
|
+
elapsedMs: number;
|
|
541
|
+
isOnline: boolean;
|
|
542
|
+
/**
|
|
543
|
+
* Phiên cũ còn sống mà app vừa khởi động lại có thể NHẬN LẠI (thay vì tạo phiên
|
|
544
|
+
* mới). Chỉ khác null khi engine tìm thấy dấu của một phiên chưa được dừng có
|
|
545
|
+
* chủ ý VÀ caller nói phiên đó đáng giữ (keepSessionOnRestart). Tầng trên
|
|
546
|
+
* (AutoResume) đọc trường này để nối lại đúng phiên cũ — giữ nguyên link xem và
|
|
547
|
+
* VOD liền một mạch.
|
|
548
|
+
*/
|
|
549
|
+
adoptableSessionId: string | null;
|
|
550
|
+
}
|
|
551
|
+
declare const PENDING_END_KEY = "@pickleball/expo-sdk/pending-end";
|
|
552
|
+
declare const LEGAL_LIVESTREAM_TRANSITIONS: Readonly<Record<LivestreamState, readonly LivestreamState[]>>;
|
|
553
|
+
declare class LivestreamEngine {
|
|
554
|
+
private readonly dependencies;
|
|
555
|
+
private room?;
|
|
556
|
+
private readonly now;
|
|
557
|
+
private snapshot;
|
|
558
|
+
private listeners;
|
|
559
|
+
private preparePromise?;
|
|
560
|
+
private startPromise?;
|
|
561
|
+
private stopPromise?;
|
|
562
|
+
private retryPromise?;
|
|
563
|
+
private credentialRefreshPromise?;
|
|
564
|
+
private pendingReplayPromise?;
|
|
565
|
+
private startupRecoveryPromise?;
|
|
566
|
+
private startupRecovered;
|
|
567
|
+
private pendingReplayError?;
|
|
568
|
+
private ownedSessionId?;
|
|
569
|
+
private commandTail;
|
|
570
|
+
private commandCount;
|
|
571
|
+
private roomUnsubscribe;
|
|
572
|
+
private networkUnsubscribe;
|
|
573
|
+
private networkSubscribed;
|
|
574
|
+
private latestAppState;
|
|
575
|
+
private backgroundTimer?;
|
|
576
|
+
private standbyTimer?;
|
|
577
|
+
private reconnectTimer?;
|
|
578
|
+
private redialTimer?;
|
|
579
|
+
/** Đang trong vòng quay số lại — xem doRetry() để biết vì sao cần cờ này. */
|
|
580
|
+
private redialing;
|
|
581
|
+
private heartbeatTimer?;
|
|
582
|
+
private elapsedTimer?;
|
|
583
|
+
private tokenRefreshTimer?;
|
|
584
|
+
private maxSessionTimer?;
|
|
585
|
+
private commandPollTimer?;
|
|
586
|
+
/** Huỷ subscription websocket lệnh của phiên hiện tại (nếu đang mở). */
|
|
587
|
+
private commandUnsubscribe?;
|
|
588
|
+
/** Khoá phiên+token đang subscribe — đổi thì resubscribe (token refresh/đổi phiên). */
|
|
589
|
+
private commandSubscriptionKey?;
|
|
590
|
+
/** Websocket đã đẩy được ít nhất một lần → coi là sống, cho phép hạ nhịp poll. */
|
|
591
|
+
private commandSubscriptionActive;
|
|
592
|
+
/** issuedAt của lệnh cuối đã xử lý — server chỉ trả lệnh mới hơn mốc này. */
|
|
593
|
+
private commandCursor;
|
|
594
|
+
private commandPollBackoffMs;
|
|
595
|
+
private grant?;
|
|
596
|
+
private startedAt?;
|
|
597
|
+
/** Đang publish track (live) hay chỉ ngồi trong phòng (standby). */
|
|
598
|
+
private publishing;
|
|
599
|
+
/** Tổng thời gian đã phát trước lần tạm dừng gần nhất. */
|
|
600
|
+
private pausedElapsedMs;
|
|
601
|
+
private sessionDeadline?;
|
|
602
|
+
private refreshRetryAttempt;
|
|
603
|
+
private operationGeneration;
|
|
604
|
+
private liveTimersStarted;
|
|
605
|
+
private keepAwakeActive;
|
|
606
|
+
private telemetryInFlight?;
|
|
607
|
+
private pendingCriticalTelemetry?;
|
|
608
|
+
private readonly sessionTerminations;
|
|
609
|
+
private disposePromise?;
|
|
610
|
+
private disposed;
|
|
611
|
+
constructor(dependencies: EngineDependencies);
|
|
612
|
+
getSnapshot: () => LivestreamSnapshot;
|
|
613
|
+
subscribe: (listener: () => void) => () => boolean;
|
|
614
|
+
initialize(): Promise<void>;
|
|
615
|
+
prepare(): Promise<void>;
|
|
616
|
+
private doPrepare;
|
|
617
|
+
start(input: StartLivestreamInput, options?: {
|
|
618
|
+
autoPublish?: boolean;
|
|
619
|
+
}): Promise<void>;
|
|
620
|
+
private doStart;
|
|
621
|
+
stop(reason?: EndReason): Promise<void>;
|
|
622
|
+
private doStop;
|
|
623
|
+
retry(): Promise<void>;
|
|
624
|
+
private doRetry;
|
|
625
|
+
switchCamera(): Promise<void>;
|
|
626
|
+
/** Chọn một ống kính cụ thể từ snapshot.cameras. */
|
|
627
|
+
selectCamera(deviceId: string): Promise<void>;
|
|
628
|
+
/** Chọn kiểu quay trước khi live; không đổi khi phiên đang hoạt động. */
|
|
629
|
+
setCaptureOrientation(orientation: CaptureOrientation): Promise<void>;
|
|
630
|
+
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
631
|
+
handleRoomEvent(event: RoomEvent): void;
|
|
632
|
+
handleAppState(state: "active" | "background" | "inactive"): void;
|
|
633
|
+
private ensureBackgroundGraceTimer;
|
|
634
|
+
/**
|
|
635
|
+
* `keepSession` = engine bị tháo nhưng phiên phải sống tiếp để lần mở app sau
|
|
636
|
+
* nhận lại. Mặc định false: giữ NGUYÊN hành vi cũ cho mọi SDK đối tác.
|
|
637
|
+
*
|
|
638
|
+
* Lưu ý dispose KHÔNG BAO GIỜ là đường của "người dùng bấm dừng" — nút dừng đi
|
|
639
|
+
* qua doStop, mà doStop xoá ownedSessionId trước, nên tới đây sessionId đã là
|
|
640
|
+
* undefined. Vì vậy nhánh end ở đây luôn là teardown NGOÀI Ý MUỐN (app bị giết,
|
|
641
|
+
* component remount), đúng chỗ cần giữ phiên.
|
|
642
|
+
*/
|
|
643
|
+
dispose(options?: {
|
|
644
|
+
keepSession?: boolean;
|
|
645
|
+
}): Promise<void>;
|
|
646
|
+
private replayPendingEnd;
|
|
647
|
+
private doReplayPendingEnd;
|
|
648
|
+
/**
|
|
649
|
+
* Phiên tưởng đáng nhận lại hoá ra đã chết phía server → BỎ dấu để máy arm
|
|
650
|
+
* phiên mới được.
|
|
651
|
+
*
|
|
652
|
+
* Thiếu hàm này là máy kẹt cứng: dấu vẫn nằm đó chặn mọi lượt start (đúng vai
|
|
653
|
+
* trò chống phiên song song của nó), mà lượt start duy nhất được cho qua lại là
|
|
654
|
+
* lượt nhận lại chính cái phiên đã chết — không bao giờ thành công. Đã dính
|
|
655
|
+
* thật 18/08/2026: lưới standby 30 phút kết thúc phiên trong lúc máy ngủ, mở
|
|
656
|
+
* lại thì app đứng ở "error" vĩnh viễn.
|
|
657
|
+
*/
|
|
658
|
+
abandonAdoptableSession(sessionId: string): Promise<void>;
|
|
659
|
+
private recoverStartupSession;
|
|
660
|
+
/**
|
|
661
|
+
* Dấu phiên cũ chặn thao tác mới cho tới khi nó được giải quyết.
|
|
662
|
+
*
|
|
663
|
+
* `externalSessionId` = id lượt start đang xin đi qua; bỏ trống nghĩa là
|
|
664
|
+
* prepare (mở camera), vốn phải luôn được phép khi đang định nhận lại — không
|
|
665
|
+
* mở được camera thì không nhận lại được, và đó chính là bẫy làm máy kẹt cứng ở
|
|
666
|
+
* màn lỗi "The previous livestream is still pending end".
|
|
667
|
+
*/
|
|
668
|
+
private ensurePendingEndResolved;
|
|
669
|
+
private readPendingEnd;
|
|
670
|
+
private ownsInMemorySession;
|
|
671
|
+
private startTimers;
|
|
672
|
+
private clearTimers;
|
|
673
|
+
/**
|
|
674
|
+
* Quay số lại với backoff cho adapter cần reconnect thủ công. Mỗi nhịp gọi
|
|
675
|
+
* retry() (refresh credentials + room.connect + enterSession). Thành công →
|
|
676
|
+
* enterSession phát "publishing" → nhánh "reconnected" dọn cả hai timer.
|
|
677
|
+
* Thất bại → doRetry() đưa state về "error" (vẫn còn sessionId) và nhịp sau
|
|
678
|
+
* retry() lại kéo về "reconnecting" — lặp tới khi doom timer 30s kết liễu.
|
|
679
|
+
* Backoff thưa dần để không dội server khi ingest thật sự sập.
|
|
680
|
+
*/
|
|
681
|
+
private scheduleRedial;
|
|
682
|
+
private clearRedialTimer;
|
|
683
|
+
private stopRedialing;
|
|
684
|
+
private clearReconnectTimer;
|
|
685
|
+
/**
|
|
686
|
+
* Poll lệnh chỉ khi phiên là device-rtmp (grant rtmp://) và đang standby/
|
|
687
|
+
* live. Ngoài hai trạng thái đó không có gì để điều khiển: connecting chưa
|
|
688
|
+
* có phiên chạy, reconnecting đã có reconnect timer lo, ending/ended thì thôi.
|
|
689
|
+
*/
|
|
690
|
+
private commandPollingWanted;
|
|
691
|
+
private syncCommandPolling;
|
|
692
|
+
/**
|
|
693
|
+
* Mở/đổi websocket nhận lệnh cho phiên hiện tại. Chỉ máy first-party có
|
|
694
|
+
* dependency này (đối tác qua proxy thì undefined → chỉ poll). Resubscribe
|
|
695
|
+
* khi đổi phiên HOẶC token (refresh) vì cả hai đi vào khoá.
|
|
696
|
+
*/
|
|
697
|
+
private syncCommandSubscription;
|
|
698
|
+
private stopCommandSubscription;
|
|
699
|
+
private stopCommandPolling;
|
|
700
|
+
private scheduleCommandPoll;
|
|
701
|
+
private commandPollInterval;
|
|
702
|
+
/**
|
|
703
|
+
* Áp lệnh từ poll HOẶC subscription vào cùng một state machine. Subscription
|
|
704
|
+
* trả cả lệnh CŨ (after:0) nên phải lọc theo con trỏ ở đây; poll đã lọc
|
|
705
|
+
* server-side (after=cursor) nên bước lọc này vô hại với nó.
|
|
706
|
+
*/
|
|
707
|
+
private applyCommands;
|
|
708
|
+
private pollSessionCommands;
|
|
709
|
+
/** Heartbeat kèm chỉ số thiết bị (nếu collector được cung cấp). */
|
|
710
|
+
private sendHeartbeat;
|
|
711
|
+
private sendTelemetry;
|
|
712
|
+
private startTelemetrySend;
|
|
713
|
+
private refreshCredentials;
|
|
714
|
+
private transition;
|
|
715
|
+
private startStandbyTimer;
|
|
716
|
+
private clearStandbyTimer;
|
|
717
|
+
private fail;
|
|
718
|
+
private recordError;
|
|
719
|
+
private update;
|
|
720
|
+
private ensureState;
|
|
721
|
+
private ensureNotDisposed;
|
|
722
|
+
private ensureRoom;
|
|
723
|
+
private ensureNetworkSubscription;
|
|
724
|
+
private requireRoom;
|
|
725
|
+
private releaseRoom;
|
|
726
|
+
private enqueueLifecycle;
|
|
727
|
+
private scheduleTokenRefresh;
|
|
728
|
+
private scheduleMaxSessionDuration;
|
|
729
|
+
private restartHeartbeat;
|
|
730
|
+
private enterSession;
|
|
731
|
+
/** Bắt đầu phát khi đang standby (từ nút trên app hoặc lệnh dashboard). */
|
|
732
|
+
startPublishing(): Promise<void>;
|
|
733
|
+
/** Tạm dừng phát: ngắt kết nối LiveKit hoàn toàn về standby, KHÔNG kết thúc phiên. */
|
|
734
|
+
stopPublishing(): Promise<void>;
|
|
735
|
+
private ensureSessionTermination;
|
|
736
|
+
private abandonGrant;
|
|
737
|
+
private isOperationCurrent;
|
|
738
|
+
private ensureOperation;
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
export { normalizeError as $, type SdkTelemetryEvent as A, type BootstrapInput as B, type CameraGrant as C, type DeviceMetricsSample as D, type EndLivestreamInput as E, type SdkTelemetryEventName as F, type SessionCommandAction as G, type SessionCommandChannel as H, InvalidResponseError as I, type SessionCommandPollResult as J, type SessionCommandRecord as K, type LivestreamSessionProvider as L, SessionConflictError as M, NetworkUnavailableError as N, TokenExpiredError as O, PermissionDeniedError as P, UnsupportedRuntimeError as Q, type RoomAdapter as R, type SessionCommandSubscription as S, type TelemetrySink as T, UnknownSdkError as U, UnsupportedSdkError as V, compareSemver as W, errorFromCode as X, isSdkRemoteConfig as Y, isSdkVersionSupported as Z, isSessionGoneError as _, type SdkStorage as a, LivestreamEngine as a0, type RoomConnection as a1, type RoomEvent as a2, type EngineDependencies as a3, LEGAL_LIVESTREAM_TRANSITIONS as a4, PENDING_END_KEY as a5, type SdkSessionGrant as b, type StartLivestreamInput as c, type SdkRemoteConfig as d, type CameraDeviceInfo as e, type CameraGrantStatus as f, type CaptureOrientation as g, ConsentRequiredError as h, type EndReason as i, ExpoSdkConfigurationError as j, type ExpoSdkErrorCode as k, ExpoSdkTimeoutError as l, type LivestreamSnapshot as m, type LivestreamState as n, PickleballExpoError as o, RecordingFailureError as p, SDK_TELEMETRY_EVENT_NAMES as q, type SdkBootstrap as r, type SdkConflictReason as s, SdkDisabledError as t, type SdkError as u, type SdkErrorCode as v, type SdkNetwork as w, type SdkRuntime as x, type SdkSessionRefresh as y, type SdkTelemetryCredentials as z };
|