@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.
Files changed (68) hide show
  1. package/admin-build/_app/immutable/chunks/{BN_-k-Ck.js → BDYewzou.js} +1 -1
  2. package/admin-build/_app/immutable/chunks/{qBm6xof8.js → BEM1BeVF.js} +1 -1
  3. package/admin-build/_app/immutable/chunks/{Ff90owjx.js → BYyykAbh.js} +1 -1
  4. package/admin-build/_app/immutable/chunks/{SQVAC3Cv.js → BaUG2TJ-.js} +1 -1
  5. package/admin-build/_app/immutable/chunks/{BvoGcDFV.js → BfpUQYr3.js} +1 -1
  6. package/admin-build/_app/immutable/chunks/{CLHN9MVr.js → BhCO1Fpt.js} +1 -1
  7. package/admin-build/_app/immutable/chunks/{DemDWbs-.js → CIOC1v_q.js} +3 -3
  8. package/admin-build/_app/immutable/chunks/{CrwlCAM0.js → CvczjTXx.js} +1 -1
  9. package/admin-build/_app/immutable/chunks/{DQVP4KC-.js → D1u3u7xu.js} +1 -1
  10. package/admin-build/_app/immutable/chunks/{LL3ulaxa.js → DaXO-sFP.js} +1 -1
  11. package/admin-build/_app/immutable/chunks/{CbfX3ELZ.js → DnpbvAPi.js} +1 -1
  12. package/admin-build/_app/immutable/chunks/{DdvsFblq.js → Dz9cUCuv.js} +1 -1
  13. package/admin-build/_app/immutable/chunks/{DmDTovpg.js → Tea2dBJ8.js} +1 -1
  14. package/admin-build/_app/immutable/chunks/{CR37B8DX.js → ejoEf2I5.js} +1 -1
  15. package/admin-build/_app/immutable/chunks/{CCUxCptE.js → iEyeblJR.js} +1 -1
  16. package/admin-build/_app/immutable/chunks/{Q3vAxeY-.js → qKdzaeX3.js} +1 -1
  17. package/admin-build/_app/immutable/entry/{app.CP83Ni80.js → app.DoUaxnew.js} +2 -2
  18. package/admin-build/_app/immutable/entry/start.MmZh8oBH.js +1 -0
  19. package/admin-build/_app/immutable/nodes/{0.DiRq7puO.js → 0.Dsxi8s7i.js} +1 -1
  20. package/admin-build/_app/immutable/nodes/{1.BFeyKLGT.js → 1.Cp2l-hol.js} +1 -1
  21. package/admin-build/_app/immutable/nodes/{10.zcee7hJx.js → 10.4oY6m8Nz.js} +1 -1
  22. package/admin-build/_app/immutable/nodes/{11.BW7wLs2Y.js → 11.DfcozD4J.js} +1 -1
  23. package/admin-build/_app/immutable/nodes/{12.CxJRlYSd.js → 12.uJgZdCIA.js} +1 -1
  24. package/admin-build/_app/immutable/nodes/{13.pp0F_5hn.js → 13.CaN1kRev.js} +1 -1
  25. package/admin-build/_app/immutable/nodes/{14.t3AfGiGo.js → 14.DQ5xIi3s.js} +1 -1
  26. package/admin-build/_app/immutable/nodes/{15.B3agc7NX.js → 15.B_EkebTJ.js} +1 -1
  27. package/admin-build/_app/immutable/nodes/{16.C4uG2-i8.js → 16.Tko1ZX8-.js} +1 -1
  28. package/admin-build/_app/immutable/nodes/{17.CwGxi1Bn.js → 17.BCmWMJX9.js} +1 -1
  29. package/admin-build/_app/immutable/nodes/{18.CrQyN_gU.js → 18.hmGhl1O2.js} +1 -1
  30. package/admin-build/_app/immutable/nodes/{19.NEPUOXl7.js → 19.D-1infOo.js} +1 -1
  31. package/admin-build/_app/immutable/nodes/{20.DGHO8ipr.js → 20.CY4KKcBL.js} +1 -1
  32. package/admin-build/_app/immutable/nodes/21.B9lbNUQr.js +1 -0
  33. package/admin-build/_app/immutable/nodes/{22.Dri5It7a.js → 22.14Vd7bnt.js} +1 -1
  34. package/admin-build/_app/immutable/nodes/{23.BPQP_Zte.js → 23.Be6jK77o.js} +1 -1
  35. package/admin-build/_app/immutable/nodes/{24.D580FdSS.js → 24.CSTFkr6R.js} +1 -1
  36. package/admin-build/_app/immutable/nodes/{25.BMNPOZwF.js → 25.DRTg8fHc.js} +1 -1
  37. package/admin-build/_app/immutable/nodes/{26.XcpEcbiz.js → 26.DKt-9lwQ.js} +1 -1
  38. package/admin-build/_app/immutable/nodes/{27.C1zHHcYv.js → 27.D5caPu0F.js} +1 -1
  39. package/admin-build/_app/immutable/nodes/{28.CuKzzrY8.js → 28.hJhlnlyY.js} +1 -1
  40. package/admin-build/_app/immutable/nodes/{29.nLpBMXnM.js → 29.CDYBzFyT.js} +1 -1
  41. package/admin-build/_app/immutable/nodes/{3.5G_aseoL.js → 3.DMyKwkGn.js} +1 -1
  42. package/admin-build/_app/immutable/nodes/{30.CQC4nLoU.js → 30.BaHNeEmc.js} +1 -1
  43. package/admin-build/_app/immutable/nodes/{31.Bet8kxOK.js → 31.C6PV5L-2.js} +1 -1
  44. package/admin-build/_app/immutable/nodes/{4.nmJDYJpC.js → 4.9E118Ftm.js} +1 -1
  45. package/admin-build/_app/immutable/nodes/{5.CnbYLG4E.js → 5.D8guAl3v.js} +1 -1
  46. package/admin-build/_app/immutable/nodes/{6.KA01b-3y.js → 6.D1u__DtT.js} +1 -1
  47. package/admin-build/_app/immutable/nodes/{7.CP9fkn1L.js → 7.DWXHnRFf.js} +1 -1
  48. package/admin-build/_app/immutable/nodes/{8.BTzDb---.js → 8.Dojd8krc.js} +1 -1
  49. package/admin-build/_app/immutable/nodes/{9.DkNJg_J6.js → 9.CLtrr0K_.js} +1 -1
  50. package/admin-build/_app/version.json +1 -1
  51. package/admin-build/index.html +7 -7
  52. package/openapi.json +6 -1941
  53. package/package.json +3 -3
  54. package/src/__tests__/openapi-coverage.test.ts +0 -6
  55. package/src/__tests__/room-auth-state-loss.test.ts +6 -0
  56. package/src/__tests__/room-handler-context.test.ts +0 -31
  57. package/src/__tests__/room-rate-limit-scopes.test.ts +1 -5
  58. package/src/__tests__/room-runtime-routing.test.ts +1 -111
  59. package/src/__tests__/smoke-skip-report.test.ts +1 -1
  60. package/src/durable-objects/room-runtime-base.ts +241 -17
  61. package/src/durable-objects/rooms-do.ts +190 -1345
  62. package/src/lib/openapi.ts +1 -4
  63. package/src/routes/room.ts +0 -285
  64. package/src/types.ts +1 -14
  65. package/admin-build/_app/immutable/entry/start.DY6YakU0.js +0 -1
  66. package/admin-build/_app/immutable/nodes/21.UVKBDvp4.js +0 -1
  67. package/src/__tests__/cloudflare-realtime.test.ts +0 -113
  68. package/src/lib/cloudflare-realtime.ts +0 -251
@@ -47,10 +47,7 @@ const USER_BEARER_PATHS = new Set([
47
47
  '/api/push/topic/unsubscribe',
48
48
  ]);
49
49
 
50
- const USER_BEARER_PREFIXES = [
51
- '/api/room/media/realtime/',
52
- '/api/room/media/cloudflare_realtimekit/',
53
- ];
50
+ const USER_BEARER_PREFIXES: string[] = [];
54
51
 
55
52
  const SERVICE_KEY_ONLY_PATHS = new Set([
56
53
  '/api/db/broadcast',
@@ -65,109 +65,6 @@ const roomSummaryCollectionSchema = z.object({
65
65
  deniedIds: z.array(z.string()),
66
66
  updatedAt: z.string(),
67
67
  });
68
- const roomRealtimeSessionDescriptionSchema = z.object({
69
- sdp: z.string().openapi({ description: 'WebRTC session description payload' }),
70
- type: z.enum(['offer', 'answer']).openapi({ description: 'Session description type' }),
71
- });
72
- const roomRealtimeTrackSchema = z.object({
73
- location: z.enum(['local', 'remote']).openapi({ description: 'Track direction relative to the caller' }),
74
- mid: z.string().optional().openapi({ description: 'WebRTC media ID' }),
75
- sessionId: z.string().optional().openapi({ description: 'Provider session ID associated with this track' }),
76
- trackName: z.string().optional().openapi({ description: 'Track name used by the provider' }),
77
- bidirectionalMediaStream: z.boolean().optional().openapi({ description: 'Whether the track should be bidirectional' }),
78
- kind: z.string().optional().openapi({ description: 'Track kind reported by the provider' }),
79
- simulcast: z.object({
80
- preferredRid: z.string().optional(),
81
- priorityOrdering: z.enum(['none', 'asciibetical']).optional(),
82
- ridNotAvailable: z.enum(['none', 'asciibetical']).optional(),
83
- }).optional().openapi({ description: 'Optional simulcast preferences' }),
84
- errorCode: z.string().optional().openapi({ description: 'Provider-level error code for this track' }),
85
- errorDescription: z.string().optional().openapi({ description: 'Provider-level error description for this track' }),
86
- });
87
- const roomRealtimeCreateSessionBodySchema = z.object({
88
- connectionId: z.string().optional().openapi({ description: 'Specific room connection ID to bind the realtime session to' }),
89
- correlationId: z.string().optional().openapi({ description: 'Optional provider correlation ID' }),
90
- thirdparty: z.boolean().optional().openapi({ description: 'Forward Cloudflare Realtime thirdparty mode' }),
91
- sessionDescription: roomRealtimeSessionDescriptionSchema.optional(),
92
- });
93
- const roomCloudflareRealtimeKitCreateSessionBodySchema = z.object({
94
- connectionId: z.string().optional().openapi({ description: 'Specific room connection ID to bind the Cloudflare RealtimeKit participant to' }),
95
- customParticipantId: z.string().optional().openapi({ description: 'Optional custom participant identifier for the provisioned RealtimeKit participant' }),
96
- name: z.string().optional().openapi({ description: 'Optional display name for the provisioned RealtimeKit participant' }),
97
- picture: z.string().optional().openapi({ description: 'Optional avatar URL for the provisioned RealtimeKit participant' }),
98
- });
99
- const roomRealtimeCreateSessionResponseSchema = z.object({
100
- sessionId: z.string().openapi({ description: 'Realtime provider session ID' }),
101
- sessionDescription: roomRealtimeSessionDescriptionSchema.optional(),
102
- errorCode: z.string().optional(),
103
- errorDescription: z.string().optional(),
104
- connectionId: z.string().optional().openapi({ description: 'Room connection ID associated with the session' }),
105
- reused: z.boolean().optional().openapi({ description: 'Whether an existing provider session was reused' }),
106
- });
107
- const roomCloudflareRealtimeKitCreateSessionResponseSchema = z.object({
108
- sessionId: z.string().openapi({ description: 'Cloudflare RealtimeKit participant ID' }),
109
- meetingId: z.string().openapi({ description: 'Cloudflare RealtimeKit meeting ID backing the room session' }),
110
- participantId: z.string().openapi({ description: 'Cloudflare RealtimeKit participant ID' }),
111
- authToken: z.string().openapi({ description: 'RealtimeKit auth token for the provisioned participant' }),
112
- presetName: z.string().optional().openapi({ description: 'RealtimeKit preset used for the provisioned participant' }),
113
- connectionId: z.string().optional().openapi({ description: 'Room connection ID associated with the session' }),
114
- reused: z.boolean().optional().openapi({ description: 'Whether an existing provider participant was reused' }),
115
- });
116
- const roomRealtimeSessionStateSchema = z.object({
117
- sessionId: z.string().openapi({ description: 'Realtime provider session ID' }),
118
- connectionId: z.string().optional().openapi({ description: 'Room connection ID associated with the session' }),
119
- createdAt: z.number().openapi({ description: 'Unix epoch milliseconds when the session was created' }),
120
- updatedAt: z.number().openapi({ description: 'Unix epoch milliseconds when the session was last updated' }),
121
- });
122
- const roomRealtimeIceServerSchema = z.object({
123
- urls: z.union([z.array(z.string()), z.string()]).openapi({ description: 'ICE server URL or URL list' }),
124
- username: z.string().optional(),
125
- credential: z.string().optional(),
126
- });
127
- const roomRealtimeIceServersBodySchema = z.object({
128
- ttl: z.number().optional().openapi({ description: 'Requested TURN credential TTL in seconds' }),
129
- });
130
- const roomRealtimeIceServersResponseSchema = z.object({
131
- iceServers: z.array(roomRealtimeIceServerSchema).openapi({ description: 'ICE servers returned by Cloudflare TURN' }),
132
- });
133
- const roomRealtimeTracksResponseSchema = z.object({
134
- errorCode: z.string().optional(),
135
- errorDescription: z.string().optional(),
136
- requiresImmediateRenegotiation: z.boolean().optional(),
137
- sessionDescription: roomRealtimeSessionDescriptionSchema.optional(),
138
- tracks: z.array(roomRealtimeTrackSchema).optional(),
139
- });
140
- const roomRealtimeTracksBodySchema = z.object({
141
- sessionId: z.string().openapi({ description: 'Realtime provider session ID' }),
142
- connectionId: z.string().optional().openapi({ description: 'Specific room connection ID to bind the track operation to' }),
143
- sessionDescription: roomRealtimeSessionDescriptionSchema.optional(),
144
- tracks: z.array(roomRealtimeTrackSchema).min(1).openapi({ description: 'Tracks to create or subscribe to' }),
145
- autoDiscover: z.boolean().optional().openapi({ description: 'Ask the provider to auto-discover remote tracks' }),
146
- publish: z.object({
147
- kind: z.enum(['audio', 'video', 'screen']).optional(),
148
- trackId: z.string().optional(),
149
- deviceId: z.string().optional(),
150
- muted: z.boolean().optional(),
151
- }).optional().openapi({ description: 'Optional room media state updates to apply after track creation' }),
152
- });
153
- const roomRealtimeRenegotiateBodySchema = z.object({
154
- sessionId: z.string().openapi({ description: 'Realtime provider session ID' }),
155
- connectionId: z.string().optional().openapi({ description: 'Specific room connection ID to bind the renegotiation to' }),
156
- sessionDescription: roomRealtimeSessionDescriptionSchema,
157
- });
158
- const roomRealtimeCloseTracksBodySchema = z.object({
159
- sessionId: z.string().openapi({ description: 'Realtime provider session ID' }),
160
- connectionId: z.string().optional().openapi({ description: 'Specific room connection ID to bind the close operation to' }),
161
- sessionDescription: roomRealtimeSessionDescriptionSchema.optional(),
162
- tracks: z.array(z.object({
163
- mid: z.string().openapi({ description: 'Track MID to close' }),
164
- })).min(1).openapi({ description: 'Tracks to close' }),
165
- force: z.boolean().optional().openapi({ description: 'Force close even if the provider reports the track as active' }),
166
- unpublish: z.object({
167
- kind: z.enum(['audio', 'video', 'screen']).optional(),
168
- }).optional().openapi({ description: 'Optional room media state cleanup after closing tracks' }),
169
- });
170
-
171
68
  function isRoomOperationPublic(
172
69
  namespaceConfig: RoomNamespaceConfig | null | undefined,
173
70
  operation: 'metadata' | 'join' | 'action',
@@ -705,185 +602,3 @@ roomRoute.openapi(getRoomSummaries, async (c) => {
705
602
  updatedAt: new Date().toISOString(),
706
603
  });
707
604
  });
708
-
709
- const getRoomRealtimeSession = createRoute({
710
- operationId: 'getRoomRealtimeSession',
711
- method: 'get',
712
- path: '/media/realtime/session',
713
- tags: ['client'],
714
- summary: 'Get the active room realtime media session',
715
- description: 'Returns the provider session currently bound to the authenticated room member.',
716
- request: {
717
- query: roomQuerySchema.extend({
718
- connectionId: z.string().optional().openapi({ description: 'Optional room connection ID override' }),
719
- }),
720
- },
721
- responses: {
722
- 200: { description: 'Active room realtime session', content: { 'application/json': { schema: roomRealtimeSessionStateSchema } } },
723
- 400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
724
- 401: { description: 'Authentication required', content: { 'application/json': { schema: errorResponseSchema } } },
725
- 403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
726
- 404: { description: 'No active session or runtime not found', content: { 'application/json': { schema: errorResponseSchema } } },
727
- },
728
- });
729
-
730
- const createRoomRealtimeSession = createRoute({
731
- operationId: 'createRoomRealtimeSession',
732
- method: 'post',
733
- path: '/media/realtime/session',
734
- tags: ['client'],
735
- summary: 'Create a room realtime media session',
736
- description: 'Creates a Cloudflare Realtime session for the authenticated room member.',
737
- request: {
738
- query: roomQuerySchema,
739
- body: { content: { 'application/json': { schema: roomRealtimeCreateSessionBodySchema } }, required: false },
740
- },
741
- responses: {
742
- 200: { description: 'Realtime session created', content: { 'application/json': { schema: roomRealtimeCreateSessionResponseSchema } } },
743
- 400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
744
- 401: { description: 'Authentication required', content: { 'application/json': { schema: errorResponseSchema } } },
745
- 403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
746
- 404: { description: 'Room runtime not found', content: { 'application/json': { schema: errorResponseSchema } } },
747
- 409: { description: 'Conflicting existing published media', content: { 'application/json': { schema: errorResponseSchema } } },
748
- },
749
- });
750
-
751
- const createRoomCloudflareRealtimeKitSession = createRoute({
752
- operationId: 'createRoomCloudflareRealtimeKitSession',
753
- method: 'post',
754
- path: '/media/cloudflare_realtimekit/session',
755
- tags: ['client'],
756
- summary: 'Create a room Cloudflare RealtimeKit session',
757
- description: 'Creates a Cloudflare RealtimeKit session for the authenticated room member.',
758
- request: {
759
- query: roomQuerySchema,
760
- body: { content: { 'application/json': { schema: roomCloudflareRealtimeKitCreateSessionBodySchema } }, required: false },
761
- },
762
- responses: {
763
- 200: { description: 'Cloudflare RealtimeKit session created', content: { 'application/json': { schema: roomCloudflareRealtimeKitCreateSessionResponseSchema } } },
764
- 400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
765
- 401: { description: 'Authentication required', content: { 'application/json': { schema: errorResponseSchema } } },
766
- 403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
767
- 404: { description: 'Room runtime not found', content: { 'application/json': { schema: errorResponseSchema } } },
768
- 409: { description: 'Conflicting existing published media', content: { 'application/json': { schema: errorResponseSchema } } },
769
- },
770
- });
771
-
772
- const createRoomRealtimeIceServers = createRoute({
773
- operationId: 'createRoomRealtimeIceServers',
774
- method: 'post',
775
- path: '/media/realtime/turn',
776
- tags: ['client'],
777
- summary: 'Generate TURN / ICE credentials for room realtime media',
778
- description: 'Generates ICE server credentials for the authenticated room member.',
779
- request: {
780
- query: roomQuerySchema,
781
- body: { content: { 'application/json': { schema: roomRealtimeIceServersBodySchema } }, required: false },
782
- },
783
- responses: {
784
- 200: { description: 'ICE servers generated', content: { 'application/json': { schema: roomRealtimeIceServersResponseSchema } } },
785
- 400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
786
- 401: { description: 'Authentication required', content: { 'application/json': { schema: errorResponseSchema } } },
787
- 403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
788
- 404: { description: 'Room runtime not found', content: { 'application/json': { schema: errorResponseSchema } } },
789
- },
790
- });
791
-
792
- const addRoomRealtimeTracks = createRoute({
793
- operationId: 'addRoomRealtimeTracks',
794
- method: 'post',
795
- path: '/media/realtime/tracks/new',
796
- tags: ['client'],
797
- summary: 'Add realtime media tracks to a room session',
798
- description: 'Creates or subscribes realtime tracks for the authenticated room member.',
799
- request: {
800
- query: roomQuerySchema,
801
- body: { content: { 'application/json': { schema: roomRealtimeTracksBodySchema } }, required: true },
802
- },
803
- responses: {
804
- 200: { description: 'Realtime tracks updated', content: { 'application/json': { schema: roomRealtimeTracksResponseSchema } } },
805
- 400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
806
- 401: { description: 'Authentication required', content: { 'application/json': { schema: errorResponseSchema } } },
807
- 403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
808
- 404: { description: 'Room runtime not found', content: { 'application/json': { schema: errorResponseSchema } } },
809
- },
810
- });
811
-
812
- const renegotiateRoomRealtimeSession = createRoute({
813
- operationId: 'renegotiateRoomRealtimeSession',
814
- method: 'put',
815
- path: '/media/realtime/renegotiate',
816
- tags: ['client'],
817
- summary: 'Renegotiate a room realtime media session',
818
- description: 'Submits a new session description for an existing room realtime media session.',
819
- request: {
820
- query: roomQuerySchema,
821
- body: { content: { 'application/json': { schema: roomRealtimeRenegotiateBodySchema } }, required: true },
822
- },
823
- responses: {
824
- 200: { description: 'Realtime session renegotiated', content: { 'application/json': { schema: roomRealtimeTracksResponseSchema } } },
825
- 400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
826
- 401: { description: 'Authentication required', content: { 'application/json': { schema: errorResponseSchema } } },
827
- 403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
828
- 404: { description: 'Room runtime not found', content: { 'application/json': { schema: errorResponseSchema } } },
829
- },
830
- });
831
-
832
- const closeRoomRealtimeTracks = createRoute({
833
- operationId: 'closeRoomRealtimeTracks',
834
- method: 'put',
835
- path: '/media/realtime/tracks/close',
836
- tags: ['client'],
837
- summary: 'Close room realtime media tracks',
838
- description: 'Closes provider tracks for the authenticated room member and optionally unpublishes room media state.',
839
- request: {
840
- query: roomQuerySchema,
841
- body: { content: { 'application/json': { schema: roomRealtimeCloseTracksBodySchema } }, required: true },
842
- },
843
- responses: {
844
- 200: { description: 'Realtime tracks closed', content: { 'application/json': { schema: roomRealtimeTracksResponseSchema } } },
845
- 400: { description: 'Bad request', content: { 'application/json': { schema: errorResponseSchema } } },
846
- 401: { description: 'Authentication required', content: { 'application/json': { schema: errorResponseSchema } } },
847
- 403: { description: 'Forbidden', content: { 'application/json': { schema: errorResponseSchema } } },
848
- 404: { description: 'Room runtime not found', content: { 'application/json': { schema: errorResponseSchema } } },
849
- },
850
- });
851
-
852
- roomRoute.openapi(getRoomRealtimeSession, async (c) =>
853
- proxyRoomDoRequest(c, '/media/realtime/session', 'GET', { requireAuth: true }));
854
-
855
- roomRoute.openapi(createRoomRealtimeSession, async (c) =>
856
- proxyRoomDoRequest(c, '/media/realtime/session', 'POST', {
857
- requireAuth: true,
858
- validatedJson: c.req.valid('json'),
859
- }));
860
-
861
- roomRoute.openapi(createRoomRealtimeIceServers, async (c) =>
862
- proxyRoomDoRequest(c, '/media/realtime/turn', 'POST', {
863
- requireAuth: true,
864
- validatedJson: c.req.valid('json'),
865
- }));
866
-
867
- roomRoute.openapi(addRoomRealtimeTracks, async (c) =>
868
- proxyRoomDoRequest(c, '/media/realtime/tracks/new', 'POST', {
869
- requireAuth: true,
870
- validatedJson: c.req.valid('json'),
871
- }));
872
-
873
- roomRoute.openapi(renegotiateRoomRealtimeSession, async (c) =>
874
- proxyRoomDoRequest(c, '/media/realtime/renegotiate', 'PUT', {
875
- requireAuth: true,
876
- validatedJson: c.req.valid('json'),
877
- }));
878
-
879
- roomRoute.openapi(closeRoomRealtimeTracks, async (c) =>
880
- proxyRoomDoRequest(c, '/media/realtime/tracks/close', 'PUT', {
881
- requireAuth: true,
882
- validatedJson: c.req.valid('json'),
883
- }));
884
-
885
- roomRoute.openapi(createRoomCloudflareRealtimeKitSession, async (c) =>
886
- proxyRoomDoRequest(c, '/media/cloudflare_realtimekit/session', 'POST', {
887
- requireAuth: true,
888
- validatedJson: c.req.valid('json'),
889
- }));
package/src/types.ts CHANGED
@@ -7,7 +7,7 @@ export interface Env {
7
7
  DATABASE: DurableObjectNamespace;
8
8
  AUTH: DurableObjectNamespace;
9
9
  DATABASE_LIVE: DurableObjectNamespace;
10
- /** Room DO — per-room state synchronization, members, signals, media */
10
+ /** Room DO — per-room state synchronization, members, and signals */
11
11
  ROOMS: DurableObjectNamespace;
12
12
 
13
13
  // ─── R2 Storage ───
@@ -66,19 +66,6 @@ export interface Env {
66
66
  TURNSTILE_SECRET?: string;
67
67
  /** Turnstile site key — public, returned to clients via GET /api/config (§34) */
68
68
  CAPTCHA_SITE_KEY?: string;
69
- /** Cloudflare Realtime app ID for SFU session control. */
70
- CF_REALTIME_APP_ID?: string;
71
- /** Cloudflare RealtimeKit preset name used when creating participant tokens. */
72
- CF_REALTIME_PRESET_NAME?: string;
73
- /** Cloudflare Realtime app secret for SFU session control. */
74
- CF_REALTIME_APP_SECRET?: string;
75
- /** Optional override for the Cloudflare Realtime API base URL. */
76
- CF_REALTIME_BASE_URL?: string;
77
- /** Cloudflare TURN key ID used to mint short-lived ICE credentials. */
78
- CF_REALTIME_TURN_KEY_ID?: string;
79
- /** Cloudflare TURN API token returned when the TURN key is created. */
80
- CF_REALTIME_TURN_API_TOKEN?: string;
81
-
82
69
  // ─── Environment Identification ───
83
70
  /** Server environment name for Service Key constraints.env evaluation */
84
71
  ENVIRONMENT?: string;
@@ -1 +0,0 @@
1
- import{a as r}from"../chunks/SQVAC3Cv.js";import{w as t}from"../chunks/Q3vAxeY-.js";export{t as load_css,r as start};
@@ -1 +0,0 @@
1
- import{_ as m}from"../chunks/DemDWbs-.js";export{m as component};
@@ -1,113 +0,0 @@
1
- import { afterEach, describe, expect, it, vi } from 'vitest';
2
- import {
3
- CloudflareRealtimeClient,
4
- assertCloudflareRealtimeConfig,
5
- createCloudflareRealtimeClient,
6
- hasCloudflareRealtimeConfig,
7
- } from '../lib/cloudflare-realtime.js';
8
-
9
- describe('cloudflare realtime helpers', () => {
10
- afterEach(() => {
11
- vi.restoreAllMocks();
12
- vi.unstubAllGlobals();
13
- });
14
-
15
- it('detects whether the required realtime config is present', () => {
16
- expect(hasCloudflareRealtimeConfig({})).toBe(false);
17
- expect(hasCloudflareRealtimeConfig({
18
- CF_REALTIME_APP_ID: ' app-123 ',
19
- CF_REALTIME_APP_SECRET: ' secret-456 ',
20
- })).toBe(true);
21
- });
22
-
23
- it('normalizes realtime config and defaults the base URL', () => {
24
- expect(assertCloudflareRealtimeConfig({
25
- CF_REALTIME_APP_ID: ' app-123 ',
26
- CF_REALTIME_APP_SECRET: ' secret-456 ',
27
- CF_REALTIME_TURN_KEY_ID: ' key-1 ',
28
- CF_REALTIME_TURN_API_TOKEN: ' token-1 ',
29
- })).toEqual({
30
- appId: 'app-123',
31
- appSecret: 'secret-456',
32
- baseUrl: 'https://rtc.live.cloudflare.com/v1',
33
- turnKeyId: 'key-1',
34
- turnApiToken: 'token-1',
35
- });
36
-
37
- expect(() => assertCloudflareRealtimeConfig({
38
- CF_REALTIME_APP_ID: 'missing-secret',
39
- })).toThrow('Cloudflare Realtime is not configured');
40
- });
41
-
42
- it('creates a realtime client and sends authenticated session requests', async () => {
43
- const fetchMock = vi.fn(async (_input: RequestInfo | URL, _init?: RequestInit) =>
44
- new Response(JSON.stringify({ sessionId: 'sess-1' }), {
45
- status: 200,
46
- headers: { 'Content-Type': 'application/json' },
47
- }));
48
- vi.stubGlobal('fetch', fetchMock);
49
-
50
- const client = createCloudflareRealtimeClient({
51
- CF_REALTIME_APP_ID: 'app-123',
52
- CF_REALTIME_APP_SECRET: 'secret-456',
53
- } as never);
54
-
55
- expect(client).toBeInstanceOf(CloudflareRealtimeClient);
56
- await expect(client.createSession({
57
- sessionDescription: {
58
- sdp: 'offer-sdp',
59
- type: 'offer',
60
- },
61
- }, {
62
- thirdparty: true,
63
- correlationId: 'corr-1',
64
- })).resolves.toEqual({ sessionId: 'sess-1' });
65
-
66
- expect(fetchMock).toHaveBeenCalledTimes(1);
67
- expect(fetchMock.mock.calls[0]?.[0]).toBe(
68
- 'https://rtc.live.cloudflare.com/v1/apps/app-123/sessions/new?thirdparty=true&correlationId=corr-1',
69
- );
70
- expect(fetchMock.mock.calls[0]?.[1]).toMatchObject({
71
- method: 'POST',
72
- headers: {
73
- Authorization: 'Bearer secret-456',
74
- 'Content-Type': 'application/json',
75
- },
76
- });
77
- });
78
-
79
- it('uses TURN credentials when generating ICE servers', async () => {
80
- const fetchMock = vi.fn(async () =>
81
- new Response(JSON.stringify({
82
- iceServers: [{ urls: 'turn:global.example.com', username: 'user', credential: 'pass' }],
83
- }), {
84
- status: 200,
85
- headers: { 'Content-Type': 'application/json' },
86
- }));
87
- vi.stubGlobal('fetch', fetchMock);
88
-
89
- const client = new CloudflareRealtimeClient({
90
- CF_REALTIME_APP_ID: 'app-123',
91
- CF_REALTIME_APP_SECRET: 'secret-456',
92
- CF_REALTIME_BASE_URL: 'https://rtc.example.com/base/',
93
- CF_REALTIME_TURN_KEY_ID: 'turn-key',
94
- CF_REALTIME_TURN_API_TOKEN: 'turn-token',
95
- });
96
-
97
- await expect(client.generateIceServers(120)).resolves.toEqual({
98
- iceServers: [{ urls: 'turn:global.example.com', username: 'user', credential: 'pass' }],
99
- });
100
-
101
- expect(fetchMock).toHaveBeenCalledWith(
102
- 'https://rtc.example.com/base/turn/keys/turn-key/credentials/generate-ice-servers',
103
- {
104
- method: 'POST',
105
- headers: {
106
- Authorization: 'Bearer turn-token',
107
- 'Content-Type': 'application/json',
108
- },
109
- body: JSON.stringify({ ttl: 120 }),
110
- },
111
- );
112
- });
113
- });