@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.
Files changed (117) hide show
  1. package/CHANGELOG.md +65 -0
  2. package/README.md +163 -40
  3. package/dist/advanced-DcbHBCGb.d.ts +300 -0
  4. package/dist/advanced-DeQwg7MK.d.cts +300 -0
  5. package/dist/advanced.cjs +47 -0
  6. package/dist/advanced.d.cts +3 -0
  7. package/dist/advanced.d.ts +3 -0
  8. package/dist/advanced.js +47 -0
  9. package/dist/camera-controls-By8tHigY.d.cts +97 -0
  10. package/dist/camera-controls-rCBUGGPC.d.ts +97 -0
  11. package/dist/chunk-2JHULJZ7.js +265 -0
  12. package/dist/chunk-62YV3PCD.cjs +139 -0
  13. package/dist/chunk-6F3HN6US.js +50 -0
  14. package/dist/chunk-6YE5BL3A.cjs +50 -0
  15. package/dist/chunk-7OX2DSKI.cjs +33 -0
  16. package/dist/chunk-7Z7TN5N3.js +318 -0
  17. package/dist/chunk-DHDX3ZOO.js +800 -0
  18. package/dist/chunk-GXQYMPHE.cjs +9 -0
  19. package/dist/chunk-IUIRVKJI.cjs +2290 -0
  20. package/dist/chunk-KFQGP6VL.js +33 -0
  21. package/dist/chunk-LCWGNGEN.cjs +265 -0
  22. package/dist/chunk-TBUEXRFQ.cjs +318 -0
  23. package/dist/chunk-XGP2MN5R.js +2290 -0
  24. package/dist/chunk-XL4CISVQ.js +9 -0
  25. package/dist/chunk-YGSQ25UA.cjs +800 -0
  26. package/dist/chunk-ZSKSXOCZ.js +139 -0
  27. package/dist/convex-command-subscription-Cabh9tND.d.ts +41 -0
  28. package/dist/convex-command-subscription-DUgpu75l.d.cts +41 -0
  29. package/dist/device-agent/index.cjs +1427 -0
  30. package/dist/device-agent/index.d.cts +493 -0
  31. package/dist/device-agent/index.d.ts +493 -0
  32. package/dist/device-agent/index.js +1427 -0
  33. package/dist/engine-BSvSdCSE.d.cts +741 -0
  34. package/dist/engine-BSvSdCSE.d.ts +741 -0
  35. package/dist/index.cjs +426 -6174
  36. package/dist/index.d.cts +299 -1400
  37. package/dist/index.d.ts +299 -1400
  38. package/dist/index.js +413 -6063
  39. package/dist/match/index.cjs +2456 -0
  40. package/dist/match/index.d.cts +253 -0
  41. package/dist/match/index.d.ts +253 -0
  42. package/dist/match/index.js +2456 -0
  43. package/dist/match/remote/index.cjs +21 -0
  44. package/dist/match/remote/index.d.cts +67 -0
  45. package/dist/match/remote/index.d.ts +67 -0
  46. package/dist/match/remote/index.js +21 -0
  47. package/dist/match/voice/index.cjs +23 -0
  48. package/dist/match/voice/index.d.cts +114 -0
  49. package/dist/match/voice/index.d.ts +114 -0
  50. package/dist/match/voice/index.js +23 -0
  51. package/dist/plugin.cjs +52 -71
  52. package/dist/plugin.d.cts +28 -1
  53. package/dist/plugin.d.ts +28 -1
  54. package/dist/plugin.js +26 -23
  55. package/dist/realtime/index.cjs +31 -0
  56. package/dist/realtime/index.d.cts +15 -0
  57. package/dist/realtime/index.d.ts +15 -0
  58. package/dist/realtime/index.js +31 -0
  59. package/dist/types-CrmcX408.d.cts +176 -0
  60. package/dist/types-CrmcX408.d.ts +176 -0
  61. package/dist/types-DiKWjwvN.d.cts +96 -0
  62. package/dist/types-DiKWjwvN.d.ts +96 -0
  63. package/dist/ui/index.cjs +1297 -0
  64. package/dist/ui/index.d.cts +493 -0
  65. package/dist/ui/index.d.ts +493 -0
  66. package/dist/ui/index.js +1297 -0
  67. package/dist/version.cjs +4 -32
  68. package/dist/version.js +2 -1
  69. package/package.json +95 -15
  70. package/src/advanced.ts +49 -0
  71. package/src/auto-resume.tsx +259 -0
  72. package/src/camera-controls.ts +99 -0
  73. package/src/camera-grant-provider.ts +392 -0
  74. package/src/camera-settings.ts +133 -0
  75. package/src/contracts.ts +73 -0
  76. package/src/device-agent/agent.ts +696 -0
  77. package/src/device-agent/beat-policy.ts +46 -0
  78. package/src/device-agent/component.tsx +60 -0
  79. package/src/device-agent/convex-beat-transport.ts +140 -0
  80. package/src/device-agent/convex-command-subscription.ts +101 -0
  81. package/src/device-agent/convex-control-channel.ts +88 -0
  82. package/src/device-agent/http-transport.ts +164 -0
  83. package/src/device-agent/index.ts +26 -0
  84. package/src/device-agent/mqtt-channel.ts +379 -0
  85. package/src/device-agent/mqtt-codec.ts +347 -0
  86. package/src/device-agent/tournament.ts +352 -0
  87. package/src/engine.ts +601 -20
  88. package/src/errors.ts +16 -0
  89. package/src/http-session-provider.ts +21 -183
  90. package/src/index.ts +117 -14
  91. package/src/local-video-quality.ts +43 -0
  92. package/src/match/index.ts +29 -0
  93. package/src/match/provider.tsx +4 -2
  94. package/src/match/remote/hid-gesture.ts +10 -2
  95. package/src/match/remote/types.ts +6 -4
  96. package/src/match/voice/scheduler.ts +6 -1
  97. package/src/match/voice/service.ts +16 -4
  98. package/src/native-runtime.ts +283 -77
  99. package/src/plugin.ts +54 -20
  100. package/src/provider.tsx +129 -22
  101. package/src/realtime/index.ts +56 -0
  102. package/src/resume-policy.ts +85 -0
  103. package/src/resume.ts +132 -0
  104. package/src/rtmp-room-adapter.ts +348 -0
  105. package/src/session-grant.ts +256 -0
  106. package/src/ui/camera-screen.tsx +484 -0
  107. package/src/ui/controls-sheet.tsx +445 -0
  108. package/src/ui/copy.ts +326 -0
  109. package/src/ui/helpers.ts +105 -0
  110. package/src/ui/hooks.ts +59 -0
  111. package/src/ui/index.ts +42 -0
  112. package/src/ui/preview-view.tsx +39 -0
  113. package/src/ui/primitives.tsx +304 -0
  114. package/src/uplink-check.ts +173 -0
  115. package/dist/chunk-VHLUJQG5.js +0 -17
  116. package/src/livekit-room-adapter.ts +0 -353
  117. package/src/livestream-screen.tsx +0 -980
@@ -0,0 +1,318 @@
1
+ import {
2
+ createDefaultStorage,
3
+ createRtmpRoomFactory,
4
+ useLivestream,
5
+ useLivestreamHost
6
+ } from "./chunk-XGP2MN5R.js";
7
+ import {
8
+ __require
9
+ } from "./chunk-KFQGP6VL.js";
10
+
11
+ // src/resume-policy.ts
12
+ function classifyResumeStatus(status) {
13
+ if (status === 404 || status === 409 || status === 410) return "gone";
14
+ if (status >= 200 && status < 300) return "ok";
15
+ return "retry";
16
+ }
17
+ var RESUME_RETRY_DELAYS_MS = [1e3, 2e3, 4e3, 8e3, 15e3];
18
+ function resumeRetryDelayMs(attempt) {
19
+ const index = Math.min(Math.max(attempt, 0), RESUME_RETRY_DELAYS_MS.length - 1);
20
+ return RESUME_RETRY_DELAYS_MS[index];
21
+ }
22
+ function resumeBlocksArm(input) {
23
+ if (input.engineSessionId !== null) return false;
24
+ return input.adoptableSessionId !== null || input.serverSessionId !== null;
25
+ }
26
+ function resumeExternalSessionId(sessionId) {
27
+ return `resume-${sessionId}`;
28
+ }
29
+ function resumeGrantMatches(externalSessionId, grantSessionId) {
30
+ return externalSessionId === resumeExternalSessionId(grantSessionId);
31
+ }
32
+
33
+ // src/auto-resume.tsx
34
+ import { useEffect, useRef, useState } from "react";
35
+ function useLatest(value) {
36
+ const ref = useRef(value);
37
+ ref.current = value;
38
+ return ref;
39
+ }
40
+ function PickleballAutoResume(props) {
41
+ const {
42
+ provider,
43
+ resumeInput,
44
+ enabled = true,
45
+ sessionIdHint = null,
46
+ retryDelayMs = resumeRetryDelayMs,
47
+ onAttempt
48
+ } = props;
49
+ const live = useLivestream();
50
+ const { engine } = useLivestreamHost();
51
+ const goneRef = useRef(null);
52
+ const attemptRef = useRef(0);
53
+ const [retryTick, setRetryTick] = useState(0);
54
+ const { state, sessionId, adoptableSessionId } = live;
55
+ const resumeId = adoptableSessionId ?? sessionIdHint;
56
+ const liveRef = useLatest(live);
57
+ const onAttemptRef = useLatest(onAttempt);
58
+ const resumeInputRef = useLatest(resumeInput);
59
+ const retryDelayRef = useLatest(retryDelayMs);
60
+ useEffect(() => {
61
+ if (!enabled) return;
62
+ if (!resumeId || sessionId !== null || state !== "preview") return;
63
+ if (goneRef.current === resumeId) return;
64
+ let cancelled = false;
65
+ let timer;
66
+ const scheduleRetry = () => {
67
+ const delay = retryDelayRef.current(attemptRef.current);
68
+ attemptRef.current += 1;
69
+ timer = setTimeout(() => {
70
+ if (!cancelled) setRetryTick((tick) => tick + 1);
71
+ }, delay);
72
+ };
73
+ void (async () => {
74
+ const result = await provider.resume(resumeId);
75
+ if (cancelled) return;
76
+ onAttemptRef.current?.(resumeId, result.kind);
77
+ if (result.kind === "gone") {
78
+ goneRef.current = resumeId;
79
+ attemptRef.current = 0;
80
+ void engine.abandonAdoptableSession(resumeId).catch(() => void 0);
81
+ return;
82
+ }
83
+ if (result.kind === "retry") {
84
+ scheduleRetry();
85
+ return;
86
+ }
87
+ attemptRef.current = 0;
88
+ provider.adoptResumeGrant(result.grant);
89
+ const resumeInput2 = resumeInputRef.current;
90
+ await liveRef.current.start(
91
+ {
92
+ externalSessionId: resumeExternalSessionId(resumeId),
93
+ title: resumeInput2.titleFor?.(resumeId) ?? "Phi\xEAn \u0111ang kh\xF4i ph\u1EE5c",
94
+ consentVersion: resumeInput2.consentVersion,
95
+ visibility: resumeInput2.visibility ?? "public",
96
+ ...resumeInput2.metadata ? { metadata: resumeInput2.metadata } : {}
97
+ },
98
+ // Trạng thái lấy từ chính phản hồi refresh, KHÔNG đoán: gán cứng "live" là
99
+ // camera tự lên sóng khi chủ nó chỉ đang trực sân (18/08/2026).
100
+ { autoPublish: result.live }
101
+ ).catch(() => {
102
+ onAttemptRef.current?.(resumeId, "start_failed");
103
+ if (!cancelled) scheduleRetry();
104
+ });
105
+ })();
106
+ return () => {
107
+ cancelled = true;
108
+ if (timer) clearTimeout(timer);
109
+ };
110
+ }, [enabled, provider, resumeId, sessionId, state, retryTick, engine, liveRef, onAttemptRef, resumeInputRef, retryDelayRef]);
111
+ useEffect(() => {
112
+ if (!enabled || state !== "error" || sessionId === null) return;
113
+ const timer = setTimeout(() => {
114
+ onAttemptRef.current?.(sessionId, "publish_retry");
115
+ void liveRef.current.retry().catch(() => void 0);
116
+ }, retryDelayRef.current(attemptRef.current++));
117
+ return () => clearTimeout(timer);
118
+ }, [enabled, state, sessionId, liveRef, onAttemptRef, retryDelayRef]);
119
+ useEffect(() => {
120
+ if (!enabled || state !== "error" || sessionId !== null || !adoptableSessionId) return;
121
+ const timer = setTimeout(() => {
122
+ onAttemptRef.current?.(adoptableSessionId, "reprepare");
123
+ void liveRef.current.prepare().catch(() => void 0);
124
+ }, 2e3);
125
+ return () => clearTimeout(timer);
126
+ }, [enabled, state, sessionId, adoptableSessionId, liveRef, onAttemptRef]);
127
+ useEffect(() => {
128
+ if (state === "live" || state === "standby") attemptRef.current = 0;
129
+ }, [state]);
130
+ return null;
131
+ }
132
+ function PickleballAutoPrepare(props) {
133
+ const { enabled = true, captureOrientation } = props;
134
+ const live = useLivestream();
135
+ const { state } = live;
136
+ const liveRef = useLatest(live);
137
+ const orientedRef = useRef(false);
138
+ useEffect(() => {
139
+ if (!enabled || !captureOrientation || orientedRef.current) return;
140
+ orientedRef.current = true;
141
+ void liveRef.current.setCaptureOrientation(captureOrientation).catch(() => void 0);
142
+ }, [enabled, captureOrientation, liveRef]);
143
+ useEffect(() => {
144
+ if (!enabled) return;
145
+ if (state !== "idle" && state !== "ended") return;
146
+ void liveRef.current.prepare().catch(() => void 0);
147
+ }, [enabled, state, liveRef]);
148
+ return null;
149
+ }
150
+ function useStandbySession(options) {
151
+ const { input, enabled = true, resumeSessionId = null } = options;
152
+ const live = useLivestream();
153
+ const { state, sessionId, adoptableSessionId } = live;
154
+ const liveRef = useLatest(live);
155
+ const armedForRef = useRef(null);
156
+ const inputRef = useLatest(input);
157
+ const externalSessionId = input.externalSessionId;
158
+ const armed = sessionId !== null && armedForRef.current === externalSessionId;
159
+ useEffect(() => {
160
+ if (!enabled) return;
161
+ if (state !== "preview" || sessionId !== null) return;
162
+ if (armedForRef.current === externalSessionId) return;
163
+ if (resumeBlocksArm({
164
+ adoptableSessionId,
165
+ serverSessionId: resumeSessionId,
166
+ engineSessionId: sessionId
167
+ })) {
168
+ return;
169
+ }
170
+ armedForRef.current = externalSessionId;
171
+ void liveRef.current.start(inputRef.current, { autoPublish: false }).catch(() => {
172
+ armedForRef.current = null;
173
+ });
174
+ }, [enabled, state, sessionId, adoptableSessionId, resumeSessionId, externalSessionId, liveRef, inputRef]);
175
+ return { armed };
176
+ }
177
+
178
+ // src/camera-settings.ts
179
+ import { useSyncExternalStore } from "react";
180
+ var CAMERA_SETTINGS_STORAGE_KEY = "@pickleball/expo-sdk/camera-settings";
181
+ var DEFAULT_CAMERA_SETTINGS = Object.freeze({
182
+ resolution: 720,
183
+ fps: 30,
184
+ antiFlicker: "50hz",
185
+ cameraId: null
186
+ });
187
+ function parseCameraSettings(raw, defaults = DEFAULT_CAMERA_SETTINGS) {
188
+ if (!raw) return { ...defaults };
189
+ try {
190
+ const parsed = JSON.parse(raw);
191
+ if (parsed === null || typeof parsed !== "object") return { ...defaults };
192
+ return {
193
+ resolution: parsed.resolution === 1080 ? 1080 : parsed.resolution === 720 ? 720 : defaults.resolution,
194
+ fps: parsed.fps === 24 || parsed.fps === 30 || parsed.fps === 60 ? parsed.fps : defaults.fps,
195
+ antiFlicker: parsed.antiFlicker === "auto" || parsed.antiFlicker === "60hz" || parsed.antiFlicker === "50hz" ? parsed.antiFlicker : defaults.antiFlicker,
196
+ cameraId: typeof parsed.cameraId === "string" ? parsed.cameraId : null
197
+ };
198
+ } catch {
199
+ return { ...defaults };
200
+ }
201
+ }
202
+ function createCameraSettingsStore(options = {}) {
203
+ const storage = options.storage ?? createDefaultStorage();
204
+ const key = options.key ?? CAMERA_SETTINGS_STORAGE_KEY;
205
+ const defaults = { ...DEFAULT_CAMERA_SETTINGS, ...options.defaults };
206
+ let settings = { ...defaults };
207
+ let restored = false;
208
+ const listeners = /* @__PURE__ */ new Set();
209
+ const emit = () => {
210
+ for (const listener of listeners) listener();
211
+ };
212
+ return {
213
+ key,
214
+ get: () => settings,
215
+ set(patch) {
216
+ settings = { ...settings, ...patch };
217
+ void storage.setItem(key, JSON.stringify(settings)).catch(() => void 0);
218
+ emit();
219
+ },
220
+ subscribe(listener) {
221
+ listeners.add(listener);
222
+ return () => {
223
+ listeners.delete(listener);
224
+ };
225
+ },
226
+ async restore() {
227
+ const raw = await storage.getItem(key).catch(() => null);
228
+ settings = parseCameraSettings(raw, defaults);
229
+ restored = true;
230
+ emit();
231
+ },
232
+ isRestored: () => restored
233
+ };
234
+ }
235
+ function useCameraSettings(store) {
236
+ return useSyncExternalStore(store.subscribe, store.get, store.get);
237
+ }
238
+ function cameraSettingsKey(settings) {
239
+ return `${settings.resolution}:${settings.fps}:${settings.cameraId ?? "default"}`;
240
+ }
241
+ function rtmpRoomFactoryFor(settings) {
242
+ return createRtmpRoomFactory({
243
+ frameRate: settings.fps,
244
+ ...settings.cameraId ? { cameraId: settings.cameraId } : {}
245
+ });
246
+ }
247
+
248
+ // src/camera-controls.ts
249
+ function nativeModule() {
250
+ try {
251
+ const loaded = __require("@pickleball/rtmp-native");
252
+ const candidate = loaded?.default ?? loaded;
253
+ return candidate && typeof candidate === "object" ? candidate : null;
254
+ } catch {
255
+ return null;
256
+ }
257
+ }
258
+ async function callOptional(name, invoke, fallback) {
259
+ const native = nativeModule();
260
+ if (!native || typeof native[name] !== "function") return fallback;
261
+ try {
262
+ const result = await invoke(native);
263
+ return result === void 0 ? fallback : result;
264
+ } catch {
265
+ return fallback;
266
+ }
267
+ }
268
+ function getCameraCapabilities() {
269
+ return callOptional("getCameraCapabilities", (n) => n.getCameraCapabilities?.(), null);
270
+ }
271
+ function setZoom(factor) {
272
+ return callOptional("setZoom", (n) => n.setZoom?.(factor), void 0);
273
+ }
274
+ function setExposureBias(ev) {
275
+ return callOptional("setExposureBias", (n) => n.setExposureBias?.(ev), void 0);
276
+ }
277
+ function setFocusLocked(locked) {
278
+ return callOptional("setFocusLocked", (n) => n.setFocusLocked?.(locked), void 0);
279
+ }
280
+ function setExposureLocked(locked) {
281
+ return callOptional("setExposureLocked", (n) => n.setExposureLocked?.(locked), void 0);
282
+ }
283
+ function setTorch(enabled) {
284
+ return callOptional("setTorch", (n) => n.setTorch?.(enabled), void 0);
285
+ }
286
+ function applyAntiFlicker(mode) {
287
+ return callOptional("setAntiFlicker", (n) => n.setAntiFlicker?.(mode), void 0);
288
+ }
289
+ function requestNativePermissions() {
290
+ return callOptional("requestPermissions", (n) => n.requestPermissions?.(), null);
291
+ }
292
+
293
+ export {
294
+ classifyResumeStatus,
295
+ RESUME_RETRY_DELAYS_MS,
296
+ resumeRetryDelayMs,
297
+ resumeBlocksArm,
298
+ resumeExternalSessionId,
299
+ resumeGrantMatches,
300
+ PickleballAutoResume,
301
+ PickleballAutoPrepare,
302
+ useStandbySession,
303
+ CAMERA_SETTINGS_STORAGE_KEY,
304
+ DEFAULT_CAMERA_SETTINGS,
305
+ parseCameraSettings,
306
+ createCameraSettingsStore,
307
+ useCameraSettings,
308
+ cameraSettingsKey,
309
+ rtmpRoomFactoryFor,
310
+ getCameraCapabilities,
311
+ setZoom,
312
+ setExposureBias,
313
+ setFocusLocked,
314
+ setExposureLocked,
315
+ setTorch,
316
+ applyAntiFlicker,
317
+ requestNativePermissions
318
+ };