@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
package/dist/index.d.cts
CHANGED
|
@@ -1,1201 +1,11 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
1
|
+
import { L as LivestreamSessionProvider, b as SdkSessionGrant, C as CameraGrant, c as StartLivestreamInput, a as SdkStorage, d as SdkRemoteConfig } from './engine-BSvSdCSE.cjs';
|
|
2
|
+
export { B as BootstrapInput, e as CameraDeviceInfo, f as CameraGrantStatus, g as CaptureOrientation, h as ConsentRequiredError, D as DeviceMetricsSample, E as EndLivestreamInput, i as EndReason, j as ExpoSdkConfigurationError, k as ExpoSdkErrorCode, l as ExpoSdkTimeoutError, I as InvalidResponseError, m as LivestreamSnapshot, n as LivestreamState, N as NetworkUnavailableError, P as PermissionDeniedError, o as PickleballExpoError, p as RecordingFailureError, R as RoomAdapter, q as SDK_TELEMETRY_EVENT_NAMES, r as SdkBootstrap, s as SdkConflictReason, t as SdkDisabledError, u as SdkError, v as SdkErrorCode, w as SdkNetwork, x as SdkRuntime, y as SdkSessionRefresh, z as SdkTelemetryCredentials, A as SdkTelemetryEvent, F as SdkTelemetryEventName, G as SessionCommandAction, H as SessionCommandChannel, J as SessionCommandPollResult, K as SessionCommandRecord, S as SessionCommandSubscription, M as SessionConflictError, T as TelemetrySink, O as TokenExpiredError, U as UnknownSdkError, Q as UnsupportedRuntimeError, V as UnsupportedSdkError, W as compareSemver, X as errorFromCode, Y as isSdkRemoteConfig, Z as isSdkVersionSupported, _ as isSessionGoneError, $ as normalizeError } from './engine-BSvSdCSE.cjs';
|
|
3
|
+
import { F as Fetch } from './advanced-DeQwg7MK.cjs';
|
|
4
|
+
export { L as LivestreamContextValue, a as LivestreamTheme, P as PickleballLiveProvider, b as PickleballLiveProviderProps, c as createDefaultStorage, d as createRtmpRoomFactory, e as createRtmpRuntime, f as defaultLivestreamTheme, u as useLivestream } from './advanced-DeQwg7MK.cjs';
|
|
5
|
+
export { A as AntiFlickerMode, C as CAMERA_SETTINGS_STORAGE_KEY, a as CameraCapabilities, b as CameraCapabilityRange, c as CameraSettings, d as CameraSettingsStore, e as CameraSettingsStoreOptions, D as DEFAULT_CAMERA_SETTINGS, f as applyAntiFlicker, g as cameraSettingsKey, h as createCameraSettingsStore, i as getCameraCapabilities, p as parseCameraSettings, r as requestNativePermissions, j as rtmpRoomFactoryFor, s as setExposureBias, k as setExposureLocked, l as setFocusLocked, m as setTorch, n as setZoom, u as useCameraSettings } from './camera-controls-By8tHigY.cjs';
|
|
3
6
|
export { PICKLEBALL_EXPO_SDK_VERSION } from './version.cjs';
|
|
7
|
+
import 'react';
|
|
4
8
|
|
|
5
|
-
type LivestreamState = "idle" | "preparing" | "preview" | "connecting"
|
|
6
|
-
/** Đã vào phòng, chưa phát — chờ lệnh phát từ app hoặc dashboard. */
|
|
7
|
-
| "standby" | "live" | "reconnecting" | "ending" | "ended" | "error";
|
|
8
|
-
type SdkErrorCode = "PERMISSION_DENIED" | "NETWORK_UNAVAILABLE" | "TOKEN_EXPIRED" | "SDK_UPGRADE_REQUIRED" | "SESSION_CONFLICT" | "RECORDING_FAILED" | "CONSENT_REQUIRED" | "SDK_DISABLED" | "UNKNOWN";
|
|
9
|
-
interface SdkError {
|
|
10
|
-
code: SdkErrorCode;
|
|
11
|
-
message: string;
|
|
12
|
-
}
|
|
13
|
-
interface BootstrapInput {
|
|
14
|
-
sdkVersion: string;
|
|
15
|
-
platform: "ios" | "android";
|
|
16
|
-
bundleId: string;
|
|
17
|
-
installationId: string;
|
|
18
|
-
}
|
|
19
|
-
interface SdkRemoteConfig {
|
|
20
|
-
enabled: boolean;
|
|
21
|
-
protocolVersion: 1;
|
|
22
|
-
minSdkVersion: string;
|
|
23
|
-
recommendedSdkVersion: string;
|
|
24
|
-
videoQuality: 720 | 1080;
|
|
25
|
-
reconnectTimeoutMs: number;
|
|
26
|
-
backgroundGraceMs: number;
|
|
27
|
-
telemetryIntervalMs: number;
|
|
28
|
-
maxSessionDurationMs: number;
|
|
29
|
-
/** 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). */
|
|
30
|
-
standbyTimeoutMs?: number;
|
|
31
|
-
}
|
|
32
|
-
interface SdkBootstrap {
|
|
33
|
-
config: SdkRemoteConfig;
|
|
34
|
-
serverTime: number;
|
|
35
|
-
rolloutEnabled: boolean;
|
|
36
|
-
message?: string;
|
|
37
|
-
}
|
|
38
|
-
interface StartLivestreamInput {
|
|
39
|
-
externalSessionId: string;
|
|
40
|
-
title: string;
|
|
41
|
-
visibility?: "public" | "private";
|
|
42
|
-
consentVersion: string;
|
|
43
|
-
metadata?: Record<string, string>;
|
|
44
|
-
/**
|
|
45
|
-
* true: vào phòng ở chế độ chờ (không publish) — server giữ status
|
|
46
|
-
* "scheduled" và KHÔNG khởi động recording egress cho tới khi publish.
|
|
47
|
-
* Engine tự đặt cờ này theo options.autoPublish; app không cần truyền.
|
|
48
|
-
*/
|
|
49
|
-
standby?: boolean;
|
|
50
|
-
}
|
|
51
|
-
interface SdkTelemetryCredentials {
|
|
52
|
-
endpoint: string;
|
|
53
|
-
token: string;
|
|
54
|
-
expiresAt: number;
|
|
55
|
-
}
|
|
56
|
-
interface SdkSessionGrant {
|
|
57
|
-
sessionId: string;
|
|
58
|
-
serverUrl: string;
|
|
59
|
-
participantToken: string;
|
|
60
|
-
playbackUrl: string | null;
|
|
61
|
-
tokenExpiresAt: number;
|
|
62
|
-
telemetry: SdkTelemetryCredentials;
|
|
63
|
-
config: SdkRemoteConfig;
|
|
64
|
-
}
|
|
65
|
-
interface SdkSessionRefresh {
|
|
66
|
-
participantToken?: string;
|
|
67
|
-
tokenExpiresAt?: number;
|
|
68
|
-
telemetry?: SdkTelemetryCredentials;
|
|
69
|
-
config: SdkRemoteConfig;
|
|
70
|
-
}
|
|
71
|
-
interface EndLivestreamInput {
|
|
72
|
-
sessionId: string;
|
|
73
|
-
reason: "user" | "background_timeout" | "network_timeout" | "standby_timeout" | "error";
|
|
74
|
-
}
|
|
75
|
-
type EndReason = EndLivestreamInput["reason"];
|
|
76
|
-
declare const SDK_TELEMETRY_EVENT_NAMES: readonly ["state_changed", "connection_quality", "reconnect", "error", "heartbeat", "session_ended"];
|
|
77
|
-
type SdkTelemetryEventName = (typeof SDK_TELEMETRY_EVENT_NAMES)[number];
|
|
78
|
-
interface SdkTelemetryEvent {
|
|
79
|
-
sessionId: string;
|
|
80
|
-
at: number;
|
|
81
|
-
name: SdkTelemetryEventName;
|
|
82
|
-
data?: Record<string, unknown>;
|
|
83
|
-
}
|
|
84
|
-
interface LivestreamSessionProvider {
|
|
85
|
-
bootstrap(input: BootstrapInput): Promise<SdkBootstrap>;
|
|
86
|
-
start(input: StartLivestreamInput): Promise<SdkSessionGrant>;
|
|
87
|
-
refresh(sessionId: string): Promise<SdkSessionRefresh>;
|
|
88
|
-
end(input: EndLivestreamInput): Promise<void>;
|
|
89
|
-
/**
|
|
90
|
-
* Báo server bắt đầu phát thật (standby → live): server bật recording
|
|
91
|
-
* egress + chuyển status live TRƯỚC khi engine publish track. Optional để
|
|
92
|
-
* giữ tương thích provider cũ — thiếu thì engine bỏ qua bước server.
|
|
93
|
-
*/
|
|
94
|
-
publish?(sessionId: string): Promise<void>;
|
|
95
|
-
/** Báo server tạm dừng phát (live → standby): dừng egress, status về chờ. */
|
|
96
|
-
unpublish?(sessionId: string): Promise<void>;
|
|
97
|
-
}
|
|
98
|
-
declare function isSdkRemoteConfig(value: unknown): value is SdkRemoteConfig;
|
|
99
|
-
declare function compareSemver(left: string, right: string): -1 | 0 | 1;
|
|
100
|
-
declare function isSdkVersionSupported(version: string, minimum: string): boolean;
|
|
101
|
-
|
|
102
|
-
type MatchFormat = "singles" | "doubles";
|
|
103
|
-
type MatchScoringMode = "rally" | "sideOut";
|
|
104
|
-
type MatchTeamId = "A" | "B";
|
|
105
|
-
type CourtSide = "left" | "right";
|
|
106
|
-
type MatchInputSource = "local" | "remote" | "voice" | "ble" | "system";
|
|
107
|
-
interface MatchPlayer {
|
|
108
|
-
readonly id: string;
|
|
109
|
-
readonly name: string;
|
|
110
|
-
}
|
|
111
|
-
interface MatchTeam {
|
|
112
|
-
readonly id: MatchTeamId;
|
|
113
|
-
readonly name?: string;
|
|
114
|
-
/** Defaults to the first player in the roster. */
|
|
115
|
-
readonly initialServerId?: string;
|
|
116
|
-
readonly players: readonly MatchPlayer[];
|
|
117
|
-
}
|
|
118
|
-
interface MatchCreateInput {
|
|
119
|
-
readonly id: string;
|
|
120
|
-
readonly format: MatchFormat;
|
|
121
|
-
readonly scoringMode: MatchScoringMode;
|
|
122
|
-
readonly targetScore?: number;
|
|
123
|
-
readonly initialServingTeam?: MatchTeamId;
|
|
124
|
-
readonly teams: Readonly<Record<MatchTeamId, MatchTeam>>;
|
|
125
|
-
}
|
|
126
|
-
interface TeamScore {
|
|
127
|
-
readonly A: number;
|
|
128
|
-
readonly B: number;
|
|
129
|
-
}
|
|
130
|
-
interface ServeState {
|
|
131
|
-
readonly teamId: MatchTeamId;
|
|
132
|
-
readonly playerId: string;
|
|
133
|
-
readonly serverNumber: 1 | 2;
|
|
134
|
-
readonly court: CourtSide;
|
|
135
|
-
}
|
|
136
|
-
interface GameSnapshot {
|
|
137
|
-
readonly number: 1 | 2 | 3;
|
|
138
|
-
readonly score: TeamScore;
|
|
139
|
-
readonly startingServingTeam: MatchTeamId;
|
|
140
|
-
readonly winner: MatchTeamId | null;
|
|
141
|
-
readonly midGameSwitchCompleted: boolean;
|
|
142
|
-
}
|
|
143
|
-
type PendingCourtSwitch = {
|
|
144
|
-
readonly reason: "gameEnd";
|
|
145
|
-
readonly game: 1 | 2;
|
|
146
|
-
} | {
|
|
147
|
-
readonly reason: "gameThreeMidpoint";
|
|
148
|
-
readonly game: 3;
|
|
149
|
-
};
|
|
150
|
-
interface MatchSnapshot {
|
|
151
|
-
readonly id: string;
|
|
152
|
-
readonly format: MatchFormat;
|
|
153
|
-
readonly scoringMode: MatchScoringMode;
|
|
154
|
-
readonly targetScore: number;
|
|
155
|
-
readonly bestOf: 3;
|
|
156
|
-
readonly teams: Readonly<Record<MatchTeamId, MatchTeam>>;
|
|
157
|
-
readonly designatedServerByTeam: Readonly<Record<MatchTeamId, string>>;
|
|
158
|
-
readonly initialServingTeam: MatchTeamId;
|
|
159
|
-
readonly courtSides: Readonly<Record<CourtSide, MatchTeamId>>;
|
|
160
|
-
readonly playerCourts: Readonly<Record<string, CourtSide>>;
|
|
161
|
-
readonly games: readonly GameSnapshot[];
|
|
162
|
-
readonly currentGame: 1 | 2 | 3;
|
|
163
|
-
readonly gamesWon: TeamScore;
|
|
164
|
-
readonly serve: ServeState;
|
|
165
|
-
readonly pendingCourtSwitch: PendingCourtSwitch | null;
|
|
166
|
-
readonly status: "inProgress" | "completed" | "ended";
|
|
167
|
-
readonly winner: MatchTeamId | null;
|
|
168
|
-
readonly endReason: string | null;
|
|
169
|
-
readonly sequence: number;
|
|
170
|
-
}
|
|
171
|
-
interface MatchCorrection {
|
|
172
|
-
readonly score?: Partial<TeamScore>;
|
|
173
|
-
readonly serve?: ServeState;
|
|
174
|
-
readonly playerCourts?: Readonly<Record<string, CourtSide>>;
|
|
175
|
-
}
|
|
176
|
-
interface MatchEventBase {
|
|
177
|
-
readonly schemaVersion: 1;
|
|
178
|
-
readonly id: string;
|
|
179
|
-
readonly sequence: number;
|
|
180
|
-
readonly source: MatchInputSource;
|
|
181
|
-
}
|
|
182
|
-
interface MatchCreatedEvent extends MatchEventBase {
|
|
183
|
-
readonly type: "matchCreated";
|
|
184
|
-
readonly input: MatchCreateInput;
|
|
185
|
-
readonly snapshot: MatchSnapshot;
|
|
186
|
-
}
|
|
187
|
-
interface RallyAwardedEvent extends MatchEventBase {
|
|
188
|
-
readonly type: "rallyAwarded";
|
|
189
|
-
readonly displaySide: CourtSide;
|
|
190
|
-
readonly winnerTeamId: MatchTeamId;
|
|
191
|
-
}
|
|
192
|
-
interface CourtSwitchConfirmedEvent extends MatchEventBase {
|
|
193
|
-
readonly type: "courtSwitchConfirmed";
|
|
194
|
-
readonly pending: PendingCourtSwitch;
|
|
195
|
-
}
|
|
196
|
-
interface MatchCorrectedEvent extends MatchEventBase {
|
|
197
|
-
readonly type: "matchCorrected";
|
|
198
|
-
readonly correction: MatchCorrection;
|
|
199
|
-
}
|
|
200
|
-
interface MatchEndedEvent extends MatchEventBase {
|
|
201
|
-
readonly type: "matchEnded";
|
|
202
|
-
readonly reason: string;
|
|
203
|
-
}
|
|
204
|
-
interface MatchEventUndoneEvent extends MatchEventBase {
|
|
205
|
-
readonly type: "matchEventUndone";
|
|
206
|
-
readonly targetEventId: string;
|
|
207
|
-
readonly restoredSnapshot: MatchSnapshot;
|
|
208
|
-
}
|
|
209
|
-
type MatchEvent = MatchCreatedEvent | RallyAwardedEvent | CourtSwitchConfirmedEvent | MatchCorrectedEvent | MatchEndedEvent | MatchEventUndoneEvent;
|
|
210
|
-
interface MatchCreation {
|
|
211
|
-
readonly snapshot: MatchSnapshot;
|
|
212
|
-
readonly events: readonly [MatchCreatedEvent];
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
declare function createMatch(input: MatchCreateInput, source?: MatchInputSource): MatchCreation;
|
|
216
|
-
declare function applyMatchEvent(snapshot: MatchSnapshot, event: MatchEvent): MatchSnapshot;
|
|
217
|
-
declare function awardRally(snapshot: MatchSnapshot, displaySide: CourtSide, source?: MatchInputSource): RallyAwardedEvent | null;
|
|
218
|
-
declare function confirmCourtSwitch(snapshot: MatchSnapshot, source?: MatchInputSource): CourtSwitchConfirmedEvent;
|
|
219
|
-
declare function correctMatch(snapshot: MatchSnapshot, correction: MatchCorrection, source?: MatchInputSource): MatchCorrectedEvent;
|
|
220
|
-
declare function endMatch(snapshot: MatchSnapshot, reason: string, source?: MatchInputSource): MatchEndedEvent;
|
|
221
|
-
|
|
222
|
-
declare function replayMatchEvents(events: readonly unknown[]): MatchSnapshot;
|
|
223
|
-
declare function undoLastMatchEvent(events: readonly MatchEvent[], source?: MatchInputSource): MatchEventUndoneEvent | null;
|
|
224
|
-
|
|
225
|
-
declare const BTHOME_SERVICE_UUID: "FCD2";
|
|
226
|
-
type RemoteButtonAction = "single" | "double" | "long";
|
|
227
|
-
type RemoteInputMode = "bthome" | "hidVolume" | "both";
|
|
228
|
-
type RemoteMappedAction = "left" | "right" | "undo" | "none";
|
|
229
|
-
interface RemoteActionMapping {
|
|
230
|
-
readonly volumeUp: RemoteMappedAction;
|
|
231
|
-
readonly volumeDown: RemoteMappedAction;
|
|
232
|
-
readonly longPress: RemoteMappedAction;
|
|
233
|
-
}
|
|
234
|
-
declare const DEFAULT_REMOTE_ACTION_MAPPING: RemoteActionMapping;
|
|
235
|
-
interface RemoteHidVolumeEvent {
|
|
236
|
-
readonly source: "hidVolume";
|
|
237
|
-
readonly direction: "volumeUp" | "volumeDown";
|
|
238
|
-
readonly repeat: boolean;
|
|
239
|
-
readonly longPress: boolean;
|
|
240
|
-
readonly timestamp: number;
|
|
241
|
-
}
|
|
242
|
-
type RemoteHidGesture = "single" | "double" | "hold";
|
|
243
|
-
interface RemoteHidGestureMapping {
|
|
244
|
-
/** Raw HID direction emitted by the calibrated remote button. */
|
|
245
|
-
readonly direction: RemoteHidVolumeEvent["direction"];
|
|
246
|
-
/** Quiet window used to close one physical gesture burst. */
|
|
247
|
-
readonly settleMs: number;
|
|
248
|
-
/** Repeated volume changes required to classify a hold before the burst ends. */
|
|
249
|
-
readonly holdEventCount: number;
|
|
250
|
-
readonly single: RemoteMappedAction;
|
|
251
|
-
readonly double: RemoteMappedAction;
|
|
252
|
-
readonly hold: RemoteMappedAction;
|
|
253
|
-
}
|
|
254
|
-
interface RemoteBinding {
|
|
255
|
-
/** Stable identifier supplied by CoreBluetooth or BluetoothLeScanner. */
|
|
256
|
-
readonly deviceId: string;
|
|
257
|
-
readonly label?: string;
|
|
258
|
-
}
|
|
259
|
-
type RemotePermissionStatus = "unknown" | "granted" | "denied" | "restricted" | "unavailable";
|
|
260
|
-
type RemoteStatus = "unavailable" | "poweredOff" | "idle" | "scanning" | "bound" | "outOfRange";
|
|
261
|
-
interface RemoteCandidate {
|
|
262
|
-
readonly deviceId: string;
|
|
263
|
-
readonly label?: string;
|
|
264
|
-
readonly rssi: number;
|
|
265
|
-
readonly lastSeenAt: number;
|
|
266
|
-
}
|
|
267
|
-
interface RemoteState {
|
|
268
|
-
readonly status: RemoteStatus;
|
|
269
|
-
readonly permission: RemotePermissionStatus;
|
|
270
|
-
readonly binding: RemoteBinding | null;
|
|
271
|
-
readonly candidate: RemoteCandidate | null;
|
|
272
|
-
readonly lastSeenAt: number | null;
|
|
273
|
-
readonly error: RemoteError | null;
|
|
274
|
-
readonly hidCaptureArmed: boolean;
|
|
275
|
-
readonly lastInputSource: "bthome" | "hidVolume" | null;
|
|
276
|
-
readonly lastHidEvent: RemoteHidVolumeEvent | null;
|
|
277
|
-
}
|
|
278
|
-
type RemoteErrorCode = "permission_denied" | "permission_restricted" | "bluetooth_unavailable" | "bluetooth_powered_off" | "scan_failed" | "native_unavailable";
|
|
279
|
-
declare class RemoteError extends Error {
|
|
280
|
-
readonly code: RemoteErrorCode;
|
|
281
|
-
readonly name = "RemoteError";
|
|
282
|
-
constructor(code: RemoteErrorCode, message: string, options?: {
|
|
283
|
-
cause?: unknown;
|
|
284
|
-
});
|
|
285
|
-
}
|
|
286
|
-
interface RemoteScanAdvertisement {
|
|
287
|
-
readonly deviceId: string;
|
|
288
|
-
readonly name?: string;
|
|
289
|
-
readonly rssi: number;
|
|
290
|
-
readonly serviceUuid: string;
|
|
291
|
-
/** Base64 encoded FCD2 service-data payload, beginning with the device-info byte. */
|
|
292
|
-
readonly serviceData: string;
|
|
293
|
-
readonly timestamp: number;
|
|
294
|
-
}
|
|
295
|
-
type RemoteNativeState = {
|
|
296
|
-
readonly status: "permission";
|
|
297
|
-
readonly permission: RemotePermissionStatus;
|
|
298
|
-
} | {
|
|
299
|
-
readonly status: "poweredOff" | "unavailable" | "scanning" | "idle";
|
|
300
|
-
} | {
|
|
301
|
-
readonly status: "error";
|
|
302
|
-
readonly code: string;
|
|
303
|
-
readonly message: string;
|
|
304
|
-
};
|
|
305
|
-
interface RemoteService {
|
|
306
|
-
start(): Promise<void>;
|
|
307
|
-
stop(): Promise<void>;
|
|
308
|
-
dispose(): Promise<void>;
|
|
309
|
-
subscribeAdvertisement(listener: (event: RemoteScanAdvertisement) => void): () => void;
|
|
310
|
-
subscribeState(listener: (event: RemoteNativeState) => void): () => void;
|
|
311
|
-
startHidCapture(options?: {
|
|
312
|
-
consume?: boolean;
|
|
313
|
-
}): Promise<void>;
|
|
314
|
-
stopHidCapture(): Promise<void>;
|
|
315
|
-
subscribeHidVolume(listener: (event: RemoteHidVolumeEvent) => void): () => void;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
declare const MATCH_PERSISTENCE_SCHEMA_VERSION: 1;
|
|
319
|
-
interface StoredMatchRow {
|
|
320
|
-
readonly matchId: string;
|
|
321
|
-
readonly schemaVersion: number;
|
|
322
|
-
readonly snapshotJson: string;
|
|
323
|
-
readonly snapshotSequence: number;
|
|
324
|
-
readonly status: MatchSnapshot["status"];
|
|
325
|
-
readonly updatedAt: number;
|
|
326
|
-
}
|
|
327
|
-
interface StoredMatchEventRow {
|
|
328
|
-
readonly matchId: string;
|
|
329
|
-
readonly sequence: number;
|
|
330
|
-
readonly eventId: string;
|
|
331
|
-
readonly schemaVersion: number;
|
|
332
|
-
readonly eventJson: string;
|
|
333
|
-
readonly createdAt: number;
|
|
334
|
-
}
|
|
335
|
-
interface MatchPersistenceTransaction {
|
|
336
|
-
getMatch(matchId: string): Promise<StoredMatchRow | null>;
|
|
337
|
-
getEvents(matchId: string): Promise<readonly StoredMatchEventRow[]>;
|
|
338
|
-
insertMatch(row: StoredMatchRow): Promise<void>;
|
|
339
|
-
insertEvent(row: StoredMatchEventRow): Promise<void>;
|
|
340
|
-
updateSnapshot(matchId: string, expectedSequence: number, row: Pick<StoredMatchRow, "schemaVersion" | "snapshotJson" | "snapshotSequence" | "status" | "updatedAt">): Promise<boolean>;
|
|
341
|
-
setActiveMatch(matchId: string | null): Promise<void>;
|
|
342
|
-
clearActiveMatch(matchId: string): Promise<void>;
|
|
343
|
-
}
|
|
344
|
-
interface MatchPersistenceDriver {
|
|
345
|
-
migrate(): Promise<void>;
|
|
346
|
-
transaction<T>(work: (transaction: MatchPersistenceTransaction) => Promise<T>): Promise<T>;
|
|
347
|
-
readMatch(matchId: string): Promise<{
|
|
348
|
-
readonly match: StoredMatchRow | null;
|
|
349
|
-
readonly events: readonly StoredMatchEventRow[];
|
|
350
|
-
}>;
|
|
351
|
-
getActiveMatchId(): Promise<string | null>;
|
|
352
|
-
readActiveMatch(): Promise<{
|
|
353
|
-
readonly activeMatchId: string | null;
|
|
354
|
-
readonly match: StoredMatchRow | null;
|
|
355
|
-
readonly events: readonly StoredMatchEventRow[];
|
|
356
|
-
}>;
|
|
357
|
-
readMatchList(): Promise<{
|
|
358
|
-
readonly matches: readonly StoredMatchRow[];
|
|
359
|
-
readonly activeMatchId: string | null;
|
|
360
|
-
}>;
|
|
361
|
-
getRemoteBinding?(): Promise<RemoteBinding | null>;
|
|
362
|
-
setRemoteBinding?(binding: RemoteBinding | null): Promise<void>;
|
|
363
|
-
}
|
|
364
|
-
interface PersistedMatch {
|
|
365
|
-
readonly snapshot: MatchSnapshot;
|
|
366
|
-
readonly events: readonly MatchEvent[];
|
|
367
|
-
}
|
|
368
|
-
interface MatchSummary {
|
|
369
|
-
readonly id: string;
|
|
370
|
-
readonly sequence: number;
|
|
371
|
-
readonly status: MatchSnapshot["status"];
|
|
372
|
-
readonly updatedAt: number;
|
|
373
|
-
readonly isActive: boolean;
|
|
374
|
-
}
|
|
375
|
-
type MatchPersistenceErrorCode = "not_found" | "already_exists" | "conflict" | "corrupt_data" | "unsupported_schema" | "sequence_mismatch" | "snapshot_mismatch" | "storage_failure";
|
|
376
|
-
declare class MatchPersistenceError extends Error {
|
|
377
|
-
readonly code: MatchPersistenceErrorCode;
|
|
378
|
-
readonly name = "MatchPersistenceError";
|
|
379
|
-
readonly recoverable = true;
|
|
380
|
-
readonly retryable: boolean;
|
|
381
|
-
constructor(code: MatchPersistenceErrorCode, message: string, options?: {
|
|
382
|
-
cause?: unknown;
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
declare const MATCH_SQL_SCHEMA_VERSION: 2;
|
|
387
|
-
declare const MATCH_SQL_MIGRATIONS: readonly ["CREATE TABLE IF NOT EXISTS match_aggregates (\n match_id TEXT PRIMARY KEY NOT NULL,\n schema_version INTEGER NOT NULL CHECK (schema_version = 1),\n snapshot_json TEXT NOT NULL,\n snapshot_sequence INTEGER NOT NULL CHECK (snapshot_sequence >= 0),\n status TEXT NOT NULL CHECK (status IN ('inProgress', 'completed', 'ended')),\n updated_at INTEGER NOT NULL\n )", "CREATE TABLE IF NOT EXISTS match_events (\n match_id TEXT NOT NULL,\n sequence INTEGER NOT NULL CHECK (sequence >= 0),\n event_id TEXT NOT NULL UNIQUE,\n schema_version INTEGER NOT NULL CHECK (schema_version = 1),\n event_json TEXT NOT NULL,\n created_at INTEGER NOT NULL,\n PRIMARY KEY (match_id, sequence),\n FOREIGN KEY (match_id) REFERENCES match_aggregates(match_id) ON DELETE RESTRICT\n )", "CREATE TABLE IF NOT EXISTS match_active_pointer (\n slot INTEGER PRIMARY KEY NOT NULL CHECK (slot = 1),\n match_id TEXT NOT NULL UNIQUE,\n FOREIGN KEY (match_id) REFERENCES match_aggregates(match_id) ON DELETE RESTRICT\n )", "CREATE INDEX IF NOT EXISTS match_aggregates_recent\n ON match_aggregates(updated_at DESC)", "CREATE TRIGGER IF NOT EXISTS match_events_no_update\n BEFORE UPDATE ON match_events\n BEGIN SELECT RAISE(ABORT, 'match events are immutable'); END", "CREATE TRIGGER IF NOT EXISTS match_events_no_delete\n BEFORE DELETE ON match_events\n BEGIN SELECT RAISE(ABORT, 'match events are immutable'); END", "CREATE TABLE IF NOT EXISTS remote_binding (\n slot INTEGER PRIMARY KEY NOT NULL CHECK (slot = 1),\n device_id TEXT NOT NULL CHECK (length(trim(device_id)) > 0),\n label TEXT\n )"];
|
|
388
|
-
|
|
389
|
-
interface MemoryState {
|
|
390
|
-
migrationVersion: number;
|
|
391
|
-
remoteBinding: RemoteBinding | null;
|
|
392
|
-
activeMatchId: string | null;
|
|
393
|
-
matches: StoredMatchRow[];
|
|
394
|
-
events: StoredMatchEventRow[];
|
|
395
|
-
nextOrdinal: number;
|
|
396
|
-
ordinals: Record<string, number>;
|
|
397
|
-
}
|
|
398
|
-
declare class MemoryMatchPersistenceDriver implements MatchPersistenceDriver {
|
|
399
|
-
private state;
|
|
400
|
-
private eventInsertFailure;
|
|
401
|
-
private snapshotUpdateFailure;
|
|
402
|
-
private transactionTail;
|
|
403
|
-
migrate(): Promise<void>;
|
|
404
|
-
getRemoteBinding(): Promise<{
|
|
405
|
-
deviceId: string;
|
|
406
|
-
label?: string;
|
|
407
|
-
} | null>;
|
|
408
|
-
setRemoteBinding(binding: RemoteBinding | null): Promise<void>;
|
|
409
|
-
getMigrationVersion(): number;
|
|
410
|
-
transaction<T>(work: (transaction: MatchPersistenceTransaction) => Promise<T>): Promise<T>;
|
|
411
|
-
readMatch(matchId: string): Promise<{
|
|
412
|
-
match: StoredMatchRow | null;
|
|
413
|
-
events: StoredMatchEventRow[];
|
|
414
|
-
}>;
|
|
415
|
-
getActiveMatchId(): Promise<string | null>;
|
|
416
|
-
readActiveMatch(): Promise<{
|
|
417
|
-
activeMatchId: null;
|
|
418
|
-
match: null;
|
|
419
|
-
events: never[];
|
|
420
|
-
} | {
|
|
421
|
-
activeMatchId: string;
|
|
422
|
-
match: StoredMatchRow | null;
|
|
423
|
-
events: StoredMatchEventRow[];
|
|
424
|
-
}>;
|
|
425
|
-
readMatchList(): Promise<{
|
|
426
|
-
matches: StoredMatchRow[];
|
|
427
|
-
activeMatchId: string | null;
|
|
428
|
-
}>;
|
|
429
|
-
inspect(): MemoryState;
|
|
430
|
-
failNextSnapshotUpdate(error: Error): void;
|
|
431
|
-
failNextEventInsert(error: Error): void;
|
|
432
|
-
corruptSnapshotJson(matchId: string, snapshotJson: string): void;
|
|
433
|
-
corruptEventJson(matchId: string, sequence: number, eventJson: string): void;
|
|
434
|
-
corruptEventSchema(matchId: string, sequence: number, schemaVersion: number): void;
|
|
435
|
-
corruptEventSequence(matchId: string, sequence: number): void;
|
|
436
|
-
duplicateEventRow(matchId: string, sequence: number): void;
|
|
437
|
-
removeEventRow(matchId: string, sequence: number): void;
|
|
438
|
-
private mutateMatch;
|
|
439
|
-
private mutateEvent;
|
|
440
|
-
}
|
|
441
|
-
|
|
442
|
-
interface SqlRunResult {
|
|
443
|
-
readonly changes: number;
|
|
444
|
-
readonly lastInsertRowId: number;
|
|
445
|
-
}
|
|
446
|
-
interface SqlExecutor {
|
|
447
|
-
runAsync(sql: string, ...params: unknown[]): Promise<SqlRunResult>;
|
|
448
|
-
getFirstAsync<T>(sql: string, ...params: unknown[]): Promise<T | null>;
|
|
449
|
-
getAllAsync<T>(sql: string, ...params: unknown[]): Promise<T[]>;
|
|
450
|
-
}
|
|
451
|
-
interface ExpoSqliteDatabaseLike extends SqlExecutor {
|
|
452
|
-
execAsync(sql: string): Promise<void>;
|
|
453
|
-
}
|
|
454
|
-
/**
|
|
455
|
-
* Serializes adapters sharing this exact connection object. Separate
|
|
456
|
-
* connections to the same file rely on SQLite locking plus busy_timeout.
|
|
457
|
-
*/
|
|
458
|
-
declare function createExpoSqliteMatchDriver(database: ExpoSqliteDatabaseLike): MatchPersistenceDriver;
|
|
459
|
-
|
|
460
|
-
interface MatchEventStoreOptions {
|
|
461
|
-
readonly now?: () => number;
|
|
462
|
-
}
|
|
463
|
-
declare class MatchEventStore {
|
|
464
|
-
private readonly driver;
|
|
465
|
-
private initialized;
|
|
466
|
-
private readonly now;
|
|
467
|
-
constructor(driver: MatchPersistenceDriver, options?: MatchEventStoreOptions);
|
|
468
|
-
initialize(): Promise<void>;
|
|
469
|
-
getRemoteBinding(): Promise<RemoteBinding | null>;
|
|
470
|
-
setRemoteBinding(binding: RemoteBinding | null): Promise<void>;
|
|
471
|
-
createMatch(input: MatchCreateInput, source?: MatchInputSource): Promise<PersistedMatch>;
|
|
472
|
-
appendEvent(currentSnapshot: MatchSnapshot, event: MatchEvent): Promise<PersistedMatch>;
|
|
473
|
-
resumeActiveMatch(): Promise<PersistedMatch | null>;
|
|
474
|
-
resumeMatch(matchId: string): Promise<PersistedMatch | null>;
|
|
475
|
-
listMatches(): Promise<readonly MatchSummary[]>;
|
|
476
|
-
archiveMatch(matchId: string): Promise<void>;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
declare function createDefaultMatchEventStore(_databaseName?: string): Promise<MatchEventStore>;
|
|
480
|
-
|
|
481
|
-
type AnnouncementKind = "score" | "serviceChange" | "gamePoint" | "gameWon" | "courtSwitch" | "matchWon";
|
|
482
|
-
type FallbackVoiceToken = `number-${number}` | "number-mot" | "number-lam" | `score-deu-${number}` | "score-deu-mot" | "score-deu-lam" | "server-1" | "server-2" | "court-left" | "court-right" | "service-change" | "game-point" | "game-won" | "court-switch" | "match-won" | "test-speaker";
|
|
483
|
-
interface AnnouncementPlan {
|
|
484
|
-
readonly kind: AnnouncementKind;
|
|
485
|
-
readonly text: string;
|
|
486
|
-
readonly score?: readonly [number, number];
|
|
487
|
-
readonly fallbackTokens: readonly FallbackVoiceToken[];
|
|
488
|
-
}
|
|
489
|
-
type VoiceAudioRoute = "system" | "bluetoothA2DP" | "bluetoothHFP" | "speaker" | "headphones" | "unknown";
|
|
490
|
-
type VoiceDisconnectReason = "oldDeviceUnavailable" | "becomingNoisy" | "unknown";
|
|
491
|
-
interface VoiceRouteState {
|
|
492
|
-
readonly route: VoiceAudioRoute;
|
|
493
|
-
readonly interrupted: boolean;
|
|
494
|
-
readonly disconnectedReason: VoiceDisconnectReason | null;
|
|
495
|
-
}
|
|
496
|
-
type VoiceAudioMode = "offline" | "live";
|
|
497
|
-
interface AudioPlayback {
|
|
498
|
-
configure(mode: VoiceAudioMode): Promise<void>;
|
|
499
|
-
play(source: string): Promise<void>;
|
|
500
|
-
stop(): Promise<void>;
|
|
501
|
-
dispose(): Promise<void>;
|
|
502
|
-
getRouteState(): VoiceRouteState;
|
|
503
|
-
subscribeToRouteChanges?(listener: (state: VoiceRouteState) => void): () => void;
|
|
504
|
-
}
|
|
505
|
-
type NeuralVoiceAvailability = "available" | "unavailable";
|
|
506
|
-
interface NeuralVoiceEngine {
|
|
507
|
-
availability(): Promise<NeuralVoiceAvailability>;
|
|
508
|
-
prewarm(commonPhrases: readonly string[]): Promise<void>;
|
|
509
|
-
synthesize(text: string, signal: AbortSignal): Promise<string>;
|
|
510
|
-
cancel(): Promise<void>;
|
|
511
|
-
release?(filePath: string): Promise<void>;
|
|
512
|
-
dispose(): Promise<void>;
|
|
513
|
-
}
|
|
514
|
-
/** Cloud TTS endpoint the SDK posts announcement text to (see apps/proxy /voice/tts). */
|
|
515
|
-
interface VoiceTtsOptions {
|
|
516
|
-
readonly url: string;
|
|
517
|
-
/** Extra request headers, e.g. partner bearer tokens. Re-evaluated per request. */
|
|
518
|
-
readonly headers?: () => Readonly<Record<string, string>> | Promise<Readonly<Record<string, string>>>;
|
|
519
|
-
readonly voiceName?: string;
|
|
520
|
-
readonly languageCode?: string;
|
|
521
|
-
readonly requestTimeoutMs?: number;
|
|
522
|
-
}
|
|
523
|
-
type VoiceErrorCode = "engine_unavailable" | "network_unavailable" | "synthesis_failed" | "storage_failure" | "playback_failed";
|
|
524
|
-
declare class VoiceError extends Error {
|
|
525
|
-
readonly code: VoiceErrorCode;
|
|
526
|
-
readonly name = "VoiceError";
|
|
527
|
-
constructor(code: VoiceErrorCode, message: string, options?: {
|
|
528
|
-
cause?: unknown;
|
|
529
|
-
});
|
|
530
|
-
}
|
|
531
|
-
interface MatchVoiceService {
|
|
532
|
-
readonly audioState: VoiceRouteState;
|
|
533
|
-
readonly lastError?: Error | null;
|
|
534
|
-
initialize(): Promise<void>;
|
|
535
|
-
subscribe(listener: () => void): () => void;
|
|
536
|
-
announce(previous: MatchSnapshot, current: MatchSnapshot, event: MatchEvent): void;
|
|
537
|
-
replayAnnouncement(): Promise<void>;
|
|
538
|
-
testSpeaker(): Promise<void>;
|
|
539
|
-
configureAudioMode?(mode: VoiceAudioMode): Promise<void>;
|
|
540
|
-
dispose(): Promise<void>;
|
|
541
|
-
}
|
|
542
|
-
|
|
543
|
-
interface PickleballMatchStore {
|
|
544
|
-
initialize(): Promise<void>;
|
|
545
|
-
createMatch(input: MatchCreateInput, source?: MatchInputSource): Promise<PersistedMatch>;
|
|
546
|
-
appendEvent(currentSnapshot: MatchSnapshot, event: MatchEvent): Promise<PersistedMatch>;
|
|
547
|
-
resumeMatch(matchId: string): Promise<PersistedMatch | null>;
|
|
548
|
-
resumeActiveMatch(): Promise<PersistedMatch | null>;
|
|
549
|
-
listMatches(): Promise<readonly MatchSummary[]>;
|
|
550
|
-
archiveMatch(matchId: string): Promise<void>;
|
|
551
|
-
getRemoteBinding?(): Promise<RemoteBinding | null>;
|
|
552
|
-
setRemoteBinding?(binding: RemoteBinding | null): Promise<void>;
|
|
553
|
-
}
|
|
554
|
-
declare class NoActiveMatchError extends Error {
|
|
555
|
-
readonly name = "NoActiveMatchError";
|
|
556
|
-
readonly code = "NO_ACTIVE_MATCH";
|
|
557
|
-
constructor();
|
|
558
|
-
}
|
|
559
|
-
/**
|
|
560
|
-
* State-changing actions reject with NoActiveMatchError when no match is loaded.
|
|
561
|
-
* Commands the engine deems ineligible are ignored and resolve the current snapshot.
|
|
562
|
-
*/
|
|
563
|
-
interface PickleballMatchContextValue {
|
|
564
|
-
readonly snapshot: MatchSnapshot | null;
|
|
565
|
-
readonly events: readonly MatchEvent[];
|
|
566
|
-
readonly loading: boolean;
|
|
567
|
-
readonly error: Error | null;
|
|
568
|
-
readonly voiceError: Error | null;
|
|
569
|
-
readonly audioState: VoiceRouteState;
|
|
570
|
-
readonly remoteState: RemoteState;
|
|
571
|
-
createMatch(input: MatchCreateInput, source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
572
|
-
resumeMatch(matchId?: string): Promise<MatchSnapshot | null>;
|
|
573
|
-
awardRallyToSide(side: CourtSide, source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
574
|
-
undo(source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
575
|
-
correctState(correction: MatchCorrection, source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
576
|
-
confirmCourtSwitch(source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
577
|
-
endMatch(reason: string, source?: MatchInputSource): Promise<MatchSnapshot>;
|
|
578
|
-
testSpeaker(): Promise<void>;
|
|
579
|
-
replayAnnouncement(): Promise<void>;
|
|
580
|
-
setVoiceAudioMode(mode: VoiceAudioMode): Promise<void>;
|
|
581
|
-
bindRemote(binding: RemoteBinding): Promise<void>;
|
|
582
|
-
unbindRemote(): Promise<void>;
|
|
583
|
-
startRemoteScan(): Promise<void>;
|
|
584
|
-
stopRemoteScan(): Promise<void>;
|
|
585
|
-
startHidCapture(): Promise<void>;
|
|
586
|
-
stopHidCapture(): Promise<void>;
|
|
587
|
-
}
|
|
588
|
-
interface PickleballMatchProviderProps {
|
|
589
|
-
readonly children?: ReactNode;
|
|
590
|
-
/** Fixed for this provider instance. Remount to use a different local store. */
|
|
591
|
-
readonly store?: PickleballMatchStore;
|
|
592
|
-
/** Fixed for this provider instance. Remount to use a different factory. */
|
|
593
|
-
readonly storeFactory?: () => Promise<PickleballMatchStore>;
|
|
594
|
-
/** Resumes the active local match on mount. Disable for hosts that resume explicitly. */
|
|
595
|
-
readonly autoResume?: boolean;
|
|
596
|
-
readonly onError?: (error: Error) => void | Promise<void>;
|
|
597
|
-
/** Fixed for this provider instance. Primarily useful for custom storage and tests. */
|
|
598
|
-
readonly voiceService?: MatchVoiceService;
|
|
599
|
-
/** Initial audio session mode. Defaults to live so mounting never disables capture. */
|
|
600
|
-
readonly voiceAudioMode?: VoiceAudioMode;
|
|
601
|
-
/** Cloud TTS endpoint for announcements. Without it only bundled WAVs play. Fixed for this provider instance. */
|
|
602
|
-
readonly voiceTts?: VoiceTtsOptions;
|
|
603
|
-
/** Fixed for this provider instance. Primarily useful for tests and custom native bridges. */
|
|
604
|
-
readonly remoteService?: RemoteService;
|
|
605
|
-
/** Foreground silence before a bound remote becomes outOfRange. */
|
|
606
|
-
readonly remoteOutOfRangeTimeoutMs?: number;
|
|
607
|
-
readonly remoteInputMode?: RemoteInputMode;
|
|
608
|
-
readonly remoteActionMapping?: Partial<RemoteActionMapping>;
|
|
609
|
-
/** Optional single/double/hold recognizer for one calibrated HID volume button. */
|
|
610
|
-
readonly hidGestureMapping?: RemoteHidGestureMapping;
|
|
611
|
-
readonly consumeHidVolumeKeys?: boolean;
|
|
612
|
-
}
|
|
613
|
-
declare function PickleballMatchProvider({ children, store, storeFactory, autoResume, onError, voiceService, voiceAudioMode, voiceTts, remoteService, remoteOutOfRangeTimeoutMs, remoteInputMode, remoteActionMapping, hidGestureMapping, consumeHidVolumeKeys, }: PickleballMatchProviderProps): React.JSX.Element;
|
|
614
|
-
declare function usePickleballMatch(): PickleballMatchContextValue;
|
|
615
|
-
|
|
616
|
-
type BTHomeRemoteParseErrorCode = "wrong_uuid" | "unsupported_version" | "encrypted" | "truncated" | "unknown_object" | "invalid_button_event" | "no_button";
|
|
617
|
-
declare class BTHomeRemoteParseError extends Error {
|
|
618
|
-
readonly code: BTHomeRemoteParseErrorCode;
|
|
619
|
-
readonly name = "BTHomeRemoteParseError";
|
|
620
|
-
constructor(code: BTHomeRemoteParseErrorCode, message: string);
|
|
621
|
-
}
|
|
622
|
-
interface BTHomeAdvertisementInput {
|
|
623
|
-
readonly deviceId: string;
|
|
624
|
-
readonly serviceUuid: string;
|
|
625
|
-
/** FCD2 service data only. The first byte must be BTHome device-info, not UUID bytes. */
|
|
626
|
-
readonly serviceData: Uint8Array;
|
|
627
|
-
}
|
|
628
|
-
interface ParsedBTHomeRemoteAdvertisement {
|
|
629
|
-
readonly deviceId: string;
|
|
630
|
-
readonly version: 2;
|
|
631
|
-
readonly encrypted: false;
|
|
632
|
-
readonly triggerBased: boolean;
|
|
633
|
-
readonly packetId?: number;
|
|
634
|
-
readonly button: {
|
|
635
|
-
readonly action: RemoteButtonAction;
|
|
636
|
-
readonly objectIndex: number;
|
|
637
|
-
readonly rawEvent: number;
|
|
638
|
-
};
|
|
639
|
-
readonly fingerprint: string;
|
|
640
|
-
}
|
|
641
|
-
declare function parseBTHomeV2Advertisement(input: BTHomeAdvertisementInput): ParsedBTHomeRemoteAdvertisement;
|
|
642
|
-
|
|
643
|
-
interface RemoteDeduplicationKey {
|
|
644
|
-
readonly packetId?: number;
|
|
645
|
-
readonly fingerprint: string;
|
|
646
|
-
}
|
|
647
|
-
interface BTHomeRemoteDeduplicatorOptions {
|
|
648
|
-
readonly fingerprintWindowMs?: number;
|
|
649
|
-
}
|
|
650
|
-
declare class BTHomeRemoteDeduplicator {
|
|
651
|
-
private readonly devices;
|
|
652
|
-
private readonly fingerprintWindowMs;
|
|
653
|
-
constructor(options?: BTHomeRemoteDeduplicatorOptions);
|
|
654
|
-
get trackedDeviceCount(): number;
|
|
655
|
-
accept(deviceId: string, key: RemoteDeduplicationKey, now?: number): boolean;
|
|
656
|
-
forget(deviceId: string): void;
|
|
657
|
-
clear(): void;
|
|
658
|
-
}
|
|
659
|
-
|
|
660
|
-
declare function decodeBase64Bytes(value: string): Uint8Array;
|
|
661
|
-
|
|
662
|
-
interface HidVolumeGestureRecognizerOptions extends Pick<RemoteHidGestureMapping, "direction" | "settleMs" | "holdEventCount"> {
|
|
663
|
-
readonly onGesture: (gesture: RemoteHidGesture) => void;
|
|
664
|
-
}
|
|
665
|
-
declare class HidVolumeGestureRecognizer {
|
|
666
|
-
private readonly options;
|
|
667
|
-
private count;
|
|
668
|
-
private resolved;
|
|
669
|
-
private lastTimestamp;
|
|
670
|
-
private timer;
|
|
671
|
-
constructor(options: HidVolumeGestureRecognizerOptions);
|
|
672
|
-
push(event: RemoteHidVolumeEvent): void;
|
|
673
|
-
dispose(): void;
|
|
674
|
-
private finishBurst;
|
|
675
|
-
private clearTimer;
|
|
676
|
-
private reset;
|
|
677
|
-
}
|
|
678
|
-
|
|
679
|
-
/** Pure phrase selection from the state before and after one committed event. */
|
|
680
|
-
declare function buildVietnameseAnnouncement(previous: MatchSnapshot, current: MatchSnapshot, event: MatchEvent): AnnouncementPlan;
|
|
681
|
-
declare function planBundledFallback(plan: AnnouncementPlan): readonly string[];
|
|
682
|
-
|
|
683
|
-
interface LatestAnnouncementSchedulerOptions {
|
|
684
|
-
readonly engine: NeuralVoiceEngine;
|
|
685
|
-
readonly audio: AudioPlayback;
|
|
686
|
-
readonly onError?: (error: Error) => void;
|
|
687
|
-
}
|
|
688
|
-
declare class LatestAnnouncementScheduler {
|
|
689
|
-
private readonly options;
|
|
690
|
-
private static readonly MAX_CACHE_ENTRIES;
|
|
691
|
-
private generation;
|
|
692
|
-
private activeController;
|
|
693
|
-
private lastPlan;
|
|
694
|
-
private readonly neuralCache;
|
|
695
|
-
private readonly operations;
|
|
696
|
-
private readonly prewarmControllers;
|
|
697
|
-
private lifecycle;
|
|
698
|
-
private disposePromise;
|
|
699
|
-
constructor(options: LatestAnnouncementSchedulerOptions);
|
|
700
|
-
prewarm(commonPhrases?: readonly string[]): Promise<void>;
|
|
701
|
-
enqueue(plan: AnnouncementPlan): Promise<void>;
|
|
702
|
-
private performEnqueue;
|
|
703
|
-
replayLast(): Promise<void>;
|
|
704
|
-
testSpeaker(): Promise<void>;
|
|
705
|
-
private performSpeakerTest;
|
|
706
|
-
dispose(): Promise<void>;
|
|
707
|
-
clearCache(): Promise<void>;
|
|
708
|
-
private cacheFile;
|
|
709
|
-
private touchCachedFile;
|
|
710
|
-
private track;
|
|
711
|
-
private report;
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
interface ExpoAudioPlayer {
|
|
715
|
-
play(): void;
|
|
716
|
-
pause(): void;
|
|
717
|
-
remove(): void;
|
|
718
|
-
addListener(event: "playbackStatusUpdate", listener: (status: {
|
|
719
|
-
readonly didJustFinish?: boolean;
|
|
720
|
-
readonly isLoaded?: boolean;
|
|
721
|
-
readonly isBuffering?: boolean;
|
|
722
|
-
readonly error?: unknown;
|
|
723
|
-
readonly playbackState?: string;
|
|
724
|
-
}) => void): {
|
|
725
|
-
remove(): void;
|
|
726
|
-
};
|
|
727
|
-
}
|
|
728
|
-
interface ExpoAudioBindings {
|
|
729
|
-
setAudioModeAsync(options: {
|
|
730
|
-
readonly allowsRecording: boolean;
|
|
731
|
-
readonly playsInSilentMode: boolean;
|
|
732
|
-
}): Promise<void>;
|
|
733
|
-
createAudioPlayer(source: unknown): ExpoAudioPlayer;
|
|
734
|
-
}
|
|
735
|
-
interface ExpoAudioPlaybackOptions {
|
|
736
|
-
readonly load?: () => ExpoAudioBindings;
|
|
737
|
-
readonly loadNativeAudio?: () => NativeAudioSessionBindings;
|
|
738
|
-
readonly resolveSource?: (source: string) => unknown;
|
|
739
|
-
readonly playbackTimeoutMs?: number;
|
|
740
|
-
}
|
|
741
|
-
interface NativeAudioSessionBindings {
|
|
742
|
-
captureAudioSessionState(): Promise<void>;
|
|
743
|
-
configureAudioSession(mode: VoiceAudioMode): Promise<VoiceRouteState>;
|
|
744
|
-
getAudioRouteState(): VoiceRouteState | Promise<VoiceRouteState>;
|
|
745
|
-
addListener(event: "onAudioRouteChanged", listener: (state: VoiceRouteState) => void): {
|
|
746
|
-
remove(): void;
|
|
747
|
-
};
|
|
748
|
-
disposeAudioSession(): Promise<void>;
|
|
749
|
-
}
|
|
750
|
-
declare function createExpoAudioPlayback(options?: ExpoAudioPlaybackOptions): AudioPlayback;
|
|
751
|
-
|
|
752
|
-
/** Minimal file operations the engine needs; defaults to expo-file-system. */
|
|
753
|
-
interface GeneratedAudioFileSystem {
|
|
754
|
-
writeFile(path: string, bytes: Uint8Array): Promise<void>;
|
|
755
|
-
removeFile(path: string): Promise<void>;
|
|
756
|
-
}
|
|
757
|
-
interface GeminiVoiceEngineOptions extends VoiceTtsOptions {
|
|
758
|
-
/** Directory that receives generated WAV files. */
|
|
759
|
-
readonly generatedRoot: string;
|
|
760
|
-
readonly fetch?: typeof globalThis.fetch;
|
|
761
|
-
readonly fileSystem?: GeneratedAudioFileSystem;
|
|
762
|
-
}
|
|
763
|
-
/**
|
|
764
|
-
* Cloud announcement voice: posts the phrase to the partner proxy, which calls
|
|
765
|
-
* Vertex AI Gemini TTS and returns a WAV body. Failures surface as VoiceError
|
|
766
|
-
* so the scheduler drops to the bundled WAV fallback.
|
|
767
|
-
*/
|
|
768
|
-
declare function createGeminiVoiceEngine(options: GeminiVoiceEngineOptions): NeuralVoiceEngine;
|
|
769
|
-
/** Placeholder engine used when no TTS endpoint is configured: always falls back. */
|
|
770
|
-
declare function createUnavailableVoiceEngine(): NeuralVoiceEngine;
|
|
771
|
-
|
|
772
|
-
declare function resolveBundledVoiceAsset(source: string): string;
|
|
773
|
-
|
|
774
|
-
interface DefaultMatchVoiceServiceOptions {
|
|
775
|
-
/** Cloud TTS endpoint. Without it, announcements always use the bundled WAVs. */
|
|
776
|
-
readonly tts?: VoiceTtsOptions;
|
|
777
|
-
readonly audioMode?: VoiceAudioMode;
|
|
778
|
-
/** Exact cache directory for generated WAVs. Primarily useful for tests. */
|
|
779
|
-
readonly generatedRoot?: string;
|
|
780
|
-
/** Primarily useful for tests. Defaults to expo-file-system removal. */
|
|
781
|
-
readonly removeGeneratedDirectory?: (path: string) => Promise<void>;
|
|
782
|
-
/** Primarily useful for native integration tests. */
|
|
783
|
-
readonly audio?: AudioPlayback;
|
|
784
|
-
/** Primarily useful for native integration tests. */
|
|
785
|
-
readonly engine?: NeuralVoiceEngine;
|
|
786
|
-
}
|
|
787
|
-
declare function createDefaultMatchVoiceService(options?: DefaultMatchVoiceServiceOptions): MatchVoiceService;
|
|
788
|
-
|
|
789
|
-
type ExpoSdkErrorCode = SdkErrorCode | "CONFIGURATION_ERROR" | "UNSUPPORTED_RUNTIME" | "INVALID_RESPONSE" | "TIMEOUT";
|
|
790
|
-
declare class PickleballExpoError extends Error {
|
|
791
|
-
readonly code: ExpoSdkErrorCode;
|
|
792
|
-
constructor(message: string, code: ExpoSdkErrorCode, options?: ErrorOptions);
|
|
793
|
-
}
|
|
794
|
-
declare const PermissionDeniedError_base: {
|
|
795
|
-
new (message?: string, options?: ErrorOptions): {
|
|
796
|
-
readonly code: ExpoSdkErrorCode;
|
|
797
|
-
name: string;
|
|
798
|
-
message: string;
|
|
799
|
-
stack?: string;
|
|
800
|
-
cause?: unknown;
|
|
801
|
-
};
|
|
802
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
803
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
804
|
-
stackTraceLimit: number;
|
|
805
|
-
};
|
|
806
|
-
declare class PermissionDeniedError extends PermissionDeniedError_base {
|
|
807
|
-
}
|
|
808
|
-
declare const NetworkUnavailableError_base: {
|
|
809
|
-
new (message?: string, options?: ErrorOptions): {
|
|
810
|
-
readonly code: ExpoSdkErrorCode;
|
|
811
|
-
name: string;
|
|
812
|
-
message: string;
|
|
813
|
-
stack?: string;
|
|
814
|
-
cause?: unknown;
|
|
815
|
-
};
|
|
816
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
817
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
818
|
-
stackTraceLimit: number;
|
|
819
|
-
};
|
|
820
|
-
declare class NetworkUnavailableError extends NetworkUnavailableError_base {
|
|
821
|
-
}
|
|
822
|
-
declare const TokenExpiredError_base: {
|
|
823
|
-
new (message?: string, options?: ErrorOptions): {
|
|
824
|
-
readonly code: ExpoSdkErrorCode;
|
|
825
|
-
name: string;
|
|
826
|
-
message: string;
|
|
827
|
-
stack?: string;
|
|
828
|
-
cause?: unknown;
|
|
829
|
-
};
|
|
830
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
831
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
832
|
-
stackTraceLimit: number;
|
|
833
|
-
};
|
|
834
|
-
declare class TokenExpiredError extends TokenExpiredError_base {
|
|
835
|
-
}
|
|
836
|
-
declare const UnsupportedSdkError_base: {
|
|
837
|
-
new (message?: string, options?: ErrorOptions): {
|
|
838
|
-
readonly code: ExpoSdkErrorCode;
|
|
839
|
-
name: string;
|
|
840
|
-
message: string;
|
|
841
|
-
stack?: string;
|
|
842
|
-
cause?: unknown;
|
|
843
|
-
};
|
|
844
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
845
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
846
|
-
stackTraceLimit: number;
|
|
847
|
-
};
|
|
848
|
-
declare class UnsupportedSdkError extends UnsupportedSdkError_base {
|
|
849
|
-
}
|
|
850
|
-
declare const UnsupportedRuntimeError_base: {
|
|
851
|
-
new (message?: string, options?: ErrorOptions): {
|
|
852
|
-
readonly code: ExpoSdkErrorCode;
|
|
853
|
-
name: string;
|
|
854
|
-
message: string;
|
|
855
|
-
stack?: string;
|
|
856
|
-
cause?: unknown;
|
|
857
|
-
};
|
|
858
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
859
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
860
|
-
stackTraceLimit: number;
|
|
861
|
-
};
|
|
862
|
-
declare class UnsupportedRuntimeError extends UnsupportedRuntimeError_base {
|
|
863
|
-
}
|
|
864
|
-
declare const SessionConflictError_base: {
|
|
865
|
-
new (message?: string, options?: ErrorOptions): {
|
|
866
|
-
readonly code: ExpoSdkErrorCode;
|
|
867
|
-
name: string;
|
|
868
|
-
message: string;
|
|
869
|
-
stack?: string;
|
|
870
|
-
cause?: unknown;
|
|
871
|
-
};
|
|
872
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
873
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
874
|
-
stackTraceLimit: number;
|
|
875
|
-
};
|
|
876
|
-
declare class SessionConflictError extends SessionConflictError_base {
|
|
877
|
-
}
|
|
878
|
-
declare const RecordingFailureError_base: {
|
|
879
|
-
new (message?: string, options?: ErrorOptions): {
|
|
880
|
-
readonly code: ExpoSdkErrorCode;
|
|
881
|
-
name: string;
|
|
882
|
-
message: string;
|
|
883
|
-
stack?: string;
|
|
884
|
-
cause?: unknown;
|
|
885
|
-
};
|
|
886
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
887
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
888
|
-
stackTraceLimit: number;
|
|
889
|
-
};
|
|
890
|
-
declare class RecordingFailureError extends RecordingFailureError_base {
|
|
891
|
-
}
|
|
892
|
-
declare const ConsentRequiredError_base: {
|
|
893
|
-
new (message?: string, options?: ErrorOptions): {
|
|
894
|
-
readonly code: ExpoSdkErrorCode;
|
|
895
|
-
name: string;
|
|
896
|
-
message: string;
|
|
897
|
-
stack?: string;
|
|
898
|
-
cause?: unknown;
|
|
899
|
-
};
|
|
900
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
901
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
902
|
-
stackTraceLimit: number;
|
|
903
|
-
};
|
|
904
|
-
declare class ConsentRequiredError extends ConsentRequiredError_base {
|
|
905
|
-
}
|
|
906
|
-
declare const SdkDisabledError_base: {
|
|
907
|
-
new (message?: string, options?: ErrorOptions): {
|
|
908
|
-
readonly code: ExpoSdkErrorCode;
|
|
909
|
-
name: string;
|
|
910
|
-
message: string;
|
|
911
|
-
stack?: string;
|
|
912
|
-
cause?: unknown;
|
|
913
|
-
};
|
|
914
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
915
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
916
|
-
stackTraceLimit: number;
|
|
917
|
-
};
|
|
918
|
-
declare class SdkDisabledError extends SdkDisabledError_base {
|
|
919
|
-
}
|
|
920
|
-
declare const ExpoSdkConfigurationError_base: {
|
|
921
|
-
new (message?: string, options?: ErrorOptions): {
|
|
922
|
-
readonly code: ExpoSdkErrorCode;
|
|
923
|
-
name: string;
|
|
924
|
-
message: string;
|
|
925
|
-
stack?: string;
|
|
926
|
-
cause?: unknown;
|
|
927
|
-
};
|
|
928
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
929
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
930
|
-
stackTraceLimit: number;
|
|
931
|
-
};
|
|
932
|
-
declare class ExpoSdkConfigurationError extends ExpoSdkConfigurationError_base {
|
|
933
|
-
}
|
|
934
|
-
declare const InvalidResponseError_base: {
|
|
935
|
-
new (message?: string, options?: ErrorOptions): {
|
|
936
|
-
readonly code: ExpoSdkErrorCode;
|
|
937
|
-
name: string;
|
|
938
|
-
message: string;
|
|
939
|
-
stack?: string;
|
|
940
|
-
cause?: unknown;
|
|
941
|
-
};
|
|
942
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
943
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
944
|
-
stackTraceLimit: number;
|
|
945
|
-
};
|
|
946
|
-
declare class InvalidResponseError extends InvalidResponseError_base {
|
|
947
|
-
}
|
|
948
|
-
declare const ExpoSdkTimeoutError_base: {
|
|
949
|
-
new (message?: string, options?: ErrorOptions): {
|
|
950
|
-
readonly code: ExpoSdkErrorCode;
|
|
951
|
-
name: string;
|
|
952
|
-
message: string;
|
|
953
|
-
stack?: string;
|
|
954
|
-
cause?: unknown;
|
|
955
|
-
};
|
|
956
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
957
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
958
|
-
stackTraceLimit: number;
|
|
959
|
-
};
|
|
960
|
-
declare class ExpoSdkTimeoutError extends ExpoSdkTimeoutError_base {
|
|
961
|
-
}
|
|
962
|
-
declare const UnknownSdkError_base: {
|
|
963
|
-
new (message?: string, options?: ErrorOptions): {
|
|
964
|
-
readonly code: ExpoSdkErrorCode;
|
|
965
|
-
name: string;
|
|
966
|
-
message: string;
|
|
967
|
-
stack?: string;
|
|
968
|
-
cause?: unknown;
|
|
969
|
-
};
|
|
970
|
-
captureStackTrace(targetObject: object, constructorOpt?: Function): void;
|
|
971
|
-
prepareStackTrace(err: Error, stackTraces: NodeJS.CallSite[]): any;
|
|
972
|
-
stackTraceLimit: number;
|
|
973
|
-
};
|
|
974
|
-
declare class UnknownSdkError extends UnknownSdkError_base {
|
|
975
|
-
}
|
|
976
|
-
declare function errorFromCode(code: string, message?: string): PickleballExpoError;
|
|
977
|
-
declare function normalizeError(error: unknown): PickleballExpoError;
|
|
978
|
-
|
|
979
|
-
interface RoomConnection {
|
|
980
|
-
serverUrl: string;
|
|
981
|
-
participantToken: string;
|
|
982
|
-
microphoneEnabled: boolean;
|
|
983
|
-
/** false: vào phòng nhưng chưa publish track (trạng thái standby). */
|
|
984
|
-
publish?: boolean;
|
|
985
|
-
}
|
|
986
|
-
type RoomEvent = {
|
|
987
|
-
type: "reconnecting";
|
|
988
|
-
} | {
|
|
989
|
-
type: "reconnected";
|
|
990
|
-
} | {
|
|
991
|
-
type: "disconnected";
|
|
992
|
-
reason?: string;
|
|
993
|
-
} | {
|
|
994
|
-
type: "media-error";
|
|
995
|
-
error: unknown;
|
|
996
|
-
}
|
|
997
|
-
/** Lệnh điều khiển từ dashboard qua LiveKit data channel ({"type":"cmd"}). */
|
|
998
|
-
| {
|
|
999
|
-
type: "command";
|
|
1000
|
-
action: string;
|
|
1001
|
-
};
|
|
1002
|
-
interface RoomAdapter {
|
|
1003
|
-
/** Bật/tắt publish track khi đã ở trong phòng (standby ↔ live). */
|
|
1004
|
-
setPublishing?(enabled: boolean): Promise<void>;
|
|
1005
|
-
setCaptureOrientation?(orientation: CaptureOrientation): Promise<void>;
|
|
1006
|
-
listCameras?(): Promise<CameraDeviceInfo[]>;
|
|
1007
|
-
selectCamera?(deviceId: string, facing: CameraDeviceInfo["facing"]): Promise<void>;
|
|
1008
|
-
preparePreview(quality: 720 | 1080): Promise<unknown>;
|
|
1009
|
-
connect(input: RoomConnection): Promise<void>;
|
|
1010
|
-
disconnect(stopTracks?: boolean): Promise<void>;
|
|
1011
|
-
switchCamera(): Promise<void>;
|
|
1012
|
-
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
1013
|
-
subscribe(listener: (event: RoomEvent) => void): () => void;
|
|
1014
|
-
dispose(): Promise<void>;
|
|
1015
|
-
}
|
|
1016
|
-
interface SdkRuntime {
|
|
1017
|
-
assertSupported(): void;
|
|
1018
|
-
requestPermissions(): Promise<boolean>;
|
|
1019
|
-
activateKeepAwake(): void | Promise<void>;
|
|
1020
|
-
deactivateKeepAwake(): void | Promise<void>;
|
|
1021
|
-
}
|
|
1022
|
-
interface SdkStorage {
|
|
1023
|
-
getItem(key: string): Promise<string | null>;
|
|
1024
|
-
setItem(key: string, value: string): Promise<void>;
|
|
1025
|
-
removeItem(key: string): Promise<void>;
|
|
1026
|
-
}
|
|
1027
|
-
interface SdkNetwork {
|
|
1028
|
-
isOnline(): boolean;
|
|
1029
|
-
subscribe(listener: (online: boolean) => void): () => void;
|
|
1030
|
-
}
|
|
1031
|
-
interface TelemetrySink {
|
|
1032
|
-
send(credentials: SdkTelemetryCredentials, events: readonly SdkTelemetryEvent[]): Promise<void>;
|
|
1033
|
-
dispose?(): void;
|
|
1034
|
-
}
|
|
1035
|
-
interface EngineDependencies {
|
|
1036
|
-
sessionProvider: LivestreamSessionProvider;
|
|
1037
|
-
bootstrapInput: BootstrapInput;
|
|
1038
|
-
roomFactory: () => RoomAdapter;
|
|
1039
|
-
runtime: SdkRuntime;
|
|
1040
|
-
storage: SdkStorage;
|
|
1041
|
-
network: SdkNetwork;
|
|
1042
|
-
telemetry?: TelemetrySink;
|
|
1043
|
-
/** Thu chỉ số thiết bị; gọi mỗi nhịp heartbeat khi đang phát. */
|
|
1044
|
-
deviceMetrics?: () => Promise<DeviceMetricsSample | null>;
|
|
1045
|
-
now?: () => number;
|
|
1046
|
-
}
|
|
1047
|
-
type CaptureOrientation = "portrait" | "landscape";
|
|
1048
|
-
interface CameraDeviceInfo {
|
|
1049
|
-
deviceId: string;
|
|
1050
|
-
label: string;
|
|
1051
|
-
facing: "front" | "environment" | "unknown";
|
|
1052
|
-
}
|
|
1053
|
-
/** Chỉ số thiết bị gửi kèm heartbeat (trường trùng schema deviceMetrics). */
|
|
1054
|
-
interface DeviceMetricsSample {
|
|
1055
|
-
batteryPct?: number;
|
|
1056
|
-
batteryTempC?: number;
|
|
1057
|
-
thermal?: number;
|
|
1058
|
-
ramFreeMb?: number;
|
|
1059
|
-
ramTotalMb?: number;
|
|
1060
|
-
storageFreeGb?: number;
|
|
1061
|
-
netType?: string;
|
|
1062
|
-
linkMbps?: number;
|
|
1063
|
-
rssi?: number;
|
|
1064
|
-
connQuality?: string;
|
|
1065
|
-
charging?: boolean;
|
|
1066
|
-
}
|
|
1067
|
-
interface LivestreamSnapshot {
|
|
1068
|
-
state: LivestreamState;
|
|
1069
|
-
error: PickleballExpoError | null;
|
|
1070
|
-
config: SdkRemoteConfig | null;
|
|
1071
|
-
sessionId: string | null;
|
|
1072
|
-
previewTrack: unknown;
|
|
1073
|
-
isMicrophoneEnabled: boolean;
|
|
1074
|
-
isCameraFront: boolean;
|
|
1075
|
-
/** Explicit pre-live choice; null follows the window orientation. */
|
|
1076
|
-
captureOrientation: CaptureOrientation | null;
|
|
1077
|
-
/** Mẫu chỉ số thiết bị mới nhất (cập nhật mỗi heartbeat khi live). */
|
|
1078
|
-
deviceMetrics: DeviceMetricsSample | null;
|
|
1079
|
-
/** Cameras reported by the platform after preview was prepared. */
|
|
1080
|
-
cameras: CameraDeviceInfo[];
|
|
1081
|
-
/** Explicitly chosen camera; null when following facing (front/back). */
|
|
1082
|
-
selectedCameraId: string | null;
|
|
1083
|
-
elapsedMs: number;
|
|
1084
|
-
isOnline: boolean;
|
|
1085
|
-
}
|
|
1086
|
-
declare const PENDING_END_KEY = "@pickleball/expo-sdk/pending-end";
|
|
1087
|
-
declare const LEGAL_LIVESTREAM_TRANSITIONS: Readonly<Record<LivestreamState, readonly LivestreamState[]>>;
|
|
1088
|
-
declare class LivestreamEngine {
|
|
1089
|
-
private readonly dependencies;
|
|
1090
|
-
private room?;
|
|
1091
|
-
private readonly now;
|
|
1092
|
-
private snapshot;
|
|
1093
|
-
private listeners;
|
|
1094
|
-
private preparePromise?;
|
|
1095
|
-
private startPromise?;
|
|
1096
|
-
private stopPromise?;
|
|
1097
|
-
private retryPromise?;
|
|
1098
|
-
private credentialRefreshPromise?;
|
|
1099
|
-
private pendingReplayPromise?;
|
|
1100
|
-
private startupRecoveryPromise?;
|
|
1101
|
-
private startupRecovered;
|
|
1102
|
-
private pendingReplayError?;
|
|
1103
|
-
private ownedSessionId?;
|
|
1104
|
-
private commandTail;
|
|
1105
|
-
private commandCount;
|
|
1106
|
-
private roomUnsubscribe;
|
|
1107
|
-
private networkUnsubscribe;
|
|
1108
|
-
private networkSubscribed;
|
|
1109
|
-
private latestAppState;
|
|
1110
|
-
private backgroundTimer?;
|
|
1111
|
-
private standbyTimer?;
|
|
1112
|
-
private reconnectTimer?;
|
|
1113
|
-
private heartbeatTimer?;
|
|
1114
|
-
private elapsedTimer?;
|
|
1115
|
-
private tokenRefreshTimer?;
|
|
1116
|
-
private maxSessionTimer?;
|
|
1117
|
-
private grant?;
|
|
1118
|
-
private startedAt?;
|
|
1119
|
-
/** Đang publish track (live) hay chỉ ngồi trong phòng (standby). */
|
|
1120
|
-
private publishing;
|
|
1121
|
-
/** Tổng thời gian đã phát trước lần tạm dừng gần nhất. */
|
|
1122
|
-
private pausedElapsedMs;
|
|
1123
|
-
private sessionDeadline?;
|
|
1124
|
-
private refreshRetryAttempt;
|
|
1125
|
-
private operationGeneration;
|
|
1126
|
-
private liveTimersStarted;
|
|
1127
|
-
private keepAwakeActive;
|
|
1128
|
-
private telemetryInFlight?;
|
|
1129
|
-
private pendingCriticalTelemetry?;
|
|
1130
|
-
private readonly sessionTerminations;
|
|
1131
|
-
private disposePromise?;
|
|
1132
|
-
private disposed;
|
|
1133
|
-
constructor(dependencies: EngineDependencies);
|
|
1134
|
-
getSnapshot: () => LivestreamSnapshot;
|
|
1135
|
-
subscribe: (listener: () => void) => () => boolean;
|
|
1136
|
-
initialize(): Promise<void>;
|
|
1137
|
-
prepare(): Promise<void>;
|
|
1138
|
-
private doPrepare;
|
|
1139
|
-
start(input: StartLivestreamInput, options?: {
|
|
1140
|
-
autoPublish?: boolean;
|
|
1141
|
-
}): Promise<void>;
|
|
1142
|
-
private doStart;
|
|
1143
|
-
stop(reason?: EndReason): Promise<void>;
|
|
1144
|
-
private doStop;
|
|
1145
|
-
retry(): Promise<void>;
|
|
1146
|
-
private doRetry;
|
|
1147
|
-
switchCamera(): Promise<void>;
|
|
1148
|
-
/** Chọn một ống kính cụ thể từ snapshot.cameras. */
|
|
1149
|
-
selectCamera(deviceId: string): Promise<void>;
|
|
1150
|
-
/** Chọn kiểu quay trước khi live; không đổi khi phiên đang hoạt động. */
|
|
1151
|
-
setCaptureOrientation(orientation: CaptureOrientation): Promise<void>;
|
|
1152
|
-
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
1153
|
-
handleRoomEvent(event: RoomEvent): void;
|
|
1154
|
-
handleAppState(state: "active" | "background" | "inactive"): void;
|
|
1155
|
-
private ensureBackgroundGraceTimer;
|
|
1156
|
-
dispose(): Promise<void>;
|
|
1157
|
-
private replayPendingEnd;
|
|
1158
|
-
private doReplayPendingEnd;
|
|
1159
|
-
private recoverStartupSession;
|
|
1160
|
-
private ensurePendingEndResolved;
|
|
1161
|
-
private readPendingEnd;
|
|
1162
|
-
private ownsInMemorySession;
|
|
1163
|
-
private startTimers;
|
|
1164
|
-
private clearTimers;
|
|
1165
|
-
private clearReconnectTimer;
|
|
1166
|
-
/** Heartbeat kèm chỉ số thiết bị (nếu collector được cung cấp). */
|
|
1167
|
-
private sendHeartbeat;
|
|
1168
|
-
private sendTelemetry;
|
|
1169
|
-
private startTelemetrySend;
|
|
1170
|
-
private refreshCredentials;
|
|
1171
|
-
private transition;
|
|
1172
|
-
private startStandbyTimer;
|
|
1173
|
-
private clearStandbyTimer;
|
|
1174
|
-
private fail;
|
|
1175
|
-
private recordError;
|
|
1176
|
-
private update;
|
|
1177
|
-
private ensureState;
|
|
1178
|
-
private ensureNotDisposed;
|
|
1179
|
-
private ensureRoom;
|
|
1180
|
-
private ensureNetworkSubscription;
|
|
1181
|
-
private requireRoom;
|
|
1182
|
-
private releaseRoom;
|
|
1183
|
-
private enqueueLifecycle;
|
|
1184
|
-
private scheduleTokenRefresh;
|
|
1185
|
-
private scheduleMaxSessionDuration;
|
|
1186
|
-
private restartHeartbeat;
|
|
1187
|
-
private enterSession;
|
|
1188
|
-
/** Bắt đầu phát khi đang standby (từ nút trên app hoặc lệnh dashboard). */
|
|
1189
|
-
startPublishing(): Promise<void>;
|
|
1190
|
-
/** 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. */
|
|
1191
|
-
stopPublishing(): Promise<void>;
|
|
1192
|
-
private ensureSessionTermination;
|
|
1193
|
-
private abandonGrant;
|
|
1194
|
-
private isOperationCurrent;
|
|
1195
|
-
private ensureOperation;
|
|
1196
|
-
}
|
|
1197
|
-
|
|
1198
|
-
type Fetch = typeof globalThis.fetch;
|
|
1199
9
|
type HeadersInput = Record<string, string>;
|
|
1200
10
|
interface HttpSessionProviderPaths {
|
|
1201
11
|
bootstrap: string;
|
|
@@ -1206,7 +16,11 @@ interface HttpSessionProviderPaths {
|
|
|
1206
16
|
unpublish(sessionId: string): string;
|
|
1207
17
|
}
|
|
1208
18
|
interface HttpSessionProviderOptions {
|
|
1209
|
-
/**
|
|
19
|
+
/**
|
|
20
|
+
* Base URL của proxy do đối tác host (mô hình cũ) hoặc namespace first-party
|
|
21
|
+
* `/api/v2/device-app` (app fleet). Không bao giờ là URL PickLive kèm API key.
|
|
22
|
+
* App đối tác dùng camera grant thì dùng createCameraGrantProvider thay vì đây.
|
|
23
|
+
*/
|
|
1210
24
|
baseUrl: string;
|
|
1211
25
|
fetch?: Fetch;
|
|
1212
26
|
headers?: HeadersInput | (() => HeadersInput | Promise<HeadersInput>);
|
|
@@ -1215,222 +29,307 @@ interface HttpSessionProviderOptions {
|
|
|
1215
29
|
}
|
|
1216
30
|
declare function createHttpSessionProvider(options: HttpSessionProviderOptions): LivestreamSessionProvider;
|
|
1217
31
|
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
deviceId?: string;
|
|
1242
|
-
width?: number;
|
|
1243
|
-
height?: number;
|
|
1244
|
-
frameRate?: number;
|
|
1245
|
-
}): Promise<void>;
|
|
1246
|
-
/** react-native-webrtc native camera flip; blind front/back toggle. */
|
|
1247
|
-
_switchCamera?(): void;
|
|
1248
|
-
};
|
|
1249
|
-
}
|
|
1250
|
-
interface RoomLike {
|
|
1251
|
-
state: string;
|
|
1252
|
-
localParticipant: {
|
|
1253
|
-
publishTrack(track: LocalTrackLike, options?: unknown): Promise<unknown>;
|
|
1254
|
-
unpublishTrack?(track: LocalTrackLike, stopOnUnpublish?: boolean): Promise<unknown> | unknown;
|
|
1255
|
-
};
|
|
1256
|
-
connect(serverUrl: string, token: string, options?: unknown): Promise<void>;
|
|
1257
|
-
disconnect(stopTracks?: boolean): Promise<void>;
|
|
1258
|
-
on(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
1259
|
-
off(event: string, listener: (...args: unknown[]) => void): unknown;
|
|
1260
|
-
}
|
|
1261
|
-
interface NativeLiveKitBindings {
|
|
1262
|
-
registerGlobals(): void;
|
|
1263
|
-
createRoom(): RoomLike;
|
|
1264
|
-
createLocalTracks(options: {
|
|
1265
|
-
audio: true;
|
|
1266
|
-
video: {
|
|
1267
|
-
facingMode?: "user" | "environment";
|
|
1268
|
-
deviceId?: string;
|
|
1269
|
-
resolution: {
|
|
1270
|
-
width: number;
|
|
1271
|
-
height: number;
|
|
1272
|
-
frameRate: number;
|
|
1273
|
-
};
|
|
1274
|
-
};
|
|
1275
|
-
}): Promise<LocalTrackLike[]>;
|
|
1276
|
-
startAudioSession(): Promise<void> | void;
|
|
1277
|
-
stopAudioSession(): Promise<void> | void;
|
|
1278
|
-
/** Current window orientation; capture dimensions follow it. */
|
|
1279
|
-
isLandscape?(): boolean;
|
|
1280
|
-
/** Physical/virtual cameras reported by the platform. */
|
|
1281
|
-
listCameras?(): Promise<CameraDeviceInfo[]>;
|
|
1282
|
-
cameraSource?: unknown;
|
|
1283
|
-
events: {
|
|
1284
|
-
reconnecting: string;
|
|
1285
|
-
reconnected: string;
|
|
1286
|
-
disconnected: string;
|
|
1287
|
-
mediaDevicesError: string;
|
|
1288
|
-
/** LiveKit RoomEvent.DataReceived — nhận lệnh điều khiển từ dashboard. */
|
|
1289
|
-
dataReceived?: string;
|
|
1290
|
-
};
|
|
32
|
+
/**
|
|
33
|
+
* Nhận lại phiên sau khi app bị kill/mở lại — phần PROVIDER.
|
|
34
|
+
*
|
|
35
|
+
* Kịch bản: máy đang phát, app biến mất (sụp nguồn, OEM giết, vuốt tắt). Server
|
|
36
|
+
* còn giữ phiên. Lần mở sau engine thấy dấu trên đĩa (adoptableSessionId) nhưng
|
|
37
|
+
* KHÔNG có grant trong bộ nhớ — phải hỏi server cấp lại credentials của đúng
|
|
38
|
+
* phiên đó rồi start bằng grant ấy thay vì tạo phiên mới. Link xem và VOD nhờ
|
|
39
|
+
* vậy liền một mạch.
|
|
40
|
+
*/
|
|
41
|
+
type ResumeAttempt = {
|
|
42
|
+
kind: "grant";
|
|
43
|
+
grant: SdkSessionGrant;
|
|
44
|
+
live: boolean;
|
|
45
|
+
} | {
|
|
46
|
+
kind: "gone";
|
|
47
|
+
} | {
|
|
48
|
+
kind: "retry";
|
|
49
|
+
};
|
|
50
|
+
interface ResumableSessionProvider extends LivestreamSessionProvider {
|
|
51
|
+
/** Đặt grant cho lượt start kế tiếp có externalSessionId `resume-<id>`. */
|
|
52
|
+
adoptResumeGrant(grant: SdkSessionGrant): void;
|
|
53
|
+
/** Hỏi server cấp lại grant cho phiên còn sống. */
|
|
54
|
+
resume(sessionId: string): Promise<ResumeAttempt>;
|
|
1291
55
|
}
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
56
|
+
/**
|
|
57
|
+
* Bọc provider: `start()` ưu tiên grant nhận lại nếu khớp phiên, còn lại tạo phiên
|
|
58
|
+
* mới như thường. Grant chờ nằm trong closure (không biến module) nên hai provider
|
|
59
|
+
* trong cùng app không dẫm nhau.
|
|
60
|
+
*/
|
|
61
|
+
declare function withResume(provider: LivestreamSessionProvider, resume: (sessionId: string) => Promise<ResumeAttempt>): ResumableSessionProvider;
|
|
62
|
+
/** Biến phản hồi refresh (đầy đủ) thành grant; méo → null để caller coi là retry. */
|
|
63
|
+
declare function grantFromRefreshPayload(data: unknown): SdkSessionGrant | null;
|
|
64
|
+
interface HttpResumeFetcherOptions {
|
|
65
|
+
/** URL endpoint refresh của phiên — namespace nào cũng được, miễn trả grant đầy đủ + status. */
|
|
66
|
+
url(sessionId: string): string;
|
|
67
|
+
headers?: Record<string, string> | (() => Record<string, string> | Promise<Record<string, string>>);
|
|
68
|
+
fetch?: Fetch;
|
|
69
|
+
timeoutMs?: number;
|
|
1299
70
|
}
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
71
|
+
/**
|
|
72
|
+
* Gọi endpoint refresh để dựng lại grant. Trả về PHÂN LOẠI chứ không phải
|
|
73
|
+
* null-cho-mọi-thứ (xem resume-policy): mất mạng/5xx/méo = retry, 404/409/410 =
|
|
74
|
+
* gone. Chỉ phát tiếp khi server xác nhận phiên ĐANG live — thiếu trường status
|
|
75
|
+
* coi như không phát: để camera tự lên sóng nhầm còn tệ hơn là chờ một lệnh nữa.
|
|
76
|
+
*/
|
|
77
|
+
declare function createHttpResumeFetcher(options: HttpResumeFetcherOptions): (sessionId: string) => Promise<ResumeAttempt>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Session provider cho ĐIỆN THOẠI ĐỐI TÁC (camera grant, ADR-0002).
|
|
81
|
+
*
|
|
82
|
+
* App đối tác ──(auth riêng)──► Backend đối tác ──khoá API (chỉ server)──► PickLive REST v1
|
|
83
|
+
* App đối tác ◄───────── CameraGrant nguyên trạng ◄──────────
|
|
84
|
+
* SDK ──Bearer grant.telemetry.token──► PickLive /api/v2/camera/sessions/{id}/*
|
|
85
|
+
*
|
|
86
|
+
* App KHÔNG cầm API key, KHÔNG cấu hình URL PickLive: mọi endpoint suy từ
|
|
87
|
+
* `telemetry.endpoint` trong grant. Backend đối tác chỉ có đúng một việc — cấp grant.
|
|
88
|
+
*
|
|
89
|
+
* Handle phiên `{sessionId, base, token}` được LƯU trong sandbox app (expo-sqlite
|
|
90
|
+
* localStorage) duy nhất để nhận lại phiên sau khi app bị kill; token khoá theo
|
|
91
|
+
* một phiên và tự hết hạn. Đây là thay đổi so với lời hứa "token chỉ ở bộ nhớ"
|
|
92
|
+
* của mô hình proxy cũ — chủ ý, để resume hoạt động.
|
|
93
|
+
*/
|
|
94
|
+
declare const CAMERA_GRANT_STORAGE_KEY = "@pickleball/expo-sdk/camera-grant";
|
|
95
|
+
/** Config local cho bước prepare (engine chỉ dùng enabled/minSdkVersion/videoQuality ở đây). */
|
|
96
|
+
declare const DEFAULT_LOCAL_CONFIG: SdkRemoteConfig;
|
|
97
|
+
type CameraGrantPhase = "start" | "refresh" | "resume";
|
|
98
|
+
interface CameraGrantProviderOptions {
|
|
1323
99
|
/**
|
|
1324
|
-
*
|
|
1325
|
-
*
|
|
1326
|
-
*
|
|
1327
|
-
* width/height/frameRate. Sau mỗi restartTrack phải nắn lại format trên
|
|
1328
|
-
* track mới bằng applyConstraints.
|
|
100
|
+
* App đối tác gọi backend đối tác → backend gọi PickLive → trả grant nguyên
|
|
101
|
+
* trạng. Nhận StartLivestreamInput của engine (kèm `standby`, `quality`) để
|
|
102
|
+
* backend chuyển tiếp title/quality nếu muốn.
|
|
1329
103
|
*/
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
private ensurePreparedTracks;
|
|
1339
|
-
private emit;
|
|
1340
|
-
private ensureNotDisposed;
|
|
104
|
+
requestGrant(input: StartLivestreamInput): Promise<unknown>;
|
|
105
|
+
storage?: SdkStorage;
|
|
106
|
+
storageKey?: string;
|
|
107
|
+
fetch?: Fetch;
|
|
108
|
+
timeoutMs?: number;
|
|
109
|
+
/** Ghi đè config local của bước prepare (VD videoQuality theo cài đặt máy). */
|
|
110
|
+
localConfig?: Partial<SdkRemoteConfig>;
|
|
111
|
+
onGrant?(grant: CameraGrant, phase: CameraGrantPhase): void;
|
|
1341
112
|
}
|
|
113
|
+
interface CameraGrantHandle {
|
|
114
|
+
sessionId: string;
|
|
115
|
+
base: string;
|
|
116
|
+
token: string;
|
|
117
|
+
tokenExpiresAt: number;
|
|
118
|
+
quality?: 720 | 1080;
|
|
119
|
+
savedAt: number;
|
|
120
|
+
}
|
|
121
|
+
interface CameraGrantProvider extends ResumableSessionProvider {
|
|
122
|
+
/** Grant đang cầm (null khi chưa có phiên). */
|
|
123
|
+
getActiveGrant(): CameraGrant | null;
|
|
124
|
+
subscribe(listener: () => void): () => void;
|
|
125
|
+
/** Có handle phiên trên đĩa → truyền vào `keepSessionOnRestart` của provider. */
|
|
126
|
+
hasResumableSession(): Promise<boolean>;
|
|
127
|
+
/** Quên phiên (logout/đổi user). Backend đối tác nên gọi end phiên đó. */
|
|
128
|
+
clearStoredSession(): Promise<void>;
|
|
129
|
+
}
|
|
130
|
+
declare function createCameraGrantProvider(options: CameraGrantProviderOptions): CameraGrantProvider;
|
|
131
|
+
/** Grant hiện hành của provider, theo dõi bằng useSyncExternalStore. */
|
|
132
|
+
declare function useCameraGrant(provider: CameraGrantProvider): CameraGrant | null;
|
|
1342
133
|
|
|
1343
|
-
declare function createNativeLiveKitBindings(): NativeLiveKitBindings;
|
|
1344
|
-
declare function createDefaultRuntime(): SdkRuntime;
|
|
1345
|
-
declare function createDefaultStorage(): SdkStorage;
|
|
1346
|
-
declare function createDefaultNetwork(): SdkNetwork;
|
|
1347
|
-
declare function createDefaultTelemetry(fetchImplementation?: typeof fetch, timeoutMs?: number): TelemetrySink;
|
|
1348
|
-
declare function createDefaultRoomFactory(): EngineDependencies["roomFactory"];
|
|
1349
134
|
/**
|
|
1350
|
-
*
|
|
1351
|
-
*
|
|
135
|
+
* Luật quyết định khi nối lại một phiên server còn giữ cho máy này.
|
|
136
|
+
*
|
|
137
|
+
* Chuyển từ apps/device-streamer (src/resume-policy.ts) vào SDK để app đối tác
|
|
138
|
+
* không phải học lại từng cái bẫy đã đo được trên fleet.
|
|
139
|
+
*
|
|
140
|
+
* Vì sao cần tách riêng: bản đầu coi MỌI thất bại là "phiên đã chết" và bỏ cuộc
|
|
141
|
+
* vĩnh viễn sau đúng một lần thử. Hệ quả: máy khởi động lại lúc wifi chưa kịp
|
|
142
|
+
* lên là mất luôn cơ hội nối lại — đúng ca hay gặp nhất, vì máy vừa mở nguồn
|
|
143
|
+
* thì wifi luôn lên chậm hơn app.
|
|
144
|
+
*
|
|
145
|
+
* Phân biệt hai loại thất bại là toàn bộ giá trị của module này:
|
|
146
|
+
* - "gone": server nói phiên không còn. Thử lại bao nhiêu cũng vô ích.
|
|
147
|
+
* - "retry": mạng chưa lên, server trục trặc, phản hồi méo. Phiên có thể vẫn sống.
|
|
148
|
+
*
|
|
149
|
+
* Nghi ngờ thì chọn "retry": đoán nhầm thành "gone" là mất phiên vĩnh viễn (link
|
|
150
|
+
* xem đổi, VOD tách đôi); đoán nhầm thành "retry" chỉ tốn vài request, và đã có
|
|
151
|
+
* lưới server dọn phiên mồ côi.
|
|
1352
152
|
*/
|
|
1353
|
-
|
|
153
|
+
/** Phân loại phản hồi HTTP của endpoint refresh. */
|
|
154
|
+
type ResumeVerdict = "ok" | "gone" | "retry";
|
|
155
|
+
/**
|
|
156
|
+
* 404 = phiên biến mất hẳn. 409 = phiên đã kết thúc (mọi namespace trả
|
|
157
|
+
* SESSION_CONFLICT cho thao tác trên phiên ended, và grant bị thay cũng là 409).
|
|
158
|
+
* 410 = Gone theo chuẩn HTTP. Mọi mã khác — kể cả 401/403 — đều là "retry":
|
|
159
|
+
* token có thể được cấp lại ở nhịp sau, và một lỗi xác thực tạm thời không phải
|
|
160
|
+
* bằng chứng phiên đã chết.
|
|
161
|
+
*/
|
|
162
|
+
declare function classifyResumeStatus(status: number): ResumeVerdict;
|
|
163
|
+
/**
|
|
164
|
+
* Nhịp thử lại. Nhịp đầu 1 giây vì ca điển hình là "app mở trước khi wifi lên" —
|
|
165
|
+
* chờ vài giây là xong. Trần 15 giây để một máy chết hẳn không nện server mãi;
|
|
166
|
+
* số lần thì KHÔNG giới hạn: thà thử mãi còn hơn mất phiên, và lưới server
|
|
167
|
+
* (1 giờ cho live, 30 phút cho standby) mới là thứ quyết định lúc nào bỏ cuộc.
|
|
168
|
+
*/
|
|
169
|
+
declare const RESUME_RETRY_DELAYS_MS: number[];
|
|
170
|
+
declare function resumeRetryDelayMs(attempt: number): number;
|
|
171
|
+
/**
|
|
172
|
+
* Bất kỳ ai định TẠO PHIÊN MỚI tự động (AutoTournament của fleet, useStandbySession
|
|
173
|
+
* của app đối tác) phải NHƯỜNG khi còn phiên chờ nhận lại — nhìn CẢ HAI nguồn.
|
|
174
|
+
*
|
|
175
|
+
* Bản đầu chỉ nhìn serverSession (heartbeat, về SAU vài giây); adoptableSessionId
|
|
176
|
+
* (dấu trên đĩa) có mặt ngay từ lúc engine khởi động xong. Khe hở vài giây đó là
|
|
177
|
+
* đủ để một lượt arm nổ súng trước AutoResume: hoặc đụng chốt marker (app đứng
|
|
178
|
+
* màn LỖI), hoặc tệ hơn — lách qua trong đúng cửa sổ race và tạo phiên chờ THỨ
|
|
179
|
+
* HAI đè phiên live cũ (đo thật đêm 18/08/2026).
|
|
180
|
+
*/
|
|
181
|
+
declare function resumeBlocksArm(input: {
|
|
182
|
+
adoptableSessionId: string | null;
|
|
183
|
+
serverSessionId: string | null;
|
|
184
|
+
engineSessionId: string | null;
|
|
185
|
+
}): boolean;
|
|
186
|
+
/** Quy ước externalSessionId của lượt start NHẬN LẠI — engine chỉ cho lượt này qua chốt marker. */
|
|
187
|
+
declare function resumeExternalSessionId(sessionId: string): string;
|
|
188
|
+
/**
|
|
189
|
+
* Grant nối lại chỉ được tiêu thụ cho ĐÚNG phiên nó được cấp. AutoResume có thể
|
|
190
|
+
* bắn nhiều lượt (retry, đổi nguồn); không kiểm khớp thì một grant cũ còn sót có
|
|
191
|
+
* thể bị lượt start của phiên khác nuốt — engine nối vào nhầm phiên, đúng loại lỗi
|
|
192
|
+
* âm thầm khó truy nhất.
|
|
193
|
+
*/
|
|
194
|
+
declare function resumeGrantMatches(externalSessionId: string | undefined, grantSessionId: string): boolean;
|
|
1354
195
|
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
196
|
+
/**
|
|
197
|
+
* Ba mảnh "tự động" chuyển từ apps/device-streamer vào SDK — mọi app RTMP cần
|
|
198
|
+
* chúng, không riêng máy fleet:
|
|
199
|
+
*
|
|
200
|
+
* - PickleballAutoPrepare: camera luôn bật (prepare khi idle/ended).
|
|
201
|
+
* - PickleballAutoResume: app mở lại thấy phiên còn sống → nhận lại đúng phiên.
|
|
202
|
+
* - useStandbySession: chế độ sân — arm một phiên chờ, server lật live bằng lệnh.
|
|
203
|
+
*
|
|
204
|
+
* Các bẫy đã đo trên fleet (18–19/08/2026) nằm nguyên trong đây: retry publish
|
|
205
|
+
* khi encoder chưa có SPS, thoát hố "error" khi còn phiên chờ nhận lại, và
|
|
206
|
+
* KHÔNG arm phiên mới khi còn phiên chờ nhận lại.
|
|
207
|
+
*
|
|
208
|
+
* Kỷ luật dependency: mọi effect ở đây chỉ phụ thuộc vào TRẠNG THÁI (state,
|
|
209
|
+
* sessionId, id phiên chờ, cờ enabled) — hành động của engine, props cấu hình
|
|
210
|
+
* (resumeInput, retryDelayMs, input) và callback đi qua ref. App đối tác thường
|
|
211
|
+
* truyền object/hàm inline mỗi lần render; đưa chúng vào deps là mỗi render
|
|
212
|
+
* huỷ lượt resume đang bay, bắn lượt mới ngay (spam refresh) và timer thử lại
|
|
213
|
+
* không bao giờ kịp nổ.
|
|
214
|
+
*/
|
|
215
|
+
type ResumeAttemptOutcome = ResumeAttempt["kind"] | "start_failed" | "publish_retry" | "reprepare";
|
|
216
|
+
interface PickleballAutoResumeProps {
|
|
217
|
+
provider: ResumableSessionProvider;
|
|
218
|
+
/** Thông tin dựng lượt start nhận lại (title mặc định khi không biết tên phiên). */
|
|
219
|
+
resumeInput: {
|
|
220
|
+
consentVersion: string;
|
|
221
|
+
visibility?: "public" | "private";
|
|
222
|
+
metadata?: Record<string, string>;
|
|
223
|
+
titleFor?: (sessionId: string) => string;
|
|
224
|
+
};
|
|
225
|
+
/** false = tạm dừng (VD máy đang nghỉ). */
|
|
226
|
+
enabled?: boolean;
|
|
227
|
+
/**
|
|
228
|
+
* Nguồn thứ hai của id phiên còn sống (fleet: serverSession từ heartbeat).
|
|
229
|
+
* adoptableSessionId (dấu trên đĩa) luôn được ưu tiên vì có sớm hơn vài giây.
|
|
230
|
+
* Truyền CÙNG giá trị này vào `useStandbySession({resumeSessionId})` nếu app
|
|
231
|
+
* arm phiên chờ, để arm nhường đường nhận lại.
|
|
232
|
+
*/
|
|
233
|
+
sessionIdHint?: string | null;
|
|
234
|
+
retryDelayMs?: (attempt: number) => number;
|
|
235
|
+
onAttempt?: (sessionId: string, outcome: ResumeAttemptOutcome) => void;
|
|
1391
236
|
}
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
autoPublish?: boolean;
|
|
1398
|
-
}): Promise<void>;
|
|
1399
|
-
/** Bắt đầu phát khi đang standby. */
|
|
1400
|
-
startPublishing(): Promise<void>;
|
|
1401
|
-
/** Tạm dừng phát về standby (không kết thúc phiên). */
|
|
1402
|
-
stopPublishing(): Promise<void>;
|
|
1403
|
-
stop(reason?: EndReason): Promise<void>;
|
|
1404
|
-
retry(): Promise<void>;
|
|
1405
|
-
switchCamera(): Promise<void>;
|
|
1406
|
-
setMicrophoneEnabled(enabled: boolean): Promise<void>;
|
|
1407
|
-
setCaptureOrientation(orientation: "portrait" | "landscape"): Promise<void>;
|
|
1408
|
-
selectCamera(deviceId: string): Promise<void>;
|
|
237
|
+
declare function PickleballAutoResume(props: PickleballAutoResumeProps): null;
|
|
238
|
+
interface PickleballAutoPrepareProps {
|
|
239
|
+
enabled?: boolean;
|
|
240
|
+
/** Khung cố định (fleet: landscape). Bỏ trống = theo hướng máy như engine mặc định. */
|
|
241
|
+
captureOrientation?: "portrait" | "landscape";
|
|
1409
242
|
}
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
243
|
+
/**
|
|
244
|
+
* Camera luôn sẵn: prepare khi idle và SAU KHI phiên kết thúc (thiếu nhánh
|
|
245
|
+
* `ended` là camera tối sau phiên đầu tiên). Không chạy khi đang lỗi — người
|
|
246
|
+
* dùng cần thấy lỗi, và AutoResume lo nhánh lỗi có phiên chờ.
|
|
247
|
+
*/
|
|
248
|
+
declare function PickleballAutoPrepare(props: PickleballAutoPrepareProps): null;
|
|
249
|
+
interface UseStandbySessionOptions {
|
|
250
|
+
/** Lượt start chờ; đổi `externalSessionId` (trận kế) mới arm lại sau khi phiên trước kết thúc. */
|
|
1415
251
|
input: StartLivestreamInput;
|
|
1416
|
-
|
|
252
|
+
enabled?: boolean;
|
|
1417
253
|
/**
|
|
1418
|
-
*
|
|
1419
|
-
*
|
|
1420
|
-
*
|
|
254
|
+
* Id phiên còn sống theo nguồn NGOÀI đĩa (fleet: heartbeat; app đối tác: id
|
|
255
|
+
* backend trả về) — cùng giá trị truyền vào `PickleballAutoResume.sessionIdHint`.
|
|
256
|
+
* Có nó là arm NHƯỜNG: không tạo phiên chờ thứ hai trong lúc AutoResume đang
|
|
257
|
+
* nối lại phiên cũ.
|
|
1421
258
|
*/
|
|
1422
|
-
|
|
1423
|
-
/**
|
|
1424
|
-
* Tự kết nối vào phòng khi mở màn hình nếu consent đã được lưu từ trước
|
|
1425
|
-
* (mặc định bật) — dashboard điều khiển được ngay từ lúc app bật.
|
|
1426
|
-
*/
|
|
1427
|
-
autoConnect?: boolean;
|
|
1428
|
-
renderPreview?: (state: RenderState) => ReactNode;
|
|
1429
|
-
renderHeader?: (state: RenderState) => ReactNode;
|
|
1430
|
-
renderControls?: (state: RenderState) => ReactNode;
|
|
1431
|
-
renderError?: (state: RenderState) => ReactNode;
|
|
1432
|
-
onEnded?: () => void;
|
|
259
|
+
resumeSessionId?: string | null;
|
|
1433
260
|
}
|
|
1434
|
-
|
|
261
|
+
/**
|
|
262
|
+
* Chế độ SÂN: điện thoại đứng chờ (standby) với grant đã cấp; trọng tài bấm
|
|
263
|
+
* match-start là server gửi lệnh `start` qua kênh lệnh và engine tự phát.
|
|
264
|
+
* Arm đúng một lần cho mỗi externalSessionId, và NHƯỜNG khi còn phiên chờ nhận
|
|
265
|
+
* lại (resumeBlocksArm) để không tạo phiên chờ thứ hai đè phiên cũ.
|
|
266
|
+
*/
|
|
267
|
+
declare function useStandbySession(options: UseStandbySessionOptions): {
|
|
268
|
+
armed: boolean;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Độ phân giải là lựa chọn LOCAL của máy — override `videoQuality` mà server trả
|
|
273
|
+
* trong bootstrap/start/refresh (engine truyền config.videoQuality vào
|
|
274
|
+
* room.preparePreview nên không cần sửa engine).
|
|
275
|
+
*
|
|
276
|
+
* Lúc TẠO phiên phải khai ngược lên server (`quality` trong StartLivestreamInput),
|
|
277
|
+
* nếu không bản ghi phiên nói dối: mặc định local 720 còn server 1080 — dashboard
|
|
278
|
+
* báo 1080 trong khi máy phát 720 suốt (18/08/2026). Chốt MỘT lần lúc start:
|
|
279
|
+
* gọi `resolution()` hai lần có thể ra hai giá trị khác nhau, khai một đằng dùng
|
|
280
|
+
* một nẻo thì tệ hơn là không khai.
|
|
281
|
+
*/
|
|
282
|
+
declare function withLocalVideoQuality<P extends LivestreamSessionProvider>(provider: P, resolution: () => 720 | 1080): P;
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Đo tốc độ TẢI LÊN — chiều duy nhất quan trọng với máy phát RTMP (root cause
|
|
286
|
+
* các ca đứng hình field day là uplink hụt). Chuyển từ apps/device-streamer/
|
|
287
|
+
* src/net-check.ts; bảng bitrate giờ là nguồn duy nhất (adapter RTMP import lại).
|
|
288
|
+
*
|
|
289
|
+
* Đích đo mặc định speed.cloudflare.com/__up (edge gần VN/SG): đo "sức uplink
|
|
290
|
+
* của đường mạng", không phải đường tới đúng VPS ingest — nghẽn thực tế nằm ở
|
|
291
|
+
* last-mile WiFi/4G nên xấp xỉ tốt, và không phải mở endpoint nào trên hạ tầng.
|
|
292
|
+
*/
|
|
293
|
+
/**
|
|
294
|
+
* Trần bitrate video theo mức chất lượng — trong khoảng TikTok khuyến nghị cho
|
|
295
|
+
* live (720p 3–6 Mbps, 1080p 6–9 Mbps). Thấp hơn là pickleball bể khối rất rõ.
|
|
296
|
+
*/
|
|
297
|
+
declare const VIDEO_BITRATE_BPS: Readonly<Record<720 | 1080, number>>;
|
|
298
|
+
declare const AUDIO_BITRATE_BPS = 128000;
|
|
299
|
+
interface UplinkCheckOptions {
|
|
300
|
+
resolution: 720 | 1080;
|
|
301
|
+
/** Đích POST khối dữ liệu; mặc định Cloudflare speed test. */
|
|
302
|
+
uploadUrl?: string;
|
|
303
|
+
/** HEAD để đo RTT (VD ingest của bạn). Bỏ trống = không đo ping. */
|
|
304
|
+
pingUrl?: string;
|
|
305
|
+
fetch?: typeof globalThis.fetch;
|
|
306
|
+
/** Đo tối thiểu chừng này thời gian sau warmup (mặc định 6s). */
|
|
307
|
+
measureMs?: number;
|
|
308
|
+
/** Trần cho cả bài đo (mặc định 25s). */
|
|
309
|
+
totalTimeoutMs?: number;
|
|
310
|
+
/** Cỡ mỗi khối POST (mặc định 2 MiB). */
|
|
311
|
+
chunkBytes?: number;
|
|
312
|
+
/** Trần cho khối warmup (mặc định 10s) — quá là mạng "low", không phải lỗi đo. */
|
|
313
|
+
warmupTimeoutMs?: number;
|
|
314
|
+
}
|
|
315
|
+
interface UplinkCheckResult {
|
|
316
|
+
uploadMbps: number;
|
|
317
|
+
/** RTT tới pingUrl, null nếu không đo/đo hỏng. */
|
|
318
|
+
pingMs: number | null;
|
|
319
|
+
/** Băng thông cần cho cấu hình (video trần + audio), Mbps. */
|
|
320
|
+
requiredMbps: number;
|
|
321
|
+
verdict: "good" | "ok" | "low";
|
|
322
|
+
}
|
|
323
|
+
declare const DEFAULT_UPLOAD_URL = "https://speed.cloudflare.com/__up";
|
|
324
|
+
declare function requiredMbpsFor(settings: {
|
|
325
|
+
resolution: 720 | 1080;
|
|
326
|
+
}): number;
|
|
327
|
+
/**
|
|
328
|
+
* Verdict theo tỉ lệ đo/cần: ≥1.8× là dư (chịu được dao động), ≥1.25× tạm đủ
|
|
329
|
+
* (AIMD sẽ phải hạ bitrate lúc mạng chùng), dưới đó là thiếu — hạ cấu hình
|
|
330
|
+
* hoặc đổi mạng trước khi lên sóng.
|
|
331
|
+
*/
|
|
332
|
+
declare function verdictFor(uploadMbps: number, requiredMbps: number): UplinkCheckResult["verdict"];
|
|
333
|
+
declare function runUplinkCheck(options: UplinkCheckOptions): Promise<UplinkCheckResult>;
|
|
1435
334
|
|
|
1436
|
-
export {
|
|
335
|
+
export { AUDIO_BITRATE_BPS, CAMERA_GRANT_STORAGE_KEY, CameraGrant, type CameraGrantHandle, type CameraGrantPhase, type CameraGrantProvider, type CameraGrantProviderOptions, DEFAULT_LOCAL_CONFIG, DEFAULT_UPLOAD_URL, type HttpResumeFetcherOptions, type HttpSessionProviderOptions, type HttpSessionProviderPaths, LivestreamSessionProvider, PickleballAutoPrepare, type PickleballAutoPrepareProps, PickleballAutoResume, type PickleballAutoResumeProps, RESUME_RETRY_DELAYS_MS, type ResumableSessionProvider, type ResumeAttempt, type ResumeAttemptOutcome, type ResumeVerdict, SdkRemoteConfig, SdkSessionGrant, SdkStorage, StartLivestreamInput, type UplinkCheckOptions, type UplinkCheckResult, type UseStandbySessionOptions, VIDEO_BITRATE_BPS, classifyResumeStatus, createCameraGrantProvider, createHttpResumeFetcher, createHttpSessionProvider, grantFromRefreshPayload, requiredMbpsFor, resumeBlocksArm, resumeExternalSessionId, resumeGrantMatches, resumeRetryDelayMs, runUplinkCheck, useCameraGrant, useStandbySession, verdictFor, withLocalVideoQuality, withResume };
|