@@ -151,10 +151,11 @@ function processErrorEventData(
151151 ip : string
152152) : Promise < ErrorEvent > {
153153 return record ( "processErrorEventData" , async ( ) => {
154- const payload = errorData . payload ;
155- const eventId = parseEventId ( payload . eventId , randomUUID ) ;
154+ // Support both envelope format (payload) and direct format
155+ const payload = errorData . payload || errorData ;
156+ const eventId = parseEventId ( payload . eventId || errorData . eventId , randomUUID ) ;
156157 const now = Date . now ( ) ;
157- const timestamp = parseTimestamp ( payload . timestamp ) ;
158+ const timestamp = parseTimestamp ( payload . timestamp || errorData . timestamp ) ;
158159
159160 const [ geoData , uaData ] = await Promise . all ( [
160161 getGeo ( ip ) ,
@@ -170,25 +171,25 @@ function processErrorEventData(
170171 client_id : clientId ,
171172 event_id : eventId ,
172173 anonymous_id : sanitizeString (
173- payload . anonymousId ,
174+ payload . anonymousId || errorData . anonymousId ,
174175 VALIDATION_LIMITS . SHORT_STRING_MAX_LENGTH
175176 ) ,
176- session_id : validateSessionId ( payload . sessionId ) ,
177+ session_id : validateSessionId ( payload . sessionId || errorData . sessionId ) ,
177178 timestamp,
178- path : sanitizeString ( payload . path , VALIDATION_LIMITS . STRING_MAX_LENGTH ) ,
179+ path : sanitizeString ( payload . path || errorData . path , VALIDATION_LIMITS . STRING_MAX_LENGTH ) ,
179180 message : sanitizeString (
180- payload . message ,
181+ payload . message || errorData . message ,
181182 VALIDATION_LIMITS . STRING_MAX_LENGTH
182183 ) ,
183184 filename : sanitizeString (
184- payload . filename ,
185+ payload . filename || errorData . filename ,
185186 VALIDATION_LIMITS . STRING_MAX_LENGTH
186187 ) ,
187- lineno : payload . lineno ,
188- colno : payload . colno ,
189- stack : sanitizeString ( payload . stack , VALIDATION_LIMITS . STRING_MAX_LENGTH ) ,
188+ lineno : payload . lineno || errorData . lineno ,
189+ colno : payload . colno || errorData . colno ,
190+ stack : sanitizeString ( payload . stack || errorData . stack , VALIDATION_LIMITS . STRING_MAX_LENGTH ) ,
190191 error_type : sanitizeString (
191- payload . errorType ,
192+ payload . errorType || errorData . errorType ,
192193 VALIDATION_LIMITS . SHORT_STRING_MAX_LENGTH
193194 ) ,
194195 ip : anonymizedIP || "" ,
@@ -212,10 +213,11 @@ function processWebVitalsEventData(
212213 ip : string
213214) : Promise < WebVitalsEvent > {
214215 return record ( "processWebVitalsEventData" , async ( ) => {
215- const payload = vitalsData . payload ;
216- const eventId = parseEventId ( payload . eventId , randomUUID ) ;
216+ // Support both envelope format (payload) and direct format
217+ const payload = vitalsData . payload || vitalsData ;
218+ const eventId = parseEventId ( payload . eventId || vitalsData . eventId , randomUUID ) ;
217219 const now = Date . now ( ) ;
218- const timestamp = parseTimestamp ( payload . timestamp ) ;
220+ const timestamp = parseTimestamp ( payload . timestamp || vitalsData . timestamp ) ;
219221
220222 const [ geoData , uaData ] = await Promise . all ( [
221223 getGeo ( ip ) ,
@@ -231,17 +233,17 @@ function processWebVitalsEventData(
231233 client_id : clientId ,
232234 event_id : eventId ,
233235 anonymous_id : sanitizeString (
234- payload . anonymousId ,
236+ payload . anonymousId || vitalsData . anonymousId ,
235237 VALIDATION_LIMITS . SHORT_STRING_MAX_LENGTH
236238 ) ,
237- session_id : validateSessionId ( payload . sessionId ) ,
239+ session_id : validateSessionId ( payload . sessionId || vitalsData . sessionId ) ,
238240 timestamp,
239- path : sanitizeString ( payload . path , VALIDATION_LIMITS . STRING_MAX_LENGTH ) ,
240- fcp : validatePerformanceMetric ( payload . fcp ) ,
241- lcp : validatePerformanceMetric ( payload . lcp ) ,
242- cls : validatePerformanceMetric ( payload . cls ) ,
243- fid : validatePerformanceMetric ( payload . fid ) ,
244- inp : validatePerformanceMetric ( payload . inp ) ,
241+ path : sanitizeString ( payload . path || vitalsData . path || vitalsData . url , VALIDATION_LIMITS . STRING_MAX_LENGTH ) , // Support both path and url
242+ fcp : validatePerformanceMetric ( payload . fcp || vitalsData . fcp ) ,
243+ lcp : validatePerformanceMetric ( payload . lcp || vitalsData . lcp ) ,
244+ cls : validatePerformanceMetric ( payload . cls || vitalsData . cls ) ,
245+ fid : validatePerformanceMetric ( payload . fid || vitalsData . fid ) ,
246+ inp : validatePerformanceMetric ( payload . inp || vitalsData . inp ) ,
245247 ip : "" ,
246248 user_agent : "" ,
247249 country : country || "" ,
@@ -299,6 +301,112 @@ function processOutgoingLinkData(
299301}
300302
301303const app = new Elysia ( )
304+ . post ( "/vitals" , async ( context ) => {
305+ const { body, query, request } = context as {
306+ body : any ;
307+ query : any ;
308+ request : Request ;
309+ } ;
310+
311+ try {
312+ const validation = await validateRequest ( body , query , request ) ;
313+ if ( "error" in validation ) {
314+ return validation . error ;
315+ }
316+
317+ const { clientId, userAgent, ip } = validation ;
318+
319+ const [ botError , parseResult ] = await Promise . all ( [
320+ checkForBot ( request , body , query , clientId , userAgent ) ,
321+ validateEventSchema (
322+ webVitalsEventSchema ,
323+ body ,
324+ request ,
325+ query ,
326+ clientId
327+ ) ,
328+ ] ) ;
329+
330+ if ( botError ) {
331+ return botError . error ;
332+ }
333+
334+ if ( ! parseResult . success ) {
335+ return createSchemaErrorResponse ( parseResult . error . issues ) ;
336+ }
337+
338+ const vitalsEvent = await processWebVitalsEventData (
339+ body ,
340+ clientId ,
341+ userAgent ,
342+ ip
343+ ) ;
344+
345+ await insertWebVitals ( vitalsEvent , clientId , userAgent , ip ) ;
346+ return { status : "success" , type : "web_vitals" } ;
347+
348+ } catch ( error ) {
349+ captureError ( error , { message : "Error processing vitals" } ) ;
350+ return { status : "error" , message : "Internal server error" } ;
351+ }
352+ } )
353+ . post ( "/errors" , async ( context ) => {
354+ const { body, query, request } = context as {
355+ body : any ;
356+ query : any ;
357+ request : Request ;
358+ } ;
359+
360+ try {
361+ const validation = await validateRequest ( body , query , request ) ;
362+ if ( "error" in validation ) {
363+ return validation . error ;
364+ }
365+
366+ const { clientId, userAgent, ip } = validation ;
367+
368+ if ( FILTERED_ERROR_MESSAGES . has ( body . message ) ) {
369+ return {
370+ status : "ignored" ,
371+ type : "error" ,
372+ reason : "filtered_message" ,
373+ } ;
374+ }
375+
376+ const [ botError , parseResult ] = await Promise . all ( [
377+ checkForBot ( request , body , query , clientId , userAgent ) ,
378+ validateEventSchema (
379+ errorEventSchema ,
380+ body ,
381+ request ,
382+ query ,
383+ clientId
384+ ) ,
385+ ] ) ;
386+
387+ if ( botError ) {
388+ return botError . error ;
389+ }
390+
391+ if ( ! parseResult . success ) {
392+ return createSchemaErrorResponse ( parseResult . error . issues ) ;
393+ }
394+
395+ const errorEvent = await processErrorEventData (
396+ body ,
397+ clientId ,
398+ userAgent ,
399+ ip
400+ ) ;
401+
402+ await insertError ( errorEvent , clientId , userAgent , ip ) ;
403+ return { status : "success" , type : "error" } ;
404+
405+ } catch ( error ) {
406+ captureError ( error , { message : "Error processing error" } ) ;
407+ return { status : "error" , message : "Internal server error" } ;
408+ }
409+ } )
302410 . post ( "/" , async ( context ) => {
303411 const { body, query, request } = context as {
304412 body : any ;
@@ -362,7 +470,8 @@ const app = new Elysia()
362470 return createSchemaErrorResponse ( parseResult . error . issues ) ;
363471 }
364472
365- insertError ( body , clientId , userAgent , ip ) ;
473+ const errorEvent = await processErrorEventData ( body , clientId , userAgent , ip ) ;
474+ insertError ( errorEvent , clientId , userAgent , ip ) ;
366475 return { status : "success" , type : "error" } ;
367476 }
368477
@@ -386,7 +495,8 @@ const app = new Elysia()
386495 return createSchemaErrorResponse ( parseResult . error . issues ) ;
387496 }
388497
389- insertWebVitals ( body , clientId , userAgent , ip ) ;
498+ const vitalsEvent = await processWebVitalsEventData ( body , clientId , userAgent , ip ) ;
499+ insertWebVitals ( vitalsEvent , clientId , userAgent , ip ) ;
390500 return { status : "success" , type : "web_vitals" } ;
391501 }
392502
0 commit comments