@edge-base/server 0.2.5 → 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/{DILS_-VJ.js → B3CvhH3c.js} +1 -1
- package/admin-build/_app/immutable/chunks/BDYewzou.js +1 -0
- package/admin-build/_app/immutable/chunks/{Cdm5zBRA.js → BEM1BeVF.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Dt4vL4Df.js → BYL_uBga.js} +1 -1
- package/admin-build/_app/immutable/chunks/{B94PilAN.js → BYyykAbh.js} +1 -1
- package/admin-build/_app/immutable/chunks/BaUG2TJ-.js +1 -0
- package/admin-build/_app/immutable/chunks/{C72lTcG0.js → Bcs4KYNp.js} +1 -1
- package/admin-build/_app/immutable/chunks/{D2j3I1VQ.js → BfpUQYr3.js} +1 -1
- package/admin-build/_app/immutable/chunks/BhCO1Fpt.js +1 -0
- package/admin-build/_app/immutable/chunks/{B8s_s9QY.js → BkZCgsc3.js} +1 -1
- package/admin-build/_app/immutable/chunks/CIOC1v_q.js +128 -0
- package/admin-build/_app/immutable/chunks/CjcrXziO.js +2 -0
- package/admin-build/_app/immutable/chunks/CvczjTXx.js +1 -0
- package/admin-build/_app/immutable/chunks/D1u3u7xu.js +1 -0
- package/admin-build/_app/immutable/chunks/{B0HRJ657.js → DOOPbWwG.js} +1 -1
- package/admin-build/_app/immutable/chunks/{BqTb6Mxk.js → DaXO-sFP.js} +1 -1
- package/admin-build/_app/immutable/chunks/DnpbvAPi.js +1 -0
- package/admin-build/_app/immutable/chunks/{B6MschND.js → Dz9cUCuv.js} +1 -1
- package/admin-build/_app/immutable/chunks/{CaVKAiCe.js → Tea2dBJ8.js} +1 -1
- package/admin-build/_app/immutable/chunks/{Z41NK6i6.js → bguI1TeA.js} +1 -1
- package/admin-build/_app/immutable/chunks/{J2Gw0SMu.js → ejoEf2I5.js} +1 -1
- package/admin-build/_app/immutable/chunks/{B2TnDKF7.js → iEyeblJR.js} +1 -1
- package/admin-build/_app/immutable/chunks/{_teD5ji5.js → nlAMTi52.js} +1 -1
- package/admin-build/_app/immutable/chunks/qKdzaeX3.js +1 -0
- package/admin-build/_app/immutable/entry/{app.D3flihMw.js → app.DoUaxnew.js} +2 -2
- package/admin-build/_app/immutable/entry/start.MmZh8oBH.js +1 -0
- package/admin-build/_app/immutable/nodes/{0.CdczqZLK.js → 0.Dsxi8s7i.js} +1 -1
- package/admin-build/_app/immutable/nodes/1.Cp2l-hol.js +1 -0
- package/admin-build/_app/immutable/nodes/10.4oY6m8Nz.js +1 -0
- package/admin-build/_app/immutable/nodes/11.DfcozD4J.js +1 -0
- package/admin-build/_app/immutable/nodes/12.uJgZdCIA.js +1 -0
- package/admin-build/_app/immutable/nodes/13.CaN1kRev.js +110 -0
- package/admin-build/_app/immutable/nodes/14.DQ5xIi3s.js +3 -0
- package/admin-build/_app/immutable/nodes/15.B_EkebTJ.js +1 -0
- package/admin-build/_app/immutable/nodes/{16.BR7WwQrS.js → 16.Tko1ZX8-.js} +1 -1
- package/admin-build/_app/immutable/nodes/{17.Cm57KKXV.js → 17.BCmWMJX9.js} +1 -1
- package/admin-build/_app/immutable/nodes/18.hmGhl1O2.js +1 -0
- package/admin-build/_app/immutable/nodes/19.D-1infOo.js +2 -0
- package/admin-build/_app/immutable/nodes/{20.DnHeFlTv.js → 20.CY4KKcBL.js} +1 -1
- package/admin-build/_app/immutable/nodes/21.B9lbNUQr.js +1 -0
- package/admin-build/_app/immutable/nodes/22.14Vd7bnt.js +1 -0
- package/admin-build/_app/immutable/nodes/{23.CWSGMcKJ.js → 23.Be6jK77o.js} +2 -2
- package/admin-build/_app/immutable/nodes/24.CSTFkr6R.js +2 -0
- package/admin-build/_app/immutable/nodes/25.DRTg8fHc.js +2 -0
- package/admin-build/_app/immutable/nodes/26.DKt-9lwQ.js +1 -0
- package/admin-build/_app/immutable/nodes/27.D5caPu0F.js +1 -0
- package/admin-build/_app/immutable/nodes/28.hJhlnlyY.js +1 -0
- package/admin-build/_app/immutable/nodes/29.CDYBzFyT.js +1 -0
- package/admin-build/_app/immutable/nodes/{3.B6q-7qr8.js → 3.DMyKwkGn.js} +1 -1
- package/admin-build/_app/immutable/nodes/30.BaHNeEmc.js +1 -0
- package/admin-build/_app/immutable/nodes/31.C6PV5L-2.js +1 -0
- package/admin-build/_app/immutable/nodes/4.9E118Ftm.js +1 -0
- package/admin-build/_app/immutable/nodes/5.D8guAl3v.js +1 -0
- package/admin-build/_app/immutable/nodes/6.D1u__DtT.js +1 -0
- package/admin-build/_app/immutable/nodes/7.DWXHnRFf.js +1 -0
- package/admin-build/_app/immutable/nodes/8.Dojd8krc.js +1 -0
- package/admin-build/_app/immutable/nodes/9.CLtrr0K_.js +1 -0
- 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__/push-handlers.test.ts +1 -1
- 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 +24 -111
- package/src/__tests__/route-parser.test.ts +6 -0
- package/src/__tests__/schema.test.ts +15 -6
- package/src/__tests__/smoke-skip-report.test.ts +1 -1
- package/src/durable-objects/database-do.ts +7 -1
- package/src/durable-objects/room-runtime-base.ts +290 -57
- package/src/durable-objects/rooms-do.ts +212 -1336
- package/src/index.ts +23 -9
- package/src/lib/d1-handler.ts +32 -17
- package/src/lib/openapi.ts +1 -4
- package/src/lib/postgres-handler.ts +24 -12
- package/src/lib/route-parser.ts +3 -0
- package/src/lib/schemas.ts +12 -2
- package/src/middleware/captcha-verify.ts +16 -3
- package/src/middleware/error-handler.ts +1 -1
- package/src/middleware/rules.ts +28 -9
- package/src/routes/admin-auth.ts +3 -3
- package/src/routes/admin.ts +13 -8
- package/src/routes/analytics-api.ts +3 -3
- package/src/routes/auth.ts +1 -1
- package/src/routes/backup.ts +1 -1
- package/src/routes/d1.ts +14 -7
- package/src/routes/database-live.ts +13 -6
- package/src/routes/kv.ts +21 -10
- package/src/routes/oauth.ts +1 -1
- package/src/routes/push.ts +119 -77
- package/src/routes/room.ts +203 -280
- package/src/routes/schema-endpoint.ts +2 -2
- package/src/routes/sql.ts +10 -6
- package/src/routes/storage.ts +4 -2
- package/src/routes/vectorize.ts +16 -4
- package/src/types.ts +1 -14
- package/admin-build/_app/immutable/chunks/6oMK_164.js +0 -1
- package/admin-build/_app/immutable/chunks/BEW7Ez_g.js +0 -1
- package/admin-build/_app/immutable/chunks/BoOooyH6.js +0 -1
- package/admin-build/_app/immutable/chunks/BvHnF5tV.js +0 -1
- package/admin-build/_app/immutable/chunks/CoI6jjbg.js +0 -2
- package/admin-build/_app/immutable/chunks/CrOZMmdF.js +0 -1
- package/admin-build/_app/immutable/chunks/Cw6OYcq-.js +0 -1
- package/admin-build/_app/immutable/chunks/DPdQ7z0T.js +0 -128
- package/admin-build/_app/immutable/chunks/pUxw8jfq.js +0 -1
- package/admin-build/_app/immutable/entry/start.Cl6sLxnz.js +0 -1
- package/admin-build/_app/immutable/nodes/1.DxcSsEqS.js +0 -1
- package/admin-build/_app/immutable/nodes/10.DuAd4aIm.js +0 -1
- package/admin-build/_app/immutable/nodes/11.0jgHQL92.js +0 -1
- package/admin-build/_app/immutable/nodes/12.CKNPqmyy.js +0 -1
- package/admin-build/_app/immutable/nodes/13.B1p2POXS.js +0 -110
- package/admin-build/_app/immutable/nodes/14.Bb-REBND.js +0 -3
- package/admin-build/_app/immutable/nodes/15.1uBFCX0X.js +0 -1
- package/admin-build/_app/immutable/nodes/18.CoiwfAuQ.js +0 -1
- package/admin-build/_app/immutable/nodes/19.B8ZdLlXj.js +0 -2
- package/admin-build/_app/immutable/nodes/21.CJFaf0Ia.js +0 -1
- package/admin-build/_app/immutable/nodes/22.CItETFzy.js +0 -1
- package/admin-build/_app/immutable/nodes/24.CWbEqNMB.js +0 -2
- package/admin-build/_app/immutable/nodes/25.DRkLEhKi.js +0 -2
- package/admin-build/_app/immutable/nodes/26.BRxO8AYH.js +0 -1
- package/admin-build/_app/immutable/nodes/27.BLs-nVHz.js +0 -1
- package/admin-build/_app/immutable/nodes/28.G79qkdBK.js +0 -1
- package/admin-build/_app/immutable/nodes/29.BOcI6g0N.js +0 -1
- package/admin-build/_app/immutable/nodes/30.DAIC7dKd.js +0 -1
- package/admin-build/_app/immutable/nodes/31.pl0XXjXF.js +0 -1
- package/admin-build/_app/immutable/nodes/4.DOdvVlZj.js +0 -1
- package/admin-build/_app/immutable/nodes/5.BW_zlgye.js +0 -1
- package/admin-build/_app/immutable/nodes/6.Dxy1CAI2.js +0 -1
- package/admin-build/_app/immutable/nodes/7.BG98w_o7.js +0 -1
- package/admin-build/_app/immutable/nodes/8.DoG5R2rG.js +0 -1
- package/admin-build/_app/immutable/nodes/9.Dmxf6zAC.js +0 -1
- package/src/__tests__/cloudflare-realtime.test.ts +0 -113
- package/src/lib/cloudflare-realtime.ts +0 -251
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@edge-base/server",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.7",
|
|
4
4
|
"description": "EdgeBase runtime assets consumed by the EdgeBase CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"jose": "^6.0.0",
|
|
35
35
|
"pg": "^8.16.3",
|
|
36
36
|
"zod": "^4.3.6",
|
|
37
|
-
"@edge-base/core": "0.2.
|
|
38
|
-
"@edge-base/shared": "0.2.
|
|
37
|
+
"@edge-base/core": "0.2.7",
|
|
38
|
+
"@edge-base/shared": "0.2.7"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@cloudflare/vitest-pool-workers": "^0.8.71",
|
|
@@ -118,8 +118,6 @@ describe('OpenAPI route coverage', () => {
|
|
|
118
118
|
'/api/sql': { post: {} },
|
|
119
119
|
'/admin/api/setup': { get: {} },
|
|
120
120
|
'/admin/api/data/users': { get: {} },
|
|
121
|
-
'/api/room/media/realtime/session': { post: {} },
|
|
122
|
-
'/api/room/media/cloudflare_realtimekit/session': { post: {} },
|
|
123
121
|
},
|
|
124
122
|
};
|
|
125
123
|
|
|
@@ -134,10 +132,6 @@ describe('OpenAPI route coverage', () => {
|
|
|
134
132
|
expect(schemes).toHaveProperty('serviceKeyAuth');
|
|
135
133
|
expect((normalized.paths?.['/api/auth/me'] as Record<string, { security?: unknown }>).get.security)
|
|
136
134
|
.toEqual([{ userBearerAuth: [] }]);
|
|
137
|
-
expect((normalized.paths?.['/api/room/media/realtime/session'] as Record<string, { security?: unknown }>).post.security)
|
|
138
|
-
.toEqual([{ userBearerAuth: [] }]);
|
|
139
|
-
expect((normalized.paths?.['/api/room/media/cloudflare_realtimekit/session'] as Record<string, { security?: unknown }>).post.security)
|
|
140
|
-
.toEqual([{ userBearerAuth: [] }]);
|
|
141
135
|
expect((normalized.paths?.['/api/sql'] as Record<string, { security?: unknown }>).post.security)
|
|
142
136
|
.toEqual([{ serviceKeyAuth: [] }]);
|
|
143
137
|
expect((normalized.paths?.['/admin/api/setup'] as Record<string, { security?: unknown }>).get.security)
|
|
@@ -173,7 +173,7 @@ describe('Push handlers route integration', () => {
|
|
|
173
173
|
|
|
174
174
|
expect(response.status).toBe(400);
|
|
175
175
|
await expect(response.json()).resolves.toMatchObject({
|
|
176
|
-
message: 'beforeSend must return topic and payload',
|
|
176
|
+
message: 'push.hooks.beforeSend must return a topic and payload when overriding topic delivery.',
|
|
177
177
|
});
|
|
178
178
|
});
|
|
179
179
|
});
|
|
@@ -53,6 +53,7 @@ describe('room auth-state loss recovery', () => {
|
|
|
53
53
|
room._stateSaveAt = 33_333;
|
|
54
54
|
room._emptyRoomCleanupAt = 44_444;
|
|
55
55
|
room._stateTTLAlarmAt = 55_555;
|
|
56
|
+
room._socketHeartbeatCheckAt = 66_666;
|
|
56
57
|
room.ctx = {
|
|
57
58
|
storage: {
|
|
58
59
|
put: putSpy,
|
|
@@ -72,6 +73,7 @@ describe('room auth-state loss recovery', () => {
|
|
|
72
73
|
stateSaveAt: 33_333,
|
|
73
74
|
emptyRoomCleanupAt: 44_444,
|
|
74
75
|
stateTTLAlarmAt: 55_555,
|
|
76
|
+
socketHeartbeatCheckAt: 66_666,
|
|
75
77
|
});
|
|
76
78
|
});
|
|
77
79
|
|
|
@@ -112,6 +114,7 @@ describe('room auth-state loss recovery', () => {
|
|
|
112
114
|
room._stateTTLAlarmAt = null;
|
|
113
115
|
room._metadata = {};
|
|
114
116
|
room.config = {};
|
|
117
|
+
room.env = {};
|
|
115
118
|
room.ctx = {
|
|
116
119
|
getWebSockets: vi.fn(() => []),
|
|
117
120
|
};
|
|
@@ -132,6 +135,7 @@ describe('room auth-state loss recovery', () => {
|
|
|
132
135
|
|
|
133
136
|
const room: any = Object.create(RoomRuntimeBaseDO.prototype);
|
|
134
137
|
room._metaCache = new Map();
|
|
138
|
+
room._attachmentExtraCache = new Map();
|
|
135
139
|
room.ctx = {
|
|
136
140
|
getTags: vi.fn(() => [
|
|
137
141
|
'conn:conn-1',
|
|
@@ -171,6 +175,7 @@ describe('room auth-state loss recovery', () => {
|
|
|
171
175
|
authStateLost: false,
|
|
172
176
|
connectionId: 'conn-1',
|
|
173
177
|
}]]);
|
|
178
|
+
room._attachmentExtraCache = new Map();
|
|
174
179
|
room.safeSend = vi.fn();
|
|
175
180
|
|
|
176
181
|
await room.webSocketMessage(ws, JSON.stringify({ type: 'ping' }));
|
|
@@ -193,6 +198,7 @@ describe('room auth-state loss recovery', () => {
|
|
|
193
198
|
authStateLost: true,
|
|
194
199
|
connectionId: 'conn-1',
|
|
195
200
|
}]]);
|
|
201
|
+
room._attachmentExtraCache = new Map();
|
|
196
202
|
room.safeSend = vi.fn();
|
|
197
203
|
|
|
198
204
|
await room.webSocketMessage(ws, JSON.stringify({ type: 'ping' }));
|
|
@@ -127,35 +127,4 @@ describe('RoomsDO handler context', () => {
|
|
|
127
127
|
}),
|
|
128
128
|
);
|
|
129
129
|
}, 15_000);
|
|
130
|
-
|
|
131
|
-
it('returns 409 when creating a Cloudflare RealtimeKit session while media is already published', async () => {
|
|
132
|
-
const { RoomsDO } = await import('../durable-objects/rooms-do.js');
|
|
133
|
-
|
|
134
|
-
const room: any = Object.create(RoomsDO.prototype);
|
|
135
|
-
room.readJsonBody = vi.fn().mockResolvedValue({ connectionId: 'conn-1' });
|
|
136
|
-
room.authenticateRealtimeRequest = vi.fn().mockResolvedValue({
|
|
137
|
-
memberId: 'member-1',
|
|
138
|
-
connectionId: 'conn-1',
|
|
139
|
-
meta: {
|
|
140
|
-
authenticated: true,
|
|
141
|
-
connectionId: 'conn-1',
|
|
142
|
-
},
|
|
143
|
-
});
|
|
144
|
-
room.hasPublishedTracks = vi.fn().mockReturnValue(true);
|
|
145
|
-
|
|
146
|
-
const response = await room.handleCloudflareRealtimeKitSessionCreate(
|
|
147
|
-
new Request('http://do/media/cloudflare_realtimekit/session?room=game::room-1', {
|
|
148
|
-
method: 'POST',
|
|
149
|
-
body: JSON.stringify({ connectionId: 'conn-1' }),
|
|
150
|
-
headers: { 'Content-Type': 'application/json' },
|
|
151
|
-
}),
|
|
152
|
-
new URL('http://do/media/cloudflare_realtimekit/session?room=game::room-1'),
|
|
153
|
-
);
|
|
154
|
-
|
|
155
|
-
expect(response.status).toBe(409);
|
|
156
|
-
await expect(response.json()).resolves.toEqual({
|
|
157
|
-
code: 409,
|
|
158
|
-
message: 'Unpublish existing room media before creating a new Cloudflare RealtimeKit session',
|
|
159
|
-
});
|
|
160
|
-
});
|
|
161
130
|
});
|
|
@@ -5,7 +5,7 @@ vi.mock('cloudflare:workers', () => ({
|
|
|
5
5
|
}));
|
|
6
6
|
|
|
7
7
|
describe('room rate-limit scopes', () => {
|
|
8
|
-
it('keeps signal/
|
|
8
|
+
it('keeps signal/admin buckets independent per connection', async () => {
|
|
9
9
|
const { RoomRuntimeBaseDO } = await import('../durable-objects/room-runtime-base.js');
|
|
10
10
|
|
|
11
11
|
const room: any = Object.create(RoomRuntimeBaseDO.prototype);
|
|
@@ -13,7 +13,6 @@ describe('room rate-limit scopes', () => {
|
|
|
13
13
|
rateLimit: {
|
|
14
14
|
actions: 2,
|
|
15
15
|
signals: 4,
|
|
16
|
-
media: 1,
|
|
17
16
|
admin: 1,
|
|
18
17
|
},
|
|
19
18
|
};
|
|
@@ -25,9 +24,6 @@ describe('room rate-limit scopes', () => {
|
|
|
25
24
|
expect(room.checkRateLimit('conn-1', 'signals')).toBe(true);
|
|
26
25
|
expect(room.checkRateLimit('conn-1', 'signals')).toBe(false);
|
|
27
26
|
|
|
28
|
-
expect(room.checkRateLimit('conn-1', 'media')).toBe(true);
|
|
29
|
-
expect(room.checkRateLimit('conn-1', 'media')).toBe(false);
|
|
30
|
-
|
|
31
27
|
expect(room.checkRateLimit('conn-1', 'admin')).toBe(true);
|
|
32
28
|
expect(room.checkRateLimit('conn-1', 'admin')).toBe(false);
|
|
33
29
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { afterEach, describe, expect, it
|
|
1
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
2
2
|
import { defineConfig } from '@edge-base/shared';
|
|
3
3
|
import { OpenAPIHono, type HonoEnv } from '../lib/hono.js';
|
|
4
4
|
import { setConfig } from '../lib/do-router.js';
|
|
@@ -33,21 +33,6 @@ function createRoomApp() {
|
|
|
33
33
|
return app;
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
function createAuthedRoomApp() {
|
|
37
|
-
const app = new OpenAPIHono<HonoEnv>();
|
|
38
|
-
app.use('/api/*', async (c, next) => {
|
|
39
|
-
c.set('auth', {
|
|
40
|
-
id: 'user-1',
|
|
41
|
-
role: 'user',
|
|
42
|
-
isAnonymous: false,
|
|
43
|
-
meta: {},
|
|
44
|
-
});
|
|
45
|
-
await next();
|
|
46
|
-
});
|
|
47
|
-
app.route('/api/room', roomRoute);
|
|
48
|
-
return app;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
36
|
describe('room runtime selection', () => {
|
|
52
37
|
afterEach(() => {
|
|
53
38
|
setConfig({});
|
|
@@ -98,6 +83,29 @@ describe('room route runtime routing', () => {
|
|
|
98
83
|
await expect(response.json()).resolves.toEqual({ runtime: 'rooms' });
|
|
99
84
|
});
|
|
100
85
|
|
|
86
|
+
it('routes summary requests to the rooms runtime', async () => {
|
|
87
|
+
setConfig(defineConfig({
|
|
88
|
+
rooms: {
|
|
89
|
+
game: {
|
|
90
|
+
runtime: {
|
|
91
|
+
target: 'rooms',
|
|
92
|
+
},
|
|
93
|
+
public: {
|
|
94
|
+
metadata: true,
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
}));
|
|
99
|
+
|
|
100
|
+
const app = createRoomApp();
|
|
101
|
+
const response = await app.request('/api/room/summary?namespace=game&id=room-1', {
|
|
102
|
+
method: 'GET',
|
|
103
|
+
}, createRoomRuntimeEnv());
|
|
104
|
+
|
|
105
|
+
expect(response.status).toBe(201);
|
|
106
|
+
await expect(response.json()).resolves.toEqual({ runtime: 'rooms' });
|
|
107
|
+
});
|
|
108
|
+
|
|
101
109
|
it('routes websocket upgrades to the rooms runtime', async () => {
|
|
102
110
|
setConfig(defineConfig({
|
|
103
111
|
rooms: {
|
|
@@ -172,99 +180,4 @@ describe('room route runtime routing', () => {
|
|
|
172
180
|
});
|
|
173
181
|
});
|
|
174
182
|
|
|
175
|
-
it('routes room media requests to the rooms runtime', async () => {
|
|
176
|
-
setConfig(defineConfig({
|
|
177
|
-
rooms: {
|
|
178
|
-
game: {
|
|
179
|
-
runtime: {
|
|
180
|
-
target: 'rooms',
|
|
181
|
-
},
|
|
182
|
-
},
|
|
183
|
-
},
|
|
184
|
-
}));
|
|
185
|
-
|
|
186
|
-
const doFetch = vi.fn(async (request: Request) => new Response(JSON.stringify({
|
|
187
|
-
runtime: 'rooms',
|
|
188
|
-
path: new URL(request.url).pathname,
|
|
189
|
-
auth: request.headers.get('Authorization'),
|
|
190
|
-
body: await request.clone().json(),
|
|
191
|
-
}), {
|
|
192
|
-
headers: { 'Content-Type': 'application/json' },
|
|
193
|
-
status: 201,
|
|
194
|
-
}));
|
|
195
|
-
|
|
196
|
-
const env = createRoomRuntimeEnv();
|
|
197
|
-
env.ROOMS = {
|
|
198
|
-
idFromName: (name: string) => name as unknown as DurableObjectId,
|
|
199
|
-
get: () => ({ fetch: doFetch }),
|
|
200
|
-
} as unknown as DurableObjectNamespace;
|
|
201
|
-
|
|
202
|
-
const app = createAuthedRoomApp();
|
|
203
|
-
const response = await app.request('/api/room/media/realtime/session?namespace=game&id=room-1', {
|
|
204
|
-
method: 'POST',
|
|
205
|
-
headers: {
|
|
206
|
-
Authorization: 'Bearer room-token',
|
|
207
|
-
'Content-Type': 'application/json',
|
|
208
|
-
},
|
|
209
|
-
body: JSON.stringify({ connectionId: 'conn-1' }),
|
|
210
|
-
}, env);
|
|
211
|
-
|
|
212
|
-
expect(response.status).toBe(201);
|
|
213
|
-
await expect(response.json()).resolves.toMatchObject({
|
|
214
|
-
runtime: 'rooms',
|
|
215
|
-
path: '/media/realtime/session',
|
|
216
|
-
auth: 'Bearer room-token',
|
|
217
|
-
body: { connectionId: 'conn-1' },
|
|
218
|
-
});
|
|
219
|
-
expect(doFetch).toHaveBeenCalledTimes(1);
|
|
220
|
-
expect(new URL((doFetch.mock.calls[0][0] as Request).url).searchParams.get('room')).toBe('game::room-1');
|
|
221
|
-
});
|
|
222
|
-
|
|
223
|
-
it('routes room cloudflare realtimekit session requests to the rooms runtime', async () => {
|
|
224
|
-
setConfig(defineConfig({
|
|
225
|
-
rooms: {
|
|
226
|
-
game: {
|
|
227
|
-
runtime: {
|
|
228
|
-
target: 'rooms',
|
|
229
|
-
},
|
|
230
|
-
},
|
|
231
|
-
},
|
|
232
|
-
}));
|
|
233
|
-
|
|
234
|
-
const doFetch = vi.fn(async (request: Request) => new Response(JSON.stringify({
|
|
235
|
-
runtime: 'rooms',
|
|
236
|
-
path: new URL(request.url).pathname,
|
|
237
|
-
auth: request.headers.get('Authorization'),
|
|
238
|
-
body: await request.clone().json(),
|
|
239
|
-
}), {
|
|
240
|
-
headers: { 'Content-Type': 'application/json' },
|
|
241
|
-
status: 201,
|
|
242
|
-
}));
|
|
243
|
-
|
|
244
|
-
const env = createRoomRuntimeEnv();
|
|
245
|
-
env.ROOMS = {
|
|
246
|
-
idFromName: (name: string) => name as unknown as DurableObjectId,
|
|
247
|
-
get: () => ({ fetch: doFetch }),
|
|
248
|
-
} as unknown as DurableObjectNamespace;
|
|
249
|
-
|
|
250
|
-
const app = createAuthedRoomApp();
|
|
251
|
-
const response = await app.request('/api/room/media/cloudflare_realtimekit/session?namespace=game&id=room-1', {
|
|
252
|
-
method: 'POST',
|
|
253
|
-
headers: {
|
|
254
|
-
Authorization: 'Bearer room-token',
|
|
255
|
-
'Content-Type': 'application/json',
|
|
256
|
-
},
|
|
257
|
-
body: JSON.stringify({ connectionId: 'conn-1' }),
|
|
258
|
-
}, env);
|
|
259
|
-
|
|
260
|
-
expect(response.status).toBe(201);
|
|
261
|
-
await expect(response.json()).resolves.toMatchObject({
|
|
262
|
-
runtime: 'rooms',
|
|
263
|
-
path: '/media/cloudflare_realtimekit/session',
|
|
264
|
-
auth: 'Bearer room-token',
|
|
265
|
-
body: { connectionId: 'conn-1' },
|
|
266
|
-
});
|
|
267
|
-
expect(doFetch).toHaveBeenCalledTimes(1);
|
|
268
|
-
expect(new URL((doFetch.mock.calls[0][0] as Request).url).searchParams.get('room')).toBe('game::room-1');
|
|
269
|
-
});
|
|
270
183
|
});
|
|
@@ -360,6 +360,12 @@ describe('parseRoute — room', () => {
|
|
|
360
360
|
expect(r.subcategory).toBe('metadata');
|
|
361
361
|
expect(r.operation).toBe('getMetadata');
|
|
362
362
|
});
|
|
363
|
+
|
|
364
|
+
it('GET /api/room/summary', () => {
|
|
365
|
+
const r = parseRoute('GET', '/api/room/summary');
|
|
366
|
+
expect(r.subcategory).toBe('summary');
|
|
367
|
+
expect(r.operation).toBe('getSummary');
|
|
368
|
+
});
|
|
363
369
|
});
|
|
364
370
|
|
|
365
371
|
// ─── K. Other feature routes ────────────────────────────────────────────────
|
|
@@ -825,10 +825,10 @@ describe('zodDefaultHook', () => {
|
|
|
825
825
|
const c = mockContext();
|
|
826
826
|
const result = zodDefaultHook({
|
|
827
827
|
success: false,
|
|
828
|
-
error: { issues: [{ message: 'field required' }, { message: 'invalid type' }] },
|
|
828
|
+
error: { issues: [{ message: 'field required', path: ['body', 'email'] }, { message: 'invalid type' }] },
|
|
829
829
|
}, c);
|
|
830
830
|
expect(result).toBeDefined();
|
|
831
|
-
expect(c.lastJson).toEqual({ code: 400, message: 'field required, invalid type' });
|
|
831
|
+
expect(c.lastJson).toEqual({ code: 400, message: 'body.email: field required, invalid type' });
|
|
832
832
|
expect(c.lastStatus).toBe(400);
|
|
833
833
|
});
|
|
834
834
|
|
|
@@ -841,13 +841,13 @@ describe('zodDefaultHook', () => {
|
|
|
841
841
|
expect(c.lastJson).toEqual({ code: 400, message: 'too short' });
|
|
842
842
|
});
|
|
843
843
|
|
|
844
|
-
it('handles empty issues →
|
|
844
|
+
it('handles empty issues → default message', () => {
|
|
845
845
|
const c = mockContext();
|
|
846
846
|
zodDefaultHook({
|
|
847
847
|
success: false,
|
|
848
848
|
error: { issues: [] },
|
|
849
849
|
}, c);
|
|
850
|
-
expect(c.lastJson).toEqual({ code: 400, message: '' });
|
|
850
|
+
expect(c.lastJson).toEqual({ code: 400, message: 'Request validation failed.' });
|
|
851
851
|
});
|
|
852
852
|
|
|
853
853
|
it('handles missing error.issues and error.errors', () => {
|
|
@@ -856,7 +856,7 @@ describe('zodDefaultHook', () => {
|
|
|
856
856
|
success: false,
|
|
857
857
|
error: {},
|
|
858
858
|
}, c);
|
|
859
|
-
expect(c.lastJson).toEqual({ code: 400, message: '' });
|
|
859
|
+
expect(c.lastJson).toEqual({ code: 400, message: 'Request validation failed.' });
|
|
860
860
|
});
|
|
861
861
|
|
|
862
862
|
it('handles undefined error', () => {
|
|
@@ -864,7 +864,16 @@ describe('zodDefaultHook', () => {
|
|
|
864
864
|
zodDefaultHook({
|
|
865
865
|
success: false,
|
|
866
866
|
}, c);
|
|
867
|
-
expect(c.lastJson).toEqual({ code: 400, message: '' });
|
|
867
|
+
expect(c.lastJson).toEqual({ code: 400, message: 'Request validation failed.' });
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
it('formats array indexes in issue paths', () => {
|
|
871
|
+
const c = mockContext();
|
|
872
|
+
zodDefaultHook({
|
|
873
|
+
success: false,
|
|
874
|
+
error: { issues: [{ message: 'Expected string', path: ['body', 'members', 0, 'email'] }] },
|
|
875
|
+
}, c);
|
|
876
|
+
expect(c.lastJson).toEqual({ code: 400, message: 'body.members[0].email: Expected string' });
|
|
868
877
|
});
|
|
869
878
|
});
|
|
870
879
|
|
|
@@ -1936,7 +1936,13 @@ export class DatabaseDO extends DurableObject<DOEnv> {
|
|
|
1936
1936
|
return c.json(normalizedDbError.toJSON(), normalizedDbError.code as 400);
|
|
1937
1937
|
}
|
|
1938
1938
|
console.error('DatabaseDO Error:', err);
|
|
1939
|
-
return c.json(
|
|
1939
|
+
return c.json(
|
|
1940
|
+
{
|
|
1941
|
+
code: 500,
|
|
1942
|
+
message: `Database request failed while handling '${c.req.path}'. Check the worker logs for the original exception.`,
|
|
1943
|
+
},
|
|
1944
|
+
500,
|
|
1945
|
+
);
|
|
1940
1946
|
});
|
|
1941
1947
|
|
|
1942
1948
|
return app;
|