@pickleball/server-sdk 0.1.1 → 0.2.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 +16 -0
- package/dist/{chunk-HBTNLURN.js → chunk-Y635I73O.js} +39 -0
- package/dist/index.cjs +39 -0
- package/dist/index.d.cts +45 -1
- package/dist/index.d.ts +45 -1
- package/dist/index.js +1 -1
- package/dist/proxy.cjs +170 -0
- package/dist/proxy.d.cts +4 -1
- package/dist/proxy.d.ts +4 -1
- package/dist/proxy.js +130 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# @pickleball/server-sdk
|
|
2
2
|
|
|
3
|
+
## 0.2.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Device registry endpoints for the partner proxy: `devices.register` and
|
|
8
|
+
`devices.heartbeat` on `createPickleballLiveClient`, plus the proxy handlers
|
|
9
|
+
`handleDeviceRegister` / `handleDeviceHeartbeat` (routes `/devices/register`
|
|
10
|
+
and `/devices/heartbeat`). The heartbeat response carries a one-time control
|
|
11
|
+
session grant so an operator dashboard can remote-start a livestream on a
|
|
12
|
+
device that is open but idle.
|
|
13
|
+
- New authorization operations `device_register` and `device_heartbeat`.
|
|
14
|
+
**Production authz gateways must allow these two operation names**; the
|
|
15
|
+
staging stub already allows everything.
|
|
16
|
+
- Version aligned with `@pickleball/expo-sdk@0.2.0` (both packages share a
|
|
17
|
+
stable dist-tag promotion).
|
|
18
|
+
|
|
3
19
|
## 0.1.1
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -314,6 +314,29 @@ function validateRecordings(value, sessionId) {
|
|
|
314
314
|
if (!Array.isArray(value)) invalid();
|
|
315
315
|
return value.map((item) => validateRecording(item, sessionId));
|
|
316
316
|
}
|
|
317
|
+
function validateDeviceRegister(value) {
|
|
318
|
+
const data = record(value);
|
|
319
|
+
const heartbeatIntervalMs = finite(data.heartbeatIntervalMs);
|
|
320
|
+
if (heartbeatIntervalMs <= 0) invalid();
|
|
321
|
+
return {
|
|
322
|
+
deviceId: nonblank(data.deviceId),
|
|
323
|
+
heartbeatIntervalMs
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
function validateDeviceHeartbeat(value) {
|
|
327
|
+
const data = record(value);
|
|
328
|
+
if (data.controlSession === null || data.controlSession === void 0) {
|
|
329
|
+
return { controlSession: null };
|
|
330
|
+
}
|
|
331
|
+
const grant = record(data.controlSession);
|
|
332
|
+
return {
|
|
333
|
+
controlSession: {
|
|
334
|
+
id: nonblank(grant.id),
|
|
335
|
+
token: nonblank(grant.token),
|
|
336
|
+
convexUrl: nonblank(grant.convexUrl)
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
}
|
|
317
340
|
|
|
318
341
|
// src/contracts.ts
|
|
319
342
|
var SDK_TELEMETRY_EVENT_NAMES = [
|
|
@@ -599,6 +622,22 @@ function createPickleballLiveClient(options) {
|
|
|
599
622
|
(value) => validateRecordings(value, sessionId)
|
|
600
623
|
)
|
|
601
624
|
},
|
|
625
|
+
devices: {
|
|
626
|
+
register: (input) => post(
|
|
627
|
+
"/api/v2/sdk/devices/register",
|
|
628
|
+
{ ...input, appId },
|
|
629
|
+
"transient",
|
|
630
|
+
validateDeviceRegister
|
|
631
|
+
),
|
|
632
|
+
heartbeat: (input) => post(
|
|
633
|
+
"/api/v2/sdk/devices/heartbeat",
|
|
634
|
+
{ ...input, appId },
|
|
635
|
+
// Heartbeat có side effect trao token 1 lần — không retry để tránh
|
|
636
|
+
// nhận token đã bị đánh dấu delivered ở lần trước
|
|
637
|
+
"never",
|
|
638
|
+
validateDeviceHeartbeat
|
|
639
|
+
)
|
|
640
|
+
},
|
|
602
641
|
webhooks: { verify: verifyWebhook }
|
|
603
642
|
};
|
|
604
643
|
}
|
package/dist/index.cjs
CHANGED
|
@@ -350,6 +350,29 @@ function validateRecordings(value, sessionId) {
|
|
|
350
350
|
if (!Array.isArray(value)) invalid();
|
|
351
351
|
return value.map((item) => validateRecording(item, sessionId));
|
|
352
352
|
}
|
|
353
|
+
function validateDeviceRegister(value) {
|
|
354
|
+
const data = record(value);
|
|
355
|
+
const heartbeatIntervalMs = finite(data.heartbeatIntervalMs);
|
|
356
|
+
if (heartbeatIntervalMs <= 0) invalid();
|
|
357
|
+
return {
|
|
358
|
+
deviceId: nonblank(data.deviceId),
|
|
359
|
+
heartbeatIntervalMs
|
|
360
|
+
};
|
|
361
|
+
}
|
|
362
|
+
function validateDeviceHeartbeat(value) {
|
|
363
|
+
const data = record(value);
|
|
364
|
+
if (data.controlSession === null || data.controlSession === void 0) {
|
|
365
|
+
return { controlSession: null };
|
|
366
|
+
}
|
|
367
|
+
const grant = record(data.controlSession);
|
|
368
|
+
return {
|
|
369
|
+
controlSession: {
|
|
370
|
+
id: nonblank(grant.id),
|
|
371
|
+
token: nonblank(grant.token),
|
|
372
|
+
convexUrl: nonblank(grant.convexUrl)
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
}
|
|
353
376
|
|
|
354
377
|
// src/contracts.ts
|
|
355
378
|
var SDK_TELEMETRY_EVENT_NAMES = [
|
|
@@ -635,6 +658,22 @@ function createPickleballLiveClient(options) {
|
|
|
635
658
|
(value) => validateRecordings(value, sessionId)
|
|
636
659
|
)
|
|
637
660
|
},
|
|
661
|
+
devices: {
|
|
662
|
+
register: (input) => post(
|
|
663
|
+
"/api/v2/sdk/devices/register",
|
|
664
|
+
{ ...input, appId },
|
|
665
|
+
"transient",
|
|
666
|
+
validateDeviceRegister
|
|
667
|
+
),
|
|
668
|
+
heartbeat: (input) => post(
|
|
669
|
+
"/api/v2/sdk/devices/heartbeat",
|
|
670
|
+
{ ...input, appId },
|
|
671
|
+
// Heartbeat có side effect trao token 1 lần — không retry để tránh
|
|
672
|
+
// nhận token đã bị đánh dấu delivered ở lần trước
|
|
673
|
+
"never",
|
|
674
|
+
validateDeviceHeartbeat
|
|
675
|
+
)
|
|
676
|
+
},
|
|
638
677
|
webhooks: { verify: verifyWebhook }
|
|
639
678
|
};
|
|
640
679
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -126,6 +126,46 @@ interface ApiRecording {
|
|
|
126
126
|
type StartSdkSessionInput = BootstrapInput & StartLivestreamInput & {
|
|
127
127
|
idempotencyKey: string;
|
|
128
128
|
};
|
|
129
|
+
/** Thông tin thiết bị kèm khi đăng ký (từ expo-device). */
|
|
130
|
+
interface DeviceInfoInput {
|
|
131
|
+
appVersion?: string;
|
|
132
|
+
deviceName?: string;
|
|
133
|
+
deviceModel?: string;
|
|
134
|
+
osVersion?: string;
|
|
135
|
+
}
|
|
136
|
+
interface DeviceMetricsInput {
|
|
137
|
+
ramFreeMb?: number;
|
|
138
|
+
ramTotalMb?: number;
|
|
139
|
+
storageFreeGb?: number;
|
|
140
|
+
batteryPct?: number;
|
|
141
|
+
batteryTempC?: number;
|
|
142
|
+
thermal?: number;
|
|
143
|
+
netType?: string;
|
|
144
|
+
linkMbps?: number;
|
|
145
|
+
rssi?: number;
|
|
146
|
+
connQuality?: string;
|
|
147
|
+
}
|
|
148
|
+
interface DeviceRegisterResult {
|
|
149
|
+
deviceId: string;
|
|
150
|
+
heartbeatIntervalMs: number;
|
|
151
|
+
}
|
|
152
|
+
interface DeviceHeartbeatInput {
|
|
153
|
+
/** Trạng thái engine hiện tại (idle|standby|live|...). */
|
|
154
|
+
agentState?: string;
|
|
155
|
+
/** Id phiên live đang giữ — null = không có phiên (server clear link). */
|
|
156
|
+
currentSessionId?: string | null;
|
|
157
|
+
appVersion?: string;
|
|
158
|
+
metrics?: DeviceMetricsInput;
|
|
159
|
+
}
|
|
160
|
+
/** Kênh điều khiển được dashboard yêu cầu — app dùng token mở Convex websocket. */
|
|
161
|
+
interface DeviceControlSessionGrant {
|
|
162
|
+
id: string;
|
|
163
|
+
token: string;
|
|
164
|
+
convexUrl: string;
|
|
165
|
+
}
|
|
166
|
+
interface DeviceHeartbeatResult {
|
|
167
|
+
controlSession: DeviceControlSessionGrant | null;
|
|
168
|
+
}
|
|
129
169
|
declare const SDK_TELEMETRY_EVENT_NAMES: readonly ["state_changed", "connection_quality", "reconnect", "error", "heartbeat", "session_ended"];
|
|
130
170
|
type SdkTelemetryEventName = (typeof SDK_TELEMETRY_EVENT_NAMES)[number];
|
|
131
171
|
interface SdkTelemetryEvent {
|
|
@@ -197,10 +237,14 @@ interface PickleballLiveClient {
|
|
|
197
237
|
get(sessionId: string): Promise<ApiLiveSession>;
|
|
198
238
|
getRecordings(sessionId: string): Promise<ApiRecording[]>;
|
|
199
239
|
};
|
|
240
|
+
devices: {
|
|
241
|
+
register(input: BootstrapInput & DeviceInfoInput): Promise<DeviceRegisterResult>;
|
|
242
|
+
heartbeat(input: BootstrapInput & DeviceHeartbeatInput): Promise<DeviceHeartbeatResult>;
|
|
243
|
+
};
|
|
200
244
|
webhooks: {
|
|
201
245
|
verify<T extends WebhookPayload = WebhookPayload>(rawBody: WebhookRawBody, headers: WebhookHeaders, secret: string, options?: VerifyWebhookOptions): Promise<VerifiedWebhook<T>>;
|
|
202
246
|
};
|
|
203
247
|
}
|
|
204
248
|
declare function createPickleballLiveClient(options: PickleballLiveClientOptions): PickleballLiveClient;
|
|
205
249
|
|
|
206
|
-
export { type ApiLiveSession, type ApiRecording, type BootstrapInput, type EndLivestreamInput, type LivestreamSessionProvider, type LivestreamState, PickleballApiError, PickleballConfigurationError, PickleballHttpError, PickleballInvalidResponseError, type PickleballLiveClient, type PickleballLiveClientOptions, PickleballLiveError, PickleballNetworkError, PickleballTimeoutError, PickleballWebhookVerificationError, type RecordingType, SDK_TELEMETRY_EVENT_NAMES, type SdkBootstrap, type SdkError, type SdkErrorCode, type SdkPublishState, type SdkRemoteConfig, type SdkSessionGrant, type SdkSessionRefresh, type SdkTelemetryCredentials, type SdkTelemetryEvent, type SdkTelemetryEventName, type ServerSdkErrorCode, type SessionStatus, type SessionVisibility, type StartLivestreamInput, type StartSdkSessionInput, type VerifiedWebhook, type VerifyWebhookOptions, type WebhookHeaders, type WebhookPayload, type WebhookRawBody, createPickleballLiveClient, verifyWebhook };
|
|
250
|
+
export { type ApiLiveSession, type ApiRecording, type BootstrapInput, type DeviceControlSessionGrant, type DeviceHeartbeatInput, type DeviceHeartbeatResult, type DeviceInfoInput, type DeviceMetricsInput, type DeviceRegisterResult, type EndLivestreamInput, type LivestreamSessionProvider, type LivestreamState, PickleballApiError, PickleballConfigurationError, PickleballHttpError, PickleballInvalidResponseError, type PickleballLiveClient, type PickleballLiveClientOptions, PickleballLiveError, PickleballNetworkError, PickleballTimeoutError, PickleballWebhookVerificationError, type RecordingType, SDK_TELEMETRY_EVENT_NAMES, type SdkBootstrap, type SdkError, type SdkErrorCode, type SdkPublishState, type SdkRemoteConfig, type SdkSessionGrant, type SdkSessionRefresh, type SdkTelemetryCredentials, type SdkTelemetryEvent, type SdkTelemetryEventName, type ServerSdkErrorCode, type SessionStatus, type SessionVisibility, type StartLivestreamInput, type StartSdkSessionInput, type VerifiedWebhook, type VerifyWebhookOptions, type WebhookHeaders, type WebhookPayload, type WebhookRawBody, createPickleballLiveClient, verifyWebhook };
|
package/dist/index.d.ts
CHANGED
|
@@ -126,6 +126,46 @@ interface ApiRecording {
|
|
|
126
126
|
type StartSdkSessionInput = BootstrapInput & StartLivestreamInput & {
|
|
127
127
|
idempotencyKey: string;
|
|
128
128
|
};
|
|
129
|
+
/** Thông tin thiết bị kèm khi đăng ký (từ expo-device). */
|
|
130
|
+
interface DeviceInfoInput {
|
|
131
|
+
appVersion?: string;
|
|
132
|
+
deviceName?: string;
|
|
133
|
+
deviceModel?: string;
|
|
134
|
+
osVersion?: string;
|
|
135
|
+
}
|
|
136
|
+
interface DeviceMetricsInput {
|
|
137
|
+
ramFreeMb?: number;
|
|
138
|
+
ramTotalMb?: number;
|
|
139
|
+
storageFreeGb?: number;
|
|
140
|
+
batteryPct?: number;
|
|
141
|
+
batteryTempC?: number;
|
|
142
|
+
thermal?: number;
|
|
143
|
+
netType?: string;
|
|
144
|
+
linkMbps?: number;
|
|
145
|
+
rssi?: number;
|
|
146
|
+
connQuality?: string;
|
|
147
|
+
}
|
|
148
|
+
interface DeviceRegisterResult {
|
|
149
|
+
deviceId: string;
|
|
150
|
+
heartbeatIntervalMs: number;
|
|
151
|
+
}
|
|
152
|
+
interface DeviceHeartbeatInput {
|
|
153
|
+
/** Trạng thái engine hiện tại (idle|standby|live|...). */
|
|
154
|
+
agentState?: string;
|
|
155
|
+
/** Id phiên live đang giữ — null = không có phiên (server clear link). */
|
|
156
|
+
currentSessionId?: string | null;
|
|
157
|
+
appVersion?: string;
|
|
158
|
+
metrics?: DeviceMetricsInput;
|
|
159
|
+
}
|
|
160
|
+
/** Kênh điều khiển được dashboard yêu cầu — app dùng token mở Convex websocket. */
|
|
161
|
+
interface DeviceControlSessionGrant {
|
|
162
|
+
id: string;
|
|
163
|
+
token: string;
|
|
164
|
+
convexUrl: string;
|
|
165
|
+
}
|
|
166
|
+
interface DeviceHeartbeatResult {
|
|
167
|
+
controlSession: DeviceControlSessionGrant | null;
|
|
168
|
+
}
|
|
129
169
|
declare const SDK_TELEMETRY_EVENT_NAMES: readonly ["state_changed", "connection_quality", "reconnect", "error", "heartbeat", "session_ended"];
|
|
130
170
|
type SdkTelemetryEventName = (typeof SDK_TELEMETRY_EVENT_NAMES)[number];
|
|
131
171
|
interface SdkTelemetryEvent {
|
|
@@ -197,10 +237,14 @@ interface PickleballLiveClient {
|
|
|
197
237
|
get(sessionId: string): Promise<ApiLiveSession>;
|
|
198
238
|
getRecordings(sessionId: string): Promise<ApiRecording[]>;
|
|
199
239
|
};
|
|
240
|
+
devices: {
|
|
241
|
+
register(input: BootstrapInput & DeviceInfoInput): Promise<DeviceRegisterResult>;
|
|
242
|
+
heartbeat(input: BootstrapInput & DeviceHeartbeatInput): Promise<DeviceHeartbeatResult>;
|
|
243
|
+
};
|
|
200
244
|
webhooks: {
|
|
201
245
|
verify<T extends WebhookPayload = WebhookPayload>(rawBody: WebhookRawBody, headers: WebhookHeaders, secret: string, options?: VerifyWebhookOptions): Promise<VerifiedWebhook<T>>;
|
|
202
246
|
};
|
|
203
247
|
}
|
|
204
248
|
declare function createPickleballLiveClient(options: PickleballLiveClientOptions): PickleballLiveClient;
|
|
205
249
|
|
|
206
|
-
export { type ApiLiveSession, type ApiRecording, type BootstrapInput, type EndLivestreamInput, type LivestreamSessionProvider, type LivestreamState, PickleballApiError, PickleballConfigurationError, PickleballHttpError, PickleballInvalidResponseError, type PickleballLiveClient, type PickleballLiveClientOptions, PickleballLiveError, PickleballNetworkError, PickleballTimeoutError, PickleballWebhookVerificationError, type RecordingType, SDK_TELEMETRY_EVENT_NAMES, type SdkBootstrap, type SdkError, type SdkErrorCode, type SdkPublishState, type SdkRemoteConfig, type SdkSessionGrant, type SdkSessionRefresh, type SdkTelemetryCredentials, type SdkTelemetryEvent, type SdkTelemetryEventName, type ServerSdkErrorCode, type SessionStatus, type SessionVisibility, type StartLivestreamInput, type StartSdkSessionInput, type VerifiedWebhook, type VerifyWebhookOptions, type WebhookHeaders, type WebhookPayload, type WebhookRawBody, createPickleballLiveClient, verifyWebhook };
|
|
250
|
+
export { type ApiLiveSession, type ApiRecording, type BootstrapInput, type DeviceControlSessionGrant, type DeviceHeartbeatInput, type DeviceHeartbeatResult, type DeviceInfoInput, type DeviceMetricsInput, type DeviceRegisterResult, type EndLivestreamInput, type LivestreamSessionProvider, type LivestreamState, PickleballApiError, PickleballConfigurationError, PickleballHttpError, PickleballInvalidResponseError, type PickleballLiveClient, type PickleballLiveClientOptions, PickleballLiveError, PickleballNetworkError, PickleballTimeoutError, PickleballWebhookVerificationError, type RecordingType, SDK_TELEMETRY_EVENT_NAMES, type SdkBootstrap, type SdkError, type SdkErrorCode, type SdkPublishState, type SdkRemoteConfig, type SdkSessionGrant, type SdkSessionRefresh, type SdkTelemetryCredentials, type SdkTelemetryEvent, type SdkTelemetryEventName, type ServerSdkErrorCode, type SessionStatus, type SessionVisibility, type StartLivestreamInput, type StartSdkSessionInput, type VerifiedWebhook, type VerifyWebhookOptions, type WebhookHeaders, type WebhookPayload, type WebhookRawBody, createPickleballLiveClient, verifyWebhook };
|
package/dist/index.js
CHANGED
package/dist/proxy.cjs
CHANGED
|
@@ -22,6 +22,8 @@ var proxy_exports = {};
|
|
|
22
22
|
__export(proxy_exports, {
|
|
23
23
|
createStableIdempotencyKey: () => createStableIdempotencyKey,
|
|
24
24
|
handleBootstrap: () => handleBootstrap,
|
|
25
|
+
handleDeviceHeartbeat: () => handleDeviceHeartbeat,
|
|
26
|
+
handleDeviceRegister: () => handleDeviceRegister,
|
|
25
27
|
handleSessionAction: () => handleSessionAction,
|
|
26
28
|
handleSessions: () => handleSessions
|
|
27
29
|
});
|
|
@@ -343,6 +345,29 @@ function validateRecordings(value, sessionId) {
|
|
|
343
345
|
if (!Array.isArray(value)) invalid();
|
|
344
346
|
return value.map((item) => validateRecording(item, sessionId));
|
|
345
347
|
}
|
|
348
|
+
function validateDeviceRegister(value) {
|
|
349
|
+
const data = record(value);
|
|
350
|
+
const heartbeatIntervalMs = finite(data.heartbeatIntervalMs);
|
|
351
|
+
if (heartbeatIntervalMs <= 0) invalid();
|
|
352
|
+
return {
|
|
353
|
+
deviceId: nonblank(data.deviceId),
|
|
354
|
+
heartbeatIntervalMs
|
|
355
|
+
};
|
|
356
|
+
}
|
|
357
|
+
function validateDeviceHeartbeat(value) {
|
|
358
|
+
const data = record(value);
|
|
359
|
+
if (data.controlSession === null || data.controlSession === void 0) {
|
|
360
|
+
return { controlSession: null };
|
|
361
|
+
}
|
|
362
|
+
const grant = record(data.controlSession);
|
|
363
|
+
return {
|
|
364
|
+
controlSession: {
|
|
365
|
+
id: nonblank(grant.id),
|
|
366
|
+
token: nonblank(grant.token),
|
|
367
|
+
convexUrl: nonblank(grant.convexUrl)
|
|
368
|
+
}
|
|
369
|
+
};
|
|
370
|
+
}
|
|
346
371
|
|
|
347
372
|
// src/index.ts
|
|
348
373
|
var TRANSIENT_STATUSES = /* @__PURE__ */ new Set([408, 429]);
|
|
@@ -618,6 +643,22 @@ function createPickleballLiveClient(options) {
|
|
|
618
643
|
(value) => validateRecordings(value, sessionId)
|
|
619
644
|
)
|
|
620
645
|
},
|
|
646
|
+
devices: {
|
|
647
|
+
register: (input) => post(
|
|
648
|
+
"/api/v2/sdk/devices/register",
|
|
649
|
+
{ ...input, appId },
|
|
650
|
+
"transient",
|
|
651
|
+
validateDeviceRegister
|
|
652
|
+
),
|
|
653
|
+
heartbeat: (input) => post(
|
|
654
|
+
"/api/v2/sdk/devices/heartbeat",
|
|
655
|
+
{ ...input, appId },
|
|
656
|
+
// Heartbeat có side effect trao token 1 lần — không retry để tránh
|
|
657
|
+
// nhận token đã bị đánh dấu delivered ở lần trước
|
|
658
|
+
"never",
|
|
659
|
+
validateDeviceHeartbeat
|
|
660
|
+
)
|
|
661
|
+
},
|
|
621
662
|
webhooks: { verify: verifyWebhook }
|
|
622
663
|
};
|
|
623
664
|
}
|
|
@@ -1009,10 +1050,139 @@ async function handleSessionAction(request, params, deps) {
|
|
|
1009
1050
|
return null;
|
|
1010
1051
|
});
|
|
1011
1052
|
}
|
|
1053
|
+
var DEVICE_INFO_FIELDS = ["appVersion", "deviceName", "deviceModel", "osVersion"];
|
|
1054
|
+
var DEVICE_METRIC_NUMBER_FIELDS = [
|
|
1055
|
+
"ramFreeMb",
|
|
1056
|
+
"ramTotalMb",
|
|
1057
|
+
"storageFreeGb",
|
|
1058
|
+
"batteryPct",
|
|
1059
|
+
"batteryTempC",
|
|
1060
|
+
"thermal",
|
|
1061
|
+
"linkMbps",
|
|
1062
|
+
"rssi"
|
|
1063
|
+
];
|
|
1064
|
+
var DEVICE_METRIC_STRING_FIELDS = ["netType", "connQuality"];
|
|
1065
|
+
function deviceInfoFields(value) {
|
|
1066
|
+
const info = {};
|
|
1067
|
+
for (const field of DEVICE_INFO_FIELDS) {
|
|
1068
|
+
const entry = value[field];
|
|
1069
|
+
if (entry === void 0) continue;
|
|
1070
|
+
if (!nonblank2(entry, 200)) throw new InvalidRequest();
|
|
1071
|
+
info[field] = entry;
|
|
1072
|
+
}
|
|
1073
|
+
return info;
|
|
1074
|
+
}
|
|
1075
|
+
function deviceRegisterBody(value) {
|
|
1076
|
+
if (!hasOnlyKeys(value, [
|
|
1077
|
+
"sdkVersion",
|
|
1078
|
+
"platform",
|
|
1079
|
+
"bundleId",
|
|
1080
|
+
"installationId",
|
|
1081
|
+
...DEVICE_INFO_FIELDS
|
|
1082
|
+
])) {
|
|
1083
|
+
throw new InvalidRequest();
|
|
1084
|
+
}
|
|
1085
|
+
const identity = bootstrapBody({
|
|
1086
|
+
sdkVersion: value.sdkVersion,
|
|
1087
|
+
platform: value.platform,
|
|
1088
|
+
bundleId: value.bundleId,
|
|
1089
|
+
installationId: value.installationId
|
|
1090
|
+
});
|
|
1091
|
+
return { ...identity, ...deviceInfoFields(value) };
|
|
1092
|
+
}
|
|
1093
|
+
function deviceMetricsFields(value) {
|
|
1094
|
+
if (value === void 0) return void 0;
|
|
1095
|
+
if (!isObject(value)) throw new InvalidRequest();
|
|
1096
|
+
if (!hasOnlyKeys(value, [...DEVICE_METRIC_NUMBER_FIELDS, ...DEVICE_METRIC_STRING_FIELDS])) {
|
|
1097
|
+
throw new InvalidRequest();
|
|
1098
|
+
}
|
|
1099
|
+
const metrics = {};
|
|
1100
|
+
for (const field of DEVICE_METRIC_NUMBER_FIELDS) {
|
|
1101
|
+
const entry = value[field];
|
|
1102
|
+
if (entry === void 0) continue;
|
|
1103
|
+
if (typeof entry !== "number" || !Number.isFinite(entry)) throw new InvalidRequest();
|
|
1104
|
+
metrics[field] = entry;
|
|
1105
|
+
}
|
|
1106
|
+
for (const field of DEVICE_METRIC_STRING_FIELDS) {
|
|
1107
|
+
const entry = value[field];
|
|
1108
|
+
if (entry === void 0) continue;
|
|
1109
|
+
if (!nonblank2(entry, 50)) throw new InvalidRequest();
|
|
1110
|
+
metrics[field] = entry;
|
|
1111
|
+
}
|
|
1112
|
+
return metrics;
|
|
1113
|
+
}
|
|
1114
|
+
function deviceHeartbeatBody(value) {
|
|
1115
|
+
if (!hasOnlyKeys(value, [
|
|
1116
|
+
"sdkVersion",
|
|
1117
|
+
"platform",
|
|
1118
|
+
"bundleId",
|
|
1119
|
+
"installationId",
|
|
1120
|
+
"agentState",
|
|
1121
|
+
"currentSessionId",
|
|
1122
|
+
"appVersion",
|
|
1123
|
+
"metrics"
|
|
1124
|
+
])) {
|
|
1125
|
+
throw new InvalidRequest();
|
|
1126
|
+
}
|
|
1127
|
+
const identity = bootstrapBody({
|
|
1128
|
+
sdkVersion: value.sdkVersion,
|
|
1129
|
+
platform: value.platform,
|
|
1130
|
+
bundleId: value.bundleId,
|
|
1131
|
+
installationId: value.installationId
|
|
1132
|
+
});
|
|
1133
|
+
if (value.agentState !== void 0 && !nonblank2(value.agentState, 50)) {
|
|
1134
|
+
throw new InvalidRequest();
|
|
1135
|
+
}
|
|
1136
|
+
if (value.currentSessionId !== void 0 && value.currentSessionId !== null && !nonblank2(value.currentSessionId, 256)) {
|
|
1137
|
+
throw new InvalidRequest();
|
|
1138
|
+
}
|
|
1139
|
+
if (value.appVersion !== void 0 && !nonblank2(value.appVersion, 200)) {
|
|
1140
|
+
throw new InvalidRequest();
|
|
1141
|
+
}
|
|
1142
|
+
const metrics = deviceMetricsFields(value.metrics);
|
|
1143
|
+
return {
|
|
1144
|
+
...identity,
|
|
1145
|
+
...value.agentState === void 0 ? {} : { agentState: value.agentState },
|
|
1146
|
+
...value.currentSessionId === void 0 ? {} : { currentSessionId: value.currentSessionId },
|
|
1147
|
+
...value.appVersion === void 0 ? {} : { appVersion: value.appVersion },
|
|
1148
|
+
...metrics === void 0 ? {} : { metrics }
|
|
1149
|
+
};
|
|
1150
|
+
}
|
|
1151
|
+
function deviceClientFor(deps) {
|
|
1152
|
+
const client = clientFor(deps);
|
|
1153
|
+
if (!client.devices) {
|
|
1154
|
+
throw new PickleballConfigurationError("Proxy client does not support devices");
|
|
1155
|
+
}
|
|
1156
|
+
return client.devices;
|
|
1157
|
+
}
|
|
1158
|
+
async function handleDeviceRegister(request, deps) {
|
|
1159
|
+
return execute(deps, async () => {
|
|
1160
|
+
const authorization = userAuthorization(request.headers);
|
|
1161
|
+
const attestation = appAttestation(request.headers);
|
|
1162
|
+
const identity = identityFromHeaders(request.headers);
|
|
1163
|
+
const input = deviceRegisterBody(await bodyObject(request));
|
|
1164
|
+
if (!identitiesMatch(identity, input)) throw new InvalidRequest();
|
|
1165
|
+
await authorize(deps, authorization, "device_register", identity, {}, attestation);
|
|
1166
|
+
return deviceClientFor(deps).register(input);
|
|
1167
|
+
});
|
|
1168
|
+
}
|
|
1169
|
+
async function handleDeviceHeartbeat(request, deps) {
|
|
1170
|
+
return execute(deps, async () => {
|
|
1171
|
+
const authorization = userAuthorization(request.headers);
|
|
1172
|
+
const attestation = appAttestation(request.headers);
|
|
1173
|
+
const identity = identityFromHeaders(request.headers);
|
|
1174
|
+
const input = deviceHeartbeatBody(await bodyObject(request));
|
|
1175
|
+
if (!identitiesMatch(identity, input)) throw new InvalidRequest();
|
|
1176
|
+
await authorize(deps, authorization, "device_heartbeat", identity, {}, attestation);
|
|
1177
|
+
return deviceClientFor(deps).heartbeat(input);
|
|
1178
|
+
});
|
|
1179
|
+
}
|
|
1012
1180
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1013
1181
|
0 && (module.exports = {
|
|
1014
1182
|
createStableIdempotencyKey,
|
|
1015
1183
|
handleBootstrap,
|
|
1184
|
+
handleDeviceHeartbeat,
|
|
1185
|
+
handleDeviceRegister,
|
|
1016
1186
|
handleSessionAction,
|
|
1017
1187
|
handleSessions
|
|
1018
1188
|
});
|
package/dist/proxy.d.cts
CHANGED
|
@@ -21,6 +21,7 @@ interface ProxyDependencies {
|
|
|
21
21
|
type ProxyClient = {
|
|
22
22
|
apps: Pick<PickleballLiveClient["apps"], "bootstrap">;
|
|
23
23
|
sessions: Pick<PickleballLiveClient["sessions"], "start" | "refresh" | "end" | "publish" | "unpublish">;
|
|
24
|
+
devices?: Pick<PickleballLiveClient["devices"], "register" | "heartbeat">;
|
|
24
25
|
};
|
|
25
26
|
interface SessionActionParams {
|
|
26
27
|
id: string;
|
|
@@ -35,5 +36,7 @@ declare function createStableIdempotencyKey(input: {
|
|
|
35
36
|
declare function handleBootstrap(request: Request, deps: ProxyDependencies): Promise<Response>;
|
|
36
37
|
declare function handleSessions(request: Request, deps: ProxyDependencies): Promise<Response>;
|
|
37
38
|
declare function handleSessionAction(request: Request, params: SessionActionParams, deps: ProxyDependencies): Promise<Response>;
|
|
39
|
+
declare function handleDeviceRegister(request: Request, deps: ProxyDependencies): Promise<Response>;
|
|
40
|
+
declare function handleDeviceHeartbeat(request: Request, deps: ProxyDependencies): Promise<Response>;
|
|
38
41
|
|
|
39
|
-
export { type ProxyDependencies, type ProxyEnvironment, type SessionActionParams, createStableIdempotencyKey, handleBootstrap, handleSessionAction, handleSessions };
|
|
42
|
+
export { type ProxyDependencies, type ProxyEnvironment, type SessionActionParams, createStableIdempotencyKey, handleBootstrap, handleDeviceHeartbeat, handleDeviceRegister, handleSessionAction, handleSessions };
|
package/dist/proxy.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ interface ProxyDependencies {
|
|
|
21
21
|
type ProxyClient = {
|
|
22
22
|
apps: Pick<PickleballLiveClient["apps"], "bootstrap">;
|
|
23
23
|
sessions: Pick<PickleballLiveClient["sessions"], "start" | "refresh" | "end" | "publish" | "unpublish">;
|
|
24
|
+
devices?: Pick<PickleballLiveClient["devices"], "register" | "heartbeat">;
|
|
24
25
|
};
|
|
25
26
|
interface SessionActionParams {
|
|
26
27
|
id: string;
|
|
@@ -35,5 +36,7 @@ declare function createStableIdempotencyKey(input: {
|
|
|
35
36
|
declare function handleBootstrap(request: Request, deps: ProxyDependencies): Promise<Response>;
|
|
36
37
|
declare function handleSessions(request: Request, deps: ProxyDependencies): Promise<Response>;
|
|
37
38
|
declare function handleSessionAction(request: Request, params: SessionActionParams, deps: ProxyDependencies): Promise<Response>;
|
|
39
|
+
declare function handleDeviceRegister(request: Request, deps: ProxyDependencies): Promise<Response>;
|
|
40
|
+
declare function handleDeviceHeartbeat(request: Request, deps: ProxyDependencies): Promise<Response>;
|
|
38
41
|
|
|
39
|
-
export { type ProxyDependencies, type ProxyEnvironment, type SessionActionParams, createStableIdempotencyKey, handleBootstrap, handleSessionAction, handleSessions };
|
|
42
|
+
export { type ProxyDependencies, type ProxyEnvironment, type SessionActionParams, createStableIdempotencyKey, handleBootstrap, handleDeviceHeartbeat, handleDeviceRegister, handleSessionAction, handleSessions };
|
package/dist/proxy.js
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
PickleballNetworkError,
|
|
7
7
|
PickleballTimeoutError,
|
|
8
8
|
createPickleballLiveClient
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-Y635I73O.js";
|
|
10
10
|
|
|
11
11
|
// src/proxy.ts
|
|
12
12
|
var MAX_BODY_BYTES = 16 * 1024;
|
|
@@ -395,9 +395,138 @@ async function handleSessionAction(request, params, deps) {
|
|
|
395
395
|
return null;
|
|
396
396
|
});
|
|
397
397
|
}
|
|
398
|
+
var DEVICE_INFO_FIELDS = ["appVersion", "deviceName", "deviceModel", "osVersion"];
|
|
399
|
+
var DEVICE_METRIC_NUMBER_FIELDS = [
|
|
400
|
+
"ramFreeMb",
|
|
401
|
+
"ramTotalMb",
|
|
402
|
+
"storageFreeGb",
|
|
403
|
+
"batteryPct",
|
|
404
|
+
"batteryTempC",
|
|
405
|
+
"thermal",
|
|
406
|
+
"linkMbps",
|
|
407
|
+
"rssi"
|
|
408
|
+
];
|
|
409
|
+
var DEVICE_METRIC_STRING_FIELDS = ["netType", "connQuality"];
|
|
410
|
+
function deviceInfoFields(value) {
|
|
411
|
+
const info = {};
|
|
412
|
+
for (const field of DEVICE_INFO_FIELDS) {
|
|
413
|
+
const entry = value[field];
|
|
414
|
+
if (entry === void 0) continue;
|
|
415
|
+
if (!nonblank(entry, 200)) throw new InvalidRequest();
|
|
416
|
+
info[field] = entry;
|
|
417
|
+
}
|
|
418
|
+
return info;
|
|
419
|
+
}
|
|
420
|
+
function deviceRegisterBody(value) {
|
|
421
|
+
if (!hasOnlyKeys(value, [
|
|
422
|
+
"sdkVersion",
|
|
423
|
+
"platform",
|
|
424
|
+
"bundleId",
|
|
425
|
+
"installationId",
|
|
426
|
+
...DEVICE_INFO_FIELDS
|
|
427
|
+
])) {
|
|
428
|
+
throw new InvalidRequest();
|
|
429
|
+
}
|
|
430
|
+
const identity = bootstrapBody({
|
|
431
|
+
sdkVersion: value.sdkVersion,
|
|
432
|
+
platform: value.platform,
|
|
433
|
+
bundleId: value.bundleId,
|
|
434
|
+
installationId: value.installationId
|
|
435
|
+
});
|
|
436
|
+
return { ...identity, ...deviceInfoFields(value) };
|
|
437
|
+
}
|
|
438
|
+
function deviceMetricsFields(value) {
|
|
439
|
+
if (value === void 0) return void 0;
|
|
440
|
+
if (!isObject(value)) throw new InvalidRequest();
|
|
441
|
+
if (!hasOnlyKeys(value, [...DEVICE_METRIC_NUMBER_FIELDS, ...DEVICE_METRIC_STRING_FIELDS])) {
|
|
442
|
+
throw new InvalidRequest();
|
|
443
|
+
}
|
|
444
|
+
const metrics = {};
|
|
445
|
+
for (const field of DEVICE_METRIC_NUMBER_FIELDS) {
|
|
446
|
+
const entry = value[field];
|
|
447
|
+
if (entry === void 0) continue;
|
|
448
|
+
if (typeof entry !== "number" || !Number.isFinite(entry)) throw new InvalidRequest();
|
|
449
|
+
metrics[field] = entry;
|
|
450
|
+
}
|
|
451
|
+
for (const field of DEVICE_METRIC_STRING_FIELDS) {
|
|
452
|
+
const entry = value[field];
|
|
453
|
+
if (entry === void 0) continue;
|
|
454
|
+
if (!nonblank(entry, 50)) throw new InvalidRequest();
|
|
455
|
+
metrics[field] = entry;
|
|
456
|
+
}
|
|
457
|
+
return metrics;
|
|
458
|
+
}
|
|
459
|
+
function deviceHeartbeatBody(value) {
|
|
460
|
+
if (!hasOnlyKeys(value, [
|
|
461
|
+
"sdkVersion",
|
|
462
|
+
"platform",
|
|
463
|
+
"bundleId",
|
|
464
|
+
"installationId",
|
|
465
|
+
"agentState",
|
|
466
|
+
"currentSessionId",
|
|
467
|
+
"appVersion",
|
|
468
|
+
"metrics"
|
|
469
|
+
])) {
|
|
470
|
+
throw new InvalidRequest();
|
|
471
|
+
}
|
|
472
|
+
const identity = bootstrapBody({
|
|
473
|
+
sdkVersion: value.sdkVersion,
|
|
474
|
+
platform: value.platform,
|
|
475
|
+
bundleId: value.bundleId,
|
|
476
|
+
installationId: value.installationId
|
|
477
|
+
});
|
|
478
|
+
if (value.agentState !== void 0 && !nonblank(value.agentState, 50)) {
|
|
479
|
+
throw new InvalidRequest();
|
|
480
|
+
}
|
|
481
|
+
if (value.currentSessionId !== void 0 && value.currentSessionId !== null && !nonblank(value.currentSessionId, 256)) {
|
|
482
|
+
throw new InvalidRequest();
|
|
483
|
+
}
|
|
484
|
+
if (value.appVersion !== void 0 && !nonblank(value.appVersion, 200)) {
|
|
485
|
+
throw new InvalidRequest();
|
|
486
|
+
}
|
|
487
|
+
const metrics = deviceMetricsFields(value.metrics);
|
|
488
|
+
return {
|
|
489
|
+
...identity,
|
|
490
|
+
...value.agentState === void 0 ? {} : { agentState: value.agentState },
|
|
491
|
+
...value.currentSessionId === void 0 ? {} : { currentSessionId: value.currentSessionId },
|
|
492
|
+
...value.appVersion === void 0 ? {} : { appVersion: value.appVersion },
|
|
493
|
+
...metrics === void 0 ? {} : { metrics }
|
|
494
|
+
};
|
|
495
|
+
}
|
|
496
|
+
function deviceClientFor(deps) {
|
|
497
|
+
const client = clientFor(deps);
|
|
498
|
+
if (!client.devices) {
|
|
499
|
+
throw new PickleballConfigurationError("Proxy client does not support devices");
|
|
500
|
+
}
|
|
501
|
+
return client.devices;
|
|
502
|
+
}
|
|
503
|
+
async function handleDeviceRegister(request, deps) {
|
|
504
|
+
return execute(deps, async () => {
|
|
505
|
+
const authorization = userAuthorization(request.headers);
|
|
506
|
+
const attestation = appAttestation(request.headers);
|
|
507
|
+
const identity = identityFromHeaders(request.headers);
|
|
508
|
+
const input = deviceRegisterBody(await bodyObject(request));
|
|
509
|
+
if (!identitiesMatch(identity, input)) throw new InvalidRequest();
|
|
510
|
+
await authorize(deps, authorization, "device_register", identity, {}, attestation);
|
|
511
|
+
return deviceClientFor(deps).register(input);
|
|
512
|
+
});
|
|
513
|
+
}
|
|
514
|
+
async function handleDeviceHeartbeat(request, deps) {
|
|
515
|
+
return execute(deps, async () => {
|
|
516
|
+
const authorization = userAuthorization(request.headers);
|
|
517
|
+
const attestation = appAttestation(request.headers);
|
|
518
|
+
const identity = identityFromHeaders(request.headers);
|
|
519
|
+
const input = deviceHeartbeatBody(await bodyObject(request));
|
|
520
|
+
if (!identitiesMatch(identity, input)) throw new InvalidRequest();
|
|
521
|
+
await authorize(deps, authorization, "device_heartbeat", identity, {}, attestation);
|
|
522
|
+
return deviceClientFor(deps).heartbeat(input);
|
|
523
|
+
});
|
|
524
|
+
}
|
|
398
525
|
export {
|
|
399
526
|
createStableIdempotencyKey,
|
|
400
527
|
handleBootstrap,
|
|
528
|
+
handleDeviceHeartbeat,
|
|
529
|
+
handleDeviceRegister,
|
|
401
530
|
handleSessionAction,
|
|
402
531
|
handleSessions
|
|
403
532
|
};
|