@@ -9,6 +9,7 @@ import { defineIntegration } from '../integration';
99import { SEMANTIC_ATTRIBUTE_SENTRY_OP , SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '../semanticAttributes' ;
1010import { setHttpStatus , SPAN_STATUS_ERROR , SPAN_STATUS_OK , startSpan } from '../tracing' ;
1111import type { IntegrationFn } from '../types-hoist/integration' ;
12+ import type { SpanAttributes } from '../types-hoist/span' ;
1213import { isPlainObject } from '../utils/is' ;
1314import { debug } from '../utils/logger' ;
1415
@@ -64,7 +65,8 @@ export const FILTER_MAPPINGS = {
6465 not : 'not' ,
6566} ;
6667
67- export const DB_OPERATIONS_TO_INSTRUMENT = [ 'select' , 'insert' , 'upsert' , 'update' , 'delete' ] ;
68+ export const DB_OPERATIONS_TO_INSTRUMENT = [ 'select' , 'insert' , 'upsert' , 'update' , 'delete' ] as const ;
69+ type DBOperation = ( typeof DB_OPERATIONS_TO_INSTRUMENT ) [ number ] ;
6870
6971type AuthOperationFn = ( ...args : unknown [ ] ) => Promise < unknown > ;
7072type AuthOperationName = ( typeof AUTH_OPERATIONS_TO_INSTRUMENT ) [ number ] ;
@@ -86,7 +88,7 @@ export interface PostgRESTFilterBuilder {
8688 headers : Record < string , string > ;
8789 url : URL ;
8890 schema : string ;
89- body : any ;
91+ body : unknown ;
9092}
9193
9294export interface SupabaseResponse {
@@ -122,7 +124,7 @@ export interface SupabaseClientConstructor {
122124export interface PostgRESTProtoThenable {
123125 then : < T > (
124126 onfulfilled ?: ( ( value : T ) => T | PromiseLike < T > ) | null ,
125- onrejected ?: ( ( reason : any ) => T | PromiseLike < T > ) | null ,
127+ onrejected ?: ( ( reason : unknown ) => T | PromiseLike < T > ) | null ,
126128 ) => Promise < T > ;
127129}
128130
@@ -152,7 +154,7 @@ function isInstrumented<T>(fn: T): boolean | undefined {
152154 * @param headers - The request headers
153155 * @returns The database operation type ('select', 'insert', 'upsert', 'update', or 'delete')
154156 */
155- export function extractOperation ( method : string , headers : Record < string , string > = { } ) : string {
157+ export function extractOperation ( method : string , headers : Record < string , string > = { } ) : DBOperation | '<unknown-op>' {
156158 switch ( method ) {
157159 case 'GET' : {
158160 return 'select' ;
@@ -333,7 +335,7 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
333335 const typedThis = thisArg as PostgRESTFilterBuilder ;
334336 const operation = extractOperation ( typedThis . method , typedThis . headers ) ;
335337
336- if ( ! operations . includes ( operation ) ) {
338+ if ( ! operations . includes ( operation as DBOperation ) ) {
337339 return Reflect . apply ( target , thisArg , argumentsList ) ;
338340 }
339341
@@ -364,7 +366,7 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
364366 ' ' ,
365367 ) } from(${ table } )`;
366368
367- const attributes : Record < string , any > = {
369+ const attributes : SpanAttributes = {
368370 'db.table' : table ,
369371 'db.schema' : typedThis . schema ,
370372 'db.url' : typedThis . url . origin ,
@@ -380,7 +382,11 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
380382 }
381383
382384 if ( Object . keys ( body ) . length ) {
383- attributes [ 'db.body' ] = body ;
385+ try {
386+ attributes [ 'db.body' ] = JSON . stringify ( body ) ;
387+ } catch {
388+ // could not stringify body
389+ }
384390 }
385391
386392 return startSpan (
@@ -465,17 +471,19 @@ function instrumentPostgRESTFilterBuilder(PostgRESTFilterBuilder: PostgRESTFilte
465471 markAsInstrumented ( ( PostgRESTFilterBuilder . prototype as unknown as PostgRESTProtoThenable ) . then ) ;
466472}
467473
474+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
475+ type BuilderPrototype = Record < DBOperation , ( ...args : any [ ] ) => any > ;
476+
468477function instrumentPostgRESTQueryBuilder ( PostgRESTQueryBuilder : new ( ) => PostgRESTQueryBuilder ) : void {
469478 // We need to wrap _all_ operations despite them sharing the same `PostgRESTFilterBuilder`
470479 // constructor, as we don't know which method will be called first, and we don't want to miss any calls.
471480 for ( const operation of DB_OPERATIONS_TO_INSTRUMENT ) {
472- if ( isInstrumented ( ( PostgRESTQueryBuilder . prototype as Record < string , any > ) [ operation ] ) ) {
481+ if ( isInstrumented ( ( PostgRESTQueryBuilder . prototype as BuilderPrototype ) [ operation ] ) ) {
473482 continue ;
474483 }
475484
476- type PostgRESTOperation = keyof Pick < PostgRESTQueryBuilder , 'select' | 'insert' | 'upsert' | 'update' | 'delete' > ;
477- ( PostgRESTQueryBuilder . prototype as Record < string , any > ) [ operation as PostgRESTOperation ] = new Proxy (
478- ( PostgRESTQueryBuilder . prototype as Record < string , any > ) [ operation as PostgRESTOperation ] ,
485+ ( PostgRESTQueryBuilder . prototype as BuilderPrototype ) [ operation ] = new Proxy (
486+ ( PostgRESTQueryBuilder . prototype as BuilderPrototype ) [ operation ] ,
479487 {
480488 apply ( target , thisArg , argumentsList ) {
481489 const rv = Reflect . apply ( target , thisArg , argumentsList ) ;
@@ -490,7 +498,7 @@ function instrumentPostgRESTQueryBuilder(PostgRESTQueryBuilder: new () => PostgR
490498 } ,
491499 ) ;
492500
493- markAsInstrumented ( ( PostgRESTQueryBuilder . prototype as Record < string , any > ) [ operation ] ) ;
501+ markAsInstrumented ( ( PostgRESTQueryBuilder . prototype as BuilderPrototype ) [ operation ] ) ;
494502 }
495503}
496504
@@ -517,6 +525,7 @@ const _supabaseIntegration = ((supabaseClient: unknown) => {
517525 } ;
518526} ) satisfies IntegrationFn ;
519527
528+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
520529export const supabaseIntegration = defineIntegration ( ( options : { supabaseClient : any } ) => {
521530 return _supabaseIntegration ( options . supabaseClient ) ;
522531} ) satisfies IntegrationFn ;
0 commit comments