@pickleball/expo-sdk 0.2.0 → 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 (89) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +155 -101
  3. package/dist/advanced-DcbHBCGb.d.ts +300 -0
  4. package/dist/advanced-DeQwg7MK.d.cts +300 -0
  5. package/dist/advanced.cjs +14 -2
  6. package/dist/advanced.d.cts +2 -2
  7. package/dist/advanced.d.ts +2 -2
  8. package/dist/advanced.js +17 -5
  9. package/dist/camera-controls-By8tHigY.d.cts +97 -0
  10. package/dist/camera-controls-rCBUGGPC.d.ts +97 -0
  11. package/dist/chunk-62YV3PCD.cjs +139 -0
  12. package/dist/chunk-6F3HN6US.js +50 -0
  13. package/dist/chunk-6YE5BL3A.cjs +50 -0
  14. package/dist/chunk-7Z7TN5N3.js +318 -0
  15. package/dist/{chunk-TRLASNPQ.cjs → chunk-GXQYMPHE.cjs} +1 -1
  16. package/dist/{chunk-TBPR2QND.cjs → chunk-IUIRVKJI.cjs} +736 -399
  17. package/dist/chunk-TBUEXRFQ.cjs +318 -0
  18. package/dist/{chunk-FODE2W2H.js → chunk-XGP2MN5R.js} +688 -351
  19. package/dist/{chunk-6JOMESNT.js → chunk-XL4CISVQ.js} +1 -1
  20. package/dist/chunk-ZSKSXOCZ.js +139 -0
  21. package/dist/convex-command-subscription-Cabh9tND.d.ts +41 -0
  22. package/dist/convex-command-subscription-DUgpu75l.d.cts +41 -0
  23. package/dist/device-agent/index.cjs +973 -44
  24. package/dist/device-agent/index.d.cts +339 -3
  25. package/dist/device-agent/index.d.ts +339 -3
  26. package/dist/device-agent/index.js +940 -11
  27. package/dist/{engine-CCSSqPbZ.d.cts → engine-BSvSdCSE.d.cts} +227 -2
  28. package/dist/{engine-CCSSqPbZ.d.ts → engine-BSvSdCSE.d.ts} +227 -2
  29. package/dist/index.cjs +419 -937
  30. package/dist/index.d.cts +315 -32
  31. package/dist/index.d.ts +315 -32
  32. package/dist/index.js +423 -941
  33. package/dist/plugin.cjs +11 -13
  34. package/dist/plugin.d.cts +15 -1
  35. package/dist/plugin.d.ts +15 -1
  36. package/dist/plugin.js +7 -9
  37. package/dist/realtime/index.cjs +31 -0
  38. package/dist/realtime/index.d.cts +15 -0
  39. package/dist/realtime/index.d.ts +15 -0
  40. package/dist/realtime/index.js +31 -0
  41. package/dist/ui/index.cjs +1297 -0
  42. package/dist/ui/index.d.cts +493 -0
  43. package/dist/ui/index.d.ts +493 -0
  44. package/dist/ui/index.js +1297 -0
  45. package/dist/version.cjs +2 -2
  46. package/dist/version.js +1 -1
  47. package/package.json +31 -15
  48. package/src/advanced.ts +11 -9
  49. package/src/auto-resume.tsx +259 -0
  50. package/src/camera-controls.ts +99 -0
  51. package/src/camera-grant-provider.ts +392 -0
  52. package/src/camera-settings.ts +133 -0
  53. package/src/contracts.ts +73 -0
  54. package/src/device-agent/agent.ts +262 -9
  55. package/src/device-agent/beat-policy.ts +46 -0
  56. package/src/device-agent/component.tsx +11 -0
  57. package/src/device-agent/convex-beat-transport.ts +140 -0
  58. package/src/device-agent/convex-command-subscription.ts +101 -0
  59. package/src/device-agent/convex-control-channel.ts +7 -1
  60. package/src/device-agent/http-transport.ts +22 -1
  61. package/src/device-agent/index.ts +22 -0
  62. package/src/device-agent/mqtt-channel.ts +379 -0
  63. package/src/device-agent/mqtt-codec.ts +347 -0
  64. package/src/device-agent/tournament.ts +352 -0
  65. package/src/engine.ts +580 -15
  66. package/src/http-session-provider.ts +21 -189
  67. package/src/index.ts +84 -5
  68. package/src/local-video-quality.ts +43 -0
  69. package/src/native-runtime.ts +234 -78
  70. package/src/plugin.ts +19 -6
  71. package/src/provider.tsx +94 -18
  72. package/src/realtime/index.ts +56 -0
  73. package/src/resume-policy.ts +85 -0
  74. package/src/resume.ts +132 -0
  75. package/src/rtmp-room-adapter.ts +164 -12
  76. package/src/session-grant.ts +256 -0
  77. package/src/ui/camera-screen.tsx +484 -0
  78. package/src/ui/controls-sheet.tsx +445 -0
  79. package/src/ui/copy.ts +326 -0
  80. package/src/ui/helpers.ts +105 -0
  81. package/src/ui/hooks.ts +59 -0
  82. package/src/ui/index.ts +42 -0
  83. package/src/ui/preview-view.tsx +39 -0
  84. package/src/ui/primitives.tsx +304 -0
  85. package/src/uplink-check.ts +173 -0
  86. package/dist/advanced-BXDrDl1h.d.ts +0 -316
  87. package/dist/advanced-BdAa2Erv.d.cts +0 -316
  88. package/src/livekit-room-adapter.ts +0 -390
  89. package/src/livestream-screen.tsx +0 -1002
@@ -1,5 +1,5 @@
1
1
  // package.json
2
- var version = "0.2.0";
2
+ var version = "1.0.0";
3
3
 
4
4
  // src/version.ts
5
5
  var PICKLEBALL_EXPO_SDK_VERSION = version;
@@ -0,0 +1,139 @@
1
+ import {
2
+ ExpoSdkConfigurationError,
3
+ ExpoSdkTimeoutError,
4
+ InvalidResponseError,
5
+ NetworkUnavailableError,
6
+ PickleballExpoError,
7
+ errorFromCode,
8
+ isSdkRemoteConfig
9
+ } from "./chunk-XGP2MN5R.js";
10
+
11
+ // src/session-grant.ts
12
+ function isRecord(value) {
13
+ return typeof value === "object" && value !== null && !Array.isArray(value);
14
+ }
15
+ function isFiniteNumber(value) {
16
+ return typeof value === "number" && Number.isFinite(value);
17
+ }
18
+ function isBootstrap(value) {
19
+ return isRecord(value) && isSdkRemoteConfig(value.config) && isFiniteNumber(value.serverTime) && typeof value.rolloutEnabled === "boolean" && (value.message === void 0 || typeof value.message === "string");
20
+ }
21
+ function isTelemetryCredentials(value) {
22
+ return isRecord(value) && typeof value.endpoint === "string" && /^https:\/\//.test(value.endpoint) && typeof value.token === "string" && value.token.length > 0 && isFiniteNumber(value.expiresAt);
23
+ }
24
+ function isSessionGrant(value) {
25
+ return isRecord(value) && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.serverUrl === "string" && /^rtmps?:\/\//i.test(value.serverUrl) && typeof value.participantToken === "string" && value.participantToken.length > 0 && (value.playbackUrl === null || typeof value.playbackUrl === "string") && isFiniteNumber(value.tokenExpiresAt) && isTelemetryCredentials(value.telemetry) && isSdkRemoteConfig(value.config);
26
+ }
27
+ function optionalNullableString(value) {
28
+ return value === void 0 || value === null || typeof value === "string";
29
+ }
30
+ function isCameraGrant(value) {
31
+ return isSessionGrant(value) && /^rtmps?:\/\//i.test(value.serverUrl) && (value.status === "scheduled" || value.status === "live") && optionalNullableString(value.matchRef) && optionalNullableString(value.courtRef) && optionalNullableString(value.watchUrl) && optionalNullableString(value.convexUrl);
32
+ }
33
+ function isSessionRefresh(value) {
34
+ if (!isRecord(value) || !isSdkRemoteConfig(value.config)) return false;
35
+ if (value.participantToken !== void 0 && (typeof value.participantToken !== "string" || !value.participantToken)) {
36
+ return false;
37
+ }
38
+ if (value.tokenExpiresAt !== void 0 && !isFiniteNumber(value.tokenExpiresAt)) {
39
+ return false;
40
+ }
41
+ return value.telemetry === void 0 || isTelemetryCredentials(value.telemetry);
42
+ }
43
+ function isSessionStatus(value) {
44
+ return isRecord(value) && typeof value.sessionId === "string" && value.sessionId.length > 0 && typeof value.status === "string" && value.status.length > 0;
45
+ }
46
+ function sessionApiBaseFromTelemetryEndpoint(endpoint) {
47
+ if (!/^https:\/\//.test(endpoint) || !endpoint.endsWith("/telemetry")) {
48
+ throw new InvalidResponseError("The telemetry endpoint must be an HTTPS \u2026/telemetry URL");
49
+ }
50
+ return endpoint.slice(0, -"/telemetry".length);
51
+ }
52
+ var PRIVATE_LAN_HOST = /^(?:10\.\d{1,3}\.\d{1,3}\.\d{1,3}|172\.(?:1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}|192\.168\.\d{1,3}\.\d{1,3})$/;
53
+ function normalizeBaseUrl(value) {
54
+ try {
55
+ const url = new URL(value);
56
+ const loopbackHosts = /* @__PURE__ */ new Set(["localhost", "127.0.0.1", "[::1]"]);
57
+ const secure = url.protocol === "https:";
58
+ const localDevelopment = url.protocol === "http:" && loopbackHosts.has(url.hostname);
59
+ const devPrivateLan = typeof __DEV__ !== "undefined" && __DEV__ === true && url.protocol === "http:" && PRIVATE_LAN_HOST.test(url.hostname);
60
+ if (!secure && !localDevelopment && !devPrivateLan || url.username || url.password) {
61
+ throw new Error();
62
+ }
63
+ return value.replace(/\/+$/, "");
64
+ } catch {
65
+ throw new ExpoSdkConfigurationError(
66
+ "baseUrl must use HTTPS (HTTP is allowed only for localhost loopback)"
67
+ );
68
+ }
69
+ }
70
+ function joinUrl(base, path) {
71
+ return `${base}/${path.replace(/^\/+/, "")}`;
72
+ }
73
+ function parseEnvelopeError(envelope, httpStatus) {
74
+ const body = isRecord(envelope) && isRecord(envelope.error) ? envelope.error : null;
75
+ const error = body && typeof body.code === "string" ? errorFromCode(body.code, typeof body.message === "string" ? body.message : void 0) : new InvalidResponseError();
76
+ return Object.assign(
77
+ error,
78
+ { httpStatus },
79
+ body && typeof body.reason === "string" ? { reason: body.reason } : {},
80
+ body && typeof body.retryAfterMs === "number" ? { retryAfterMs: body.retryAfterMs } : {}
81
+ );
82
+ }
83
+ function createJsonPoster(options = {}) {
84
+ const fetchImplementation = options.fetch ?? globalThis.fetch;
85
+ if (typeof fetchImplementation !== "function") {
86
+ throw new ExpoSdkConfigurationError("A Fetch API implementation is required");
87
+ }
88
+ const timeoutMs = options.timeoutMs ?? 1e4;
89
+ return async function post(url, body, headers = {}) {
90
+ const controller = new AbortController();
91
+ const timeout = setTimeout(() => controller.abort(), timeoutMs);
92
+ try {
93
+ const response = await fetchImplementation(url, {
94
+ method: "POST",
95
+ headers: { "Content-Type": "application/json", ...headers },
96
+ body: JSON.stringify(body),
97
+ signal: controller.signal,
98
+ redirect: "error"
99
+ });
100
+ const envelope = await response.json().catch(() => {
101
+ throw new InvalidResponseError();
102
+ });
103
+ if (!response.ok) throw parseEnvelopeError(envelope, response.status);
104
+ if (!isRecord(envelope) || !("data" in envelope)) {
105
+ throw new InvalidResponseError();
106
+ }
107
+ return envelope.data;
108
+ } catch (error) {
109
+ if (controller.signal.aborted) throw new ExpoSdkTimeoutError();
110
+ if (error instanceof PickleballExpoError) throw error;
111
+ if (error instanceof TypeError) {
112
+ throw new NetworkUnavailableError(void 0, { cause: error });
113
+ }
114
+ throw error;
115
+ } finally {
116
+ clearTimeout(timeout);
117
+ }
118
+ };
119
+ }
120
+ function httpStatusOf(error) {
121
+ const status = error?.httpStatus;
122
+ return typeof status === "number" ? status : void 0;
123
+ }
124
+
125
+ export {
126
+ isRecord,
127
+ isFiniteNumber,
128
+ isBootstrap,
129
+ isTelemetryCredentials,
130
+ isSessionGrant,
131
+ isCameraGrant,
132
+ isSessionRefresh,
133
+ isSessionStatus,
134
+ sessionApiBaseFromTelemetryEndpoint,
135
+ normalizeBaseUrl,
136
+ joinUrl,
137
+ createJsonPoster,
138
+ httpStatusOf
139
+ };
@@ -0,0 +1,41 @@
1
+ import { S as SessionCommandSubscription } from './engine-BSvSdCSE.js';
2
+
3
+ /**
4
+ * Kênh nhận lệnh device-rtmp qua WEBSOCKET (Convex subscription) thay poll HTTP
5
+ * 2 giây. Vì sao đổi: poll 2s là 2 function call mỗi 2 giây MỖI MÁY chạy suốt
6
+ * ngày đêm — ở 200 máy là ~518 triệu call/tháng, gấp 20 lần quota gói Pro.
7
+ * Subscription chỉ chạy lại (và tính tiền) KHI hàng lệnh của phiên đổi: lệnh về
8
+ * tức thì (<1s), còn lúc rảnh gần như 0.
9
+ *
10
+ * CHỈ dùng cho máy FIRST-PARTY nối thẳng Convex (có URL .convex.cloud). Máy đối
11
+ * tác đi qua proxy không có đường websocket trực tiếp → giữ nguyên poll HTTP.
12
+ *
13
+ * Auth: gửi telemetry token THÔ làm tham số query, backend hash server-side
14
+ * (sessionCommands.pendingForSession) — client không có crypto. Cùng lối với
15
+ * deviceControl.pendingCommands (cũng nhận token thô trong query).
16
+ *
17
+ * ConvexClient được dựng MỘT lần và tái dùng qua mọi phiên: engine chỉ
18
+ * subscribe/unsubscribe theo phiên, KHÔNG đóng client giữa chừng (đóng rồi mở
19
+ * lại là bắt tay websocket mới mỗi lần đổi độ phân giải/phiên).
20
+ */
21
+ declare function createConvexCommandSubscription(convexUrl: string): SessionCommandSubscription;
22
+ /**
23
+ * Mọi kênh Convex realtime của máy first-party trên MỘT websocket duy nhất:
24
+ * subscription lệnh (đợt 2) + heartbeat 1-call (đợt 3). Convex client tự
25
+ * multiplex query/mutation trên cùng socket — dựng hai client là trả giá hai
26
+ * lần bắt tay + hai kết nối phải nuôi.
27
+ */
28
+ interface ConvexDeviceRealtime {
29
+ commandSubscription: SessionCommandSubscription;
30
+ /**
31
+ * Heartbeat qua websocket = 1 function call (mutation `deviceApp.beat`),
32
+ * thay cho đường HTTP httpAction→runMutation = 2 call. Trả envelope `data`
33
+ * thô cùng hợp đồng với POST /devices/heartbeat. NÉM lỗi khi thất bại —
34
+ * caller (withConvexBeat) tự rơi về HTTP.
35
+ */
36
+ beat(args: Record<string, unknown>): Promise<Record<string, unknown>>;
37
+ dispose(): void;
38
+ }
39
+ declare function createConvexDeviceRealtime(convexUrl: string): ConvexDeviceRealtime;
40
+
41
+ export { type ConvexDeviceRealtime as C, createConvexDeviceRealtime as a, createConvexCommandSubscription as c };
@@ -0,0 +1,41 @@
1
+ import { S as SessionCommandSubscription } from './engine-BSvSdCSE.cjs';
2
+
3
+ /**
4
+ * Kênh nhận lệnh device-rtmp qua WEBSOCKET (Convex subscription) thay poll HTTP
5
+ * 2 giây. Vì sao đổi: poll 2s là 2 function call mỗi 2 giây MỖI MÁY chạy suốt
6
+ * ngày đêm — ở 200 máy là ~518 triệu call/tháng, gấp 20 lần quota gói Pro.
7
+ * Subscription chỉ chạy lại (và tính tiền) KHI hàng lệnh của phiên đổi: lệnh về
8
+ * tức thì (<1s), còn lúc rảnh gần như 0.
9
+ *
10
+ * CHỈ dùng cho máy FIRST-PARTY nối thẳng Convex (có URL .convex.cloud). Máy đối
11
+ * tác đi qua proxy không có đường websocket trực tiếp → giữ nguyên poll HTTP.
12
+ *
13
+ * Auth: gửi telemetry token THÔ làm tham số query, backend hash server-side
14
+ * (sessionCommands.pendingForSession) — client không có crypto. Cùng lối với
15
+ * deviceControl.pendingCommands (cũng nhận token thô trong query).
16
+ *
17
+ * ConvexClient được dựng MỘT lần và tái dùng qua mọi phiên: engine chỉ
18
+ * subscribe/unsubscribe theo phiên, KHÔNG đóng client giữa chừng (đóng rồi mở
19
+ * lại là bắt tay websocket mới mỗi lần đổi độ phân giải/phiên).
20
+ */
21
+ declare function createConvexCommandSubscription(convexUrl: string): SessionCommandSubscription;
22
+ /**
23
+ * Mọi kênh Convex realtime của máy first-party trên MỘT websocket duy nhất:
24
+ * subscription lệnh (đợt 2) + heartbeat 1-call (đợt 3). Convex client tự
25
+ * multiplex query/mutation trên cùng socket — dựng hai client là trả giá hai
26
+ * lần bắt tay + hai kết nối phải nuôi.
27
+ */
28
+ interface ConvexDeviceRealtime {
29
+ commandSubscription: SessionCommandSubscription;
30
+ /**
31
+ * Heartbeat qua websocket = 1 function call (mutation `deviceApp.beat`),
32
+ * thay cho đường HTTP httpAction→runMutation = 2 call. Trả envelope `data`
33
+ * thô cùng hợp đồng với POST /devices/heartbeat. NÉM lỗi khi thất bại —
34
+ * caller (withConvexBeat) tự rơi về HTTP.
35
+ */
36
+ beat(args: Record<string, unknown>): Promise<Record<string, unknown>>;
37
+ dispose(): void;
38
+ }
39
+ declare function createConvexDeviceRealtime(convexUrl: string): ConvexDeviceRealtime;
40
+
41
+ export { type ConvexDeviceRealtime as C, createConvexDeviceRealtime as a, createConvexCommandSubscription as c };