@edge-base/server 0.2.6 → 0.2.7
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/admin-build/_app/immutable/chunks/{BN_-k-Ck.js → BDYewzou.js} +1 -1
- package/admin-build/_app/immutable/chunks/{qBm6xof8.js → BEM1BeVF.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Ff90owjx.js → BYyykAbh.js} +1 -1
- package/admin-build/_app/immutable/chunks/{SQVAC3Cv.js → BaUG2TJ-.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BvoGcDFV.js → BfpUQYr3.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CLHN9MVr.js → BhCO1Fpt.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DemDWbs-.js → CIOC1v_q.js} +3 -3
- package/admin-build/_app/immutable/chunks/{CrwlCAM0.js → CvczjTXx.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DQVP4KC-.js → D1u3u7xu.js} +1 -1
- package/admin-build/_app/immutable/chunks/{LL3ulaxa.js → DaXO-sFP.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CbfX3ELZ.js → DnpbvAPi.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DdvsFblq.js → Dz9cUCuv.js} +1 -1
- package/admin-build/_app/immutable/chunks/{DmDTovpg.js → Tea2dBJ8.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CR37B8DX.js → ejoEf2I5.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CCUxCptE.js → iEyeblJR.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Q3vAxeY-.js → qKdzaeX3.js} +1 -1
- package/admin-build/_app/immutable/entry/{app.CP83Ni80.js → app.DoUaxnew.js} +2 -2
- package/admin-build/_app/immutable/entry/start.MmZh8oBH.js +1 -0
- package/admin-build/_app/immutable/nodes/{0.DiRq7puO.js → 0.Dsxi8s7i.js} +1 -1
- package/admin-build/_app/immutable/nodes/{1.BFeyKLGT.js → 1.Cp2l-hol.js} +1 -1
- package/admin-build/_app/immutable/nodes/{10.zcee7hJx.js → 10.4oY6m8Nz.js} +1 -1
- package/admin-build/_app/immutable/nodes/{11.BW7wLs2Y.js → 11.DfcozD4J.js} +1 -1
- package/admin-build/_app/immutable/nodes/{12.CxJRlYSd.js → 12.uJgZdCIA.js} +1 -1
- package/admin-build/_app/immutable/nodes/{13.pp0F_5hn.js → 13.CaN1kRev.js} +1 -1
- package/admin-build/_app/immutable/nodes/{14.t3AfGiGo.js → 14.DQ5xIi3s.js} +1 -1
- package/admin-build/_app/immutable/nodes/{15.B3agc7NX.js → 15.B_EkebTJ.js} +1 -1
- package/admin-build/_app/immutable/nodes/{16.C4uG2-i8.js → 16.Tko1ZX8-.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.CwGxi1Bn.js → 17.BCmWMJX9.js} +1 -1
- package/admin-build/_app/immutable/nodes/{18.CrQyN_gU.js → 18.hmGhl1O2.js} +1 -1
- package/admin-build/_app/immutable/nodes/{19.NEPUOXl7.js → 19.D-1infOo.js} +1 -1
- package/admin-build/_app/immutable/nodes/{20.DGHO8ipr.js → 20.CY4KKcBL.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.B9lbNUQr.js +1 -0
- package/admin-build/_app/immutable/nodes/{22.Dri5It7a.js → 22.14Vd7bnt.js} +1 -1
- package/admin-build/_app/immutable/nodes/{23.BPQP_Zte.js → 23.Be6jK77o.js} +1 -1
- package/admin-build/_app/immutable/nodes/{24.D580FdSS.js → 24.CSTFkr6R.js} +1 -1
- package/admin-build/_app/immutable/nodes/{25.BMNPOZwF.js → 25.DRTg8fHc.js} +1 -1
- package/admin-build/_app/immutable/nodes/{26.XcpEcbiz.js → 26.DKt-9lwQ.js} +1 -1
- package/admin-build/_app/immutable/nodes/{27.C1zHHcYv.js → 27.D5caPu0F.js} +1 -1
- package/admin-build/_app/immutable/nodes/{28.CuKzzrY8.js → 28.hJhlnlyY.js} +1 -1
- package/admin-build/_app/immutable/nodes/{29.nLpBMXnM.js → 29.CDYBzFyT.js} +1 -1
- package/admin-build/_app/immutable/nodes/{3.5G_aseoL.js → 3.DMyKwkGn.js} +1 -1
- package/admin-build/_app/immutable/nodes/{30.CQC4nLoU.js → 30.BaHNeEmc.js} +1 -1
- package/admin-build/_app/immutable/nodes/{31.Bet8kxOK.js → 31.C6PV5L-2.js} +1 -1
- package/admin-build/_app/immutable/nodes/{4.nmJDYJpC.js → 4.9E118Ftm.js} +1 -1
- package/admin-build/_app/immutable/nodes/{5.CnbYLG4E.js → 5.D8guAl3v.js} +1 -1
- package/admin-build/_app/immutable/nodes/{6.KA01b-3y.js → 6.D1u__DtT.js} +1 -1
- package/admin-build/_app/immutable/nodes/{7.CP9fkn1L.js → 7.DWXHnRFf.js} +1 -1
- package/admin-build/_app/immutable/nodes/{8.BTzDb---.js → 8.Dojd8krc.js} +1 -1
- package/admin-build/_app/immutable/nodes/{9.DkNJg_J6.js → 9.CLtrr0K_.js} +1 -1
- package/admin-build/_app/version.json +1 -1
- package/admin-build/index.html +7 -7
- package/openapi.json +6 -1941
- package/package.json +3 -3
- package/src/__tests__/openapi-coverage.test.ts +0 -6
- package/src/__tests__/room-auth-state-loss.test.ts +6 -0
- package/src/__tests__/room-handler-context.test.ts +0 -31
- package/src/__tests__/room-rate-limit-scopes.test.ts +1 -5
- package/src/__tests__/room-runtime-routing.test.ts +1 -111
- package/src/__tests__/smoke-skip-report.test.ts +1 -1
- package/src/durable-objects/room-runtime-base.ts +241 -17
- package/src/durable-objects/rooms-do.ts +190 -1345
- package/src/lib/openapi.ts +1 -4
- package/src/routes/room.ts +0 -285
- package/src/types.ts +1 -14
- package/admin-build/_app/immutable/entry/start.DY6YakU0.js +0 -1
- package/admin-build/_app/immutable/nodes/21.UVKBDvp4.js +0 -1
- package/src/__tests__/cloudflare-realtime.test.ts +0 -113
- package/src/lib/cloudflare-realtime.ts +0 -251
|
@@ -1,251 +0,0 @@
|
|
|
1
|
-
import type { Env } from '../types.js';
|
|
2
|
-
|
|
3
|
-
const DEFAULT_CLOUDFLARE_REALTIME_BASE_URL = 'https://rtc.live.cloudflare.com/v1';
|
|
4
|
-
|
|
5
|
-
export interface CloudflareRealtimeSessionDescription {
|
|
6
|
-
sdp: string;
|
|
7
|
-
type: 'offer' | 'answer';
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export interface CloudflareRealtimeTrackObject {
|
|
11
|
-
location: 'local' | 'remote';
|
|
12
|
-
mid?: string;
|
|
13
|
-
sessionId?: string;
|
|
14
|
-
trackName?: string;
|
|
15
|
-
bidirectionalMediaStream?: boolean;
|
|
16
|
-
kind?: string;
|
|
17
|
-
simulcast?: {
|
|
18
|
-
preferredRid?: string;
|
|
19
|
-
priorityOrdering?: 'none' | 'asciibetical';
|
|
20
|
-
ridNotAvailable?: 'none' | 'asciibetical';
|
|
21
|
-
};
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export interface CloudflareRealtimeNewSessionRequest {
|
|
25
|
-
sessionDescription?: CloudflareRealtimeSessionDescription;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export interface CloudflareRealtimeNewSessionResponse {
|
|
29
|
-
errorCode?: string;
|
|
30
|
-
errorDescription?: string;
|
|
31
|
-
sessionDescription?: CloudflareRealtimeSessionDescription;
|
|
32
|
-
sessionId: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface CloudflareRealtimeTracksRequest {
|
|
36
|
-
sessionDescription?: CloudflareRealtimeSessionDescription;
|
|
37
|
-
tracks: CloudflareRealtimeTrackObject[];
|
|
38
|
-
autoDiscover?: boolean;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface CloudflareRealtimeTracksResponse {
|
|
42
|
-
errorCode?: string;
|
|
43
|
-
errorDescription?: string;
|
|
44
|
-
requiresImmediateRenegotiation?: boolean;
|
|
45
|
-
sessionDescription?: CloudflareRealtimeSessionDescription;
|
|
46
|
-
tracks?: Array<CloudflareRealtimeTrackObject & {
|
|
47
|
-
errorCode?: string;
|
|
48
|
-
errorDescription?: string;
|
|
49
|
-
}>;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface CloudflareRealtimeRenegotiateRequest {
|
|
53
|
-
sessionDescription: CloudflareRealtimeSessionDescription;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export interface CloudflareRealtimeCloseTracksRequest {
|
|
57
|
-
sessionDescription?: CloudflareRealtimeSessionDescription;
|
|
58
|
-
tracks: Array<{ mid: string }>;
|
|
59
|
-
force?: boolean;
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
export interface CloudflareRealtimeIceServer {
|
|
63
|
-
urls: string[] | string;
|
|
64
|
-
username?: string;
|
|
65
|
-
credential?: string;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export interface CloudflareRealtimeIceServersResponse {
|
|
69
|
-
iceServers: CloudflareRealtimeIceServer[];
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export interface CloudflareRealtimeEnv {
|
|
73
|
-
CF_REALTIME_APP_ID?: string;
|
|
74
|
-
CF_REALTIME_APP_SECRET?: string;
|
|
75
|
-
CF_REALTIME_BASE_URL?: string;
|
|
76
|
-
CF_REALTIME_TURN_KEY_ID?: string;
|
|
77
|
-
CF_REALTIME_TURN_API_TOKEN?: string;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
function trimString(value: unknown): string | undefined {
|
|
81
|
-
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
async function parseRealtimeResponse<T>(response: Response): Promise<T> {
|
|
85
|
-
const data = (await response.json().catch(() => ({}))) as Record<string, unknown>;
|
|
86
|
-
if (!response.ok) {
|
|
87
|
-
const message =
|
|
88
|
-
(typeof data.errorDescription === 'string' && data.errorDescription)
|
|
89
|
-
|| (typeof data.message === 'string' && data.message)
|
|
90
|
-
|| `Cloudflare Realtime request failed (${response.status})`;
|
|
91
|
-
throw new Error(message);
|
|
92
|
-
}
|
|
93
|
-
return data as T;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export function hasCloudflareRealtimeConfig(env: CloudflareRealtimeEnv): boolean {
|
|
97
|
-
return !!trimString(env.CF_REALTIME_APP_ID) && !!trimString(env.CF_REALTIME_APP_SECRET);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export function assertCloudflareRealtimeConfig(env: CloudflareRealtimeEnv): {
|
|
101
|
-
appId: string;
|
|
102
|
-
appSecret: string;
|
|
103
|
-
baseUrl: string;
|
|
104
|
-
turnKeyId?: string;
|
|
105
|
-
turnApiToken?: string;
|
|
106
|
-
} {
|
|
107
|
-
const appId = trimString(env.CF_REALTIME_APP_ID);
|
|
108
|
-
const appSecret = trimString(env.CF_REALTIME_APP_SECRET);
|
|
109
|
-
if (!appId || !appSecret) {
|
|
110
|
-
throw new Error('Cloudflare Realtime is not configured. Set CF_REALTIME_APP_ID and CF_REALTIME_APP_SECRET.');
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
return {
|
|
114
|
-
appId,
|
|
115
|
-
appSecret,
|
|
116
|
-
baseUrl: trimString(env.CF_REALTIME_BASE_URL) ?? DEFAULT_CLOUDFLARE_REALTIME_BASE_URL,
|
|
117
|
-
turnKeyId: trimString(env.CF_REALTIME_TURN_KEY_ID),
|
|
118
|
-
turnApiToken: trimString(env.CF_REALTIME_TURN_API_TOKEN),
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
export class CloudflareRealtimeClient {
|
|
123
|
-
private readonly appId: string;
|
|
124
|
-
private readonly appSecret: string;
|
|
125
|
-
private readonly baseUrl: string;
|
|
126
|
-
private readonly turnKeyId?: string;
|
|
127
|
-
private readonly turnApiToken?: string;
|
|
128
|
-
|
|
129
|
-
constructor(env: CloudflareRealtimeEnv) {
|
|
130
|
-
const config = assertCloudflareRealtimeConfig(env);
|
|
131
|
-
this.appId = config.appId;
|
|
132
|
-
this.appSecret = config.appSecret;
|
|
133
|
-
this.baseUrl = config.baseUrl;
|
|
134
|
-
this.turnKeyId = config.turnKeyId;
|
|
135
|
-
this.turnApiToken = config.turnApiToken;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async createSession(
|
|
139
|
-
body: CloudflareRealtimeNewSessionRequest = {},
|
|
140
|
-
query?: { thirdparty?: boolean; correlationId?: string },
|
|
141
|
-
): Promise<CloudflareRealtimeNewSessionResponse> {
|
|
142
|
-
const url = this.buildSessionUrl('/sessions/new', query);
|
|
143
|
-
const response = await fetch(url, {
|
|
144
|
-
method: 'POST',
|
|
145
|
-
headers: this.buildAuthHeaders(),
|
|
146
|
-
body: JSON.stringify(body),
|
|
147
|
-
});
|
|
148
|
-
return parseRealtimeResponse<CloudflareRealtimeNewSessionResponse>(response);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
async addTracks(
|
|
152
|
-
sessionId: string,
|
|
153
|
-
body: CloudflareRealtimeTracksRequest,
|
|
154
|
-
): Promise<CloudflareRealtimeTracksResponse> {
|
|
155
|
-
const response = await fetch(
|
|
156
|
-
this.buildSessionUrl(`/sessions/${encodeURIComponent(sessionId)}/tracks/new`),
|
|
157
|
-
{
|
|
158
|
-
method: 'POST',
|
|
159
|
-
headers: this.buildAuthHeaders(),
|
|
160
|
-
body: JSON.stringify(body),
|
|
161
|
-
},
|
|
162
|
-
);
|
|
163
|
-
return parseRealtimeResponse<CloudflareRealtimeTracksResponse>(response);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
async renegotiate(
|
|
167
|
-
sessionId: string,
|
|
168
|
-
body: CloudflareRealtimeRenegotiateRequest,
|
|
169
|
-
): Promise<CloudflareRealtimeTracksResponse> {
|
|
170
|
-
const response = await fetch(
|
|
171
|
-
this.buildSessionUrl(`/sessions/${encodeURIComponent(sessionId)}/renegotiate`),
|
|
172
|
-
{
|
|
173
|
-
method: 'PUT',
|
|
174
|
-
headers: this.buildAuthHeaders(),
|
|
175
|
-
body: JSON.stringify(body),
|
|
176
|
-
},
|
|
177
|
-
);
|
|
178
|
-
return parseRealtimeResponse<CloudflareRealtimeTracksResponse>(response);
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
async closeTracks(
|
|
182
|
-
sessionId: string,
|
|
183
|
-
body: CloudflareRealtimeCloseTracksRequest,
|
|
184
|
-
): Promise<CloudflareRealtimeTracksResponse> {
|
|
185
|
-
const response = await fetch(
|
|
186
|
-
this.buildSessionUrl(`/sessions/${encodeURIComponent(sessionId)}/tracks/close`),
|
|
187
|
-
{
|
|
188
|
-
method: 'PUT',
|
|
189
|
-
headers: this.buildAuthHeaders(),
|
|
190
|
-
body: JSON.stringify(body),
|
|
191
|
-
},
|
|
192
|
-
);
|
|
193
|
-
return parseRealtimeResponse<CloudflareRealtimeTracksResponse>(response);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
async getSession(sessionId: string): Promise<{
|
|
197
|
-
tracks?: Array<CloudflareRealtimeTrackObject & { status?: string }>;
|
|
198
|
-
}> {
|
|
199
|
-
const response = await fetch(
|
|
200
|
-
this.buildSessionUrl(`/sessions/${encodeURIComponent(sessionId)}`),
|
|
201
|
-
{
|
|
202
|
-
method: 'GET',
|
|
203
|
-
headers: this.buildAuthHeaders(),
|
|
204
|
-
},
|
|
205
|
-
);
|
|
206
|
-
return parseRealtimeResponse<{ tracks?: Array<CloudflareRealtimeTrackObject & { status?: string }> }>(response);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async generateIceServers(ttl = 3600): Promise<CloudflareRealtimeIceServersResponse> {
|
|
210
|
-
if (!this.turnKeyId || !this.turnApiToken) {
|
|
211
|
-
throw new Error('Cloudflare TURN is not configured. Set CF_REALTIME_TURN_KEY_ID and CF_REALTIME_TURN_API_TOKEN.');
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const response = await fetch(
|
|
215
|
-
`${this.baseUrl.replace(/\/$/, '')}/turn/keys/${encodeURIComponent(this.turnKeyId)}/credentials/generate-ice-servers`,
|
|
216
|
-
{
|
|
217
|
-
method: 'POST',
|
|
218
|
-
headers: {
|
|
219
|
-
Authorization: `Bearer ${this.turnApiToken}`,
|
|
220
|
-
'Content-Type': 'application/json',
|
|
221
|
-
},
|
|
222
|
-
body: JSON.stringify({ ttl }),
|
|
223
|
-
},
|
|
224
|
-
);
|
|
225
|
-
return parseRealtimeResponse<CloudflareRealtimeIceServersResponse>(response);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
private buildSessionUrl(pathname: string, query?: { thirdparty?: boolean; correlationId?: string }): string {
|
|
229
|
-
const url = new URL(
|
|
230
|
-
`${this.baseUrl.replace(/\/$/, '')}/apps/${encodeURIComponent(this.appId)}${pathname}`,
|
|
231
|
-
);
|
|
232
|
-
if (query?.thirdparty !== undefined) {
|
|
233
|
-
url.searchParams.set('thirdparty', String(query.thirdparty));
|
|
234
|
-
}
|
|
235
|
-
if (query?.correlationId) {
|
|
236
|
-
url.searchParams.set('correlationId', query.correlationId);
|
|
237
|
-
}
|
|
238
|
-
return url.toString();
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
private buildAuthHeaders(): HeadersInit {
|
|
242
|
-
return {
|
|
243
|
-
Authorization: `Bearer ${this.appSecret}`,
|
|
244
|
-
'Content-Type': 'application/json',
|
|
245
|
-
};
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
|
|
249
|
-
export function createCloudflareRealtimeClient(env: Env): CloudflareRealtimeClient {
|
|
250
|
-
return new CloudflareRealtimeClient(env);
|
|
251
|
-
}
|