@@ -13,6 +13,8 @@ import { SSEStreamingApi, streamSSE } from "hono/streaming";
13
13
import { cors } from "hono/cors" ;
14
14
import { assertUnreachable } from "./utils" ;
15
15
import { createInspectorRouter , InspectorConnectionHandler } from "./inspect" ;
16
+ import { handleRouteError , handleRouteNotFound } from "@/common/router" ;
17
+ import { deconstructError } from "@/common/utils" ;
16
18
17
19
export interface ConnectWebSocketOpts {
18
20
req : HonoRequest ;
@@ -98,58 +100,85 @@ export function createActorRouter(
98
100
app . get (
99
101
"/connect/websocket" ,
100
102
handler . upgradeWebSocket ( async ( c ) => {
101
- if ( ! handler . onConnectWebSocket )
102
- throw new Error ( "onConnectWebSocket is not implemented" ) ;
103
-
104
- const encoding = getRequestEncoding ( c . req ) ;
105
- const parameters = getRequestConnectionParameters ( c . req , config ) ;
106
-
107
- const wsHandler = await handler . onConnectWebSocket ( {
108
- req : c . req ,
109
- encoding,
110
- parameters,
111
- } ) ;
112
-
113
- const { promise : onOpenPromise , resolve : onOpenResolve } =
114
- Promise . withResolvers < undefined > ( ) ;
115
- return {
116
- onOpen : async ( _evt , ws ) => {
117
- logger ( ) . debug ( "websocket open" ) ;
118
-
119
- // Call handler
120
- await wsHandler . onOpen ( ws ) ;
121
-
122
- // Resolve promise
123
- onOpenResolve ( undefined ) ;
124
- } ,
125
- onMessage : async ( evt ) => {
126
- await onOpenPromise ;
127
-
128
- logger ( ) . debug ( "received message" ) ;
129
-
130
- const value = evt . data . valueOf ( ) as InputData ;
131
- const message = await parseMessage ( value , {
132
- encoding : encoding ,
133
- maxIncomingMessageSize : config . maxIncomingMessageSize ,
134
- } ) ;
135
-
136
- await wsHandler . onMessage ( message ) ;
137
- } ,
138
- onClose : async ( _evt ) => {
139
- await onOpenPromise ;
140
-
141
- logger ( ) . debug ( "websocket closed" ) ;
142
-
143
- await wsHandler . onClose ( ) ;
144
- } ,
145
- onError : async ( error ) => {
146
- await onOpenPromise ;
147
-
148
- // Actors don't need to know about this, since it's abstracted
149
- // away
150
- logger ( ) . warn ( "websocket error" , { error : `${ error } ` } ) ;
151
- } ,
152
- } ;
103
+ try {
104
+ if ( ! handler . onConnectWebSocket )
105
+ throw new Error ( "onConnectWebSocket is not implemented" ) ;
106
+
107
+ const encoding = getRequestEncoding ( c . req ) ;
108
+ const parameters = getRequestConnectionParameters ( c . req , config ) ;
109
+
110
+ const wsHandler = await handler . onConnectWebSocket ( {
111
+ req : c . req ,
112
+ encoding,
113
+ parameters,
114
+ } ) ;
115
+
116
+ const { promise : onOpenPromise , resolve : onOpenResolve } =
117
+ Promise . withResolvers < undefined > ( ) ;
118
+ return {
119
+ onOpen : async ( _evt , ws ) => {
120
+ try {
121
+ logger ( ) . debug ( "websocket open" ) ;
122
+
123
+ // Call handler
124
+ await wsHandler . onOpen ( ws ) ;
125
+
126
+ // Resolve promise
127
+ onOpenResolve ( undefined ) ;
128
+ } catch ( error ) {
129
+ const { code } = deconstructError ( error , logger ( ) , {
130
+ wsEvent : "open" ,
131
+ } ) ;
132
+ ws . close ( 1011 , code ) ;
133
+ }
134
+ } ,
135
+ onMessage : async ( evt , ws ) => {
136
+ try {
137
+ await onOpenPromise ;
138
+
139
+ logger ( ) . debug ( "received message" ) ;
140
+
141
+ const value = evt . data . valueOf ( ) as InputData ;
142
+ const message = await parseMessage ( value , {
143
+ encoding : encoding ,
144
+ maxIncomingMessageSize : config . maxIncomingMessageSize ,
145
+ } ) ;
146
+
147
+ await wsHandler . onMessage ( message ) ;
148
+ } catch ( error ) {
149
+ const { code } = deconstructError ( error , logger ( ) , {
150
+ wsEvent : "message" ,
151
+ } ) ;
152
+ ws . close ( 1011 , code ) ;
153
+ }
154
+ } ,
155
+ onClose : async ( _evt ) => {
156
+ try {
157
+ await onOpenPromise ;
158
+
159
+ logger ( ) . debug ( "websocket closed" ) ;
160
+
161
+ await wsHandler . onClose ( ) ;
162
+ } catch ( error ) {
163
+ deconstructError ( error , logger ( ) , { wsEvent : "close" } ) ;
164
+ }
165
+ } ,
166
+ onError : async ( error ) => {
167
+ try {
168
+ await onOpenPromise ;
169
+
170
+ // Actors don't need to know about this, since it's abstracted
171
+ // away
172
+ logger ( ) . warn ( "websocket error" , { error : `${ error } ` } ) ;
173
+ } catch ( error ) {
174
+ deconstructError ( error , logger ( ) , { wsEvent : "error" } ) ;
175
+ }
176
+ } ,
177
+ } ;
178
+ } catch ( error ) {
179
+ deconstructError ( error , logger ( ) , { } ) ;
180
+ return { } ;
181
+ }
153
182
} ) ,
154
183
) ;
155
184
} else {
@@ -233,42 +262,20 @@ export function createActorRouter(
233
262
} satisfies protoHttpRpc . ResponseOk ) ;
234
263
} catch ( error ) {
235
264
// Build response error information similar to WebSocket handling
236
- let status : ContentfulStatusCode ;
237
- let code : string ;
238
- let message : string ;
239
- let metadata : unknown = undefined ;
240
-
241
- if ( error instanceof errors . ActorError && error . public ) {
242
- logger ( ) . info ( "http rpc public error" , {
243
- rpc : rpcName ,
244
- error,
245
- } ) ;
246
265
247
- status = 400 ;
248
- code = error . code ;
249
- message = String ( error ) ;
250
- metadata = error . metadata ;
251
- } else {
252
- logger ( ) . warn ( "http rpc internal error" , {
253
- rpc : rpcName ,
254
- error,
255
- } ) ;
256
-
257
- status = 500 ;
258
- code = errors . INTERNAL_ERROR_CODE ;
259
- message = errors . INTERNAL_ERROR_DESCRIPTION ;
260
- metadata = {
261
- //url: `https://hub.rivet.gg/projects/${this.#driver.metadata.project.slug}/environments/${this.#driver.metadata.environment.slug}/actors?actorId=${this.#driver.metadata.actor.id}`,
262
- } satisfies errors . InternalErrorMetadata ;
263
- }
266
+ const { statusCode, code, message, metadata } = deconstructError (
267
+ error ,
268
+ logger ( ) ,
269
+ { rpc : rpcName } ,
270
+ ) ;
264
271
265
272
return c . json (
266
273
{
267
274
c : code ,
268
275
m : message ,
269
276
md : metadata ,
270
277
} satisfies protoHttpRpc . ResponseErr ,
271
- { status } ,
278
+ { status : statusCode } ,
272
279
) ;
273
280
}
274
281
} ) ;
@@ -319,40 +326,19 @@ export function createActorRouter(
319
326
return c . json ( { } ) ;
320
327
} catch ( error ) {
321
328
// Build response error information similar to WebSocket handling
322
- let status : ContentfulStatusCode ;
323
- let code : string ;
324
- let message : string ;
325
- let metadata : unknown = undefined ;
326
-
327
- if ( error instanceof errors . ActorError && error . public ) {
328
- logger ( ) . info ( "http rpc public error" , {
329
- error,
330
- } ) ;
331
-
332
- status = 400 ;
333
- code = error . code ;
334
- message = String ( error ) ;
335
- metadata = error . metadata ;
336
- } else {
337
- logger ( ) . warn ( "http rpc internal error" , {
338
- error,
339
- } ) ;
340
-
341
- status = 500 ;
342
- code = errors . INTERNAL_ERROR_CODE ;
343
- message = errors . INTERNAL_ERROR_DESCRIPTION ;
344
- metadata = {
345
- //url: `https://hub.rivet.gg/projects/${this.#driver.metadata.project.slug}/environments/${this.#driver.metadata.environment.slug}/actors?actorId=${this.#driver.metadata.actor.id}`,
346
- } satisfies errors . InternalErrorMetadata ;
347
- }
329
+ const { statusCode, code, message, metadata } = deconstructError (
330
+ error ,
331
+ logger ( ) ,
332
+ { } ,
333
+ ) ;
348
334
349
335
return c . json (
350
336
{
351
337
c : code ,
352
338
m : message ,
353
339
md : metadata ,
354
340
} satisfies protoHttpRpc . ResponseErr ,
355
- { status } ,
341
+ { status : statusCode } ,
356
342
) ;
357
343
}
358
344
} ) ;
@@ -362,9 +348,8 @@ export function createActorRouter(
362
348
createInspectorRouter ( handler . upgradeWebSocket , handler . onConnectInspector ) ,
363
349
) ;
364
350
365
- app . notFound ( ( c ) => {
366
- return c . text ( "Not Found (ActorCore)" , 404 ) ;
367
- } ) ;
351
+ app . notFound ( handleRouteNotFound ) ;
352
+ app . onError ( handleRouteError ) ;
368
353
369
354
return app ;
370
355
}
0 commit comments