1- import { z } from 'zod' ;
1+ import { z } from 'zod/v4 ' ;
22import { createTRPCRouter , protectedProcedure } from '../trpc' ;
33import { and , eq , isNull , desc , sql } from 'drizzle-orm' ;
4- import { escape as sqlEscape } from 'sqlstring' ;
54import { TRPCError } from '@trpc/server' ;
65import { funnelDefinitions , chQuery } from '@databuddy/db' ;
76import { authorizeWebsiteAccess } from '../utils/auth' ;
87import { logger } from '../utils/discord-webhook' ;
98import { parseReferrer } from '../utils/referrer' ;
109
11- // Validation schemas
1210const funnelStepSchema = z . object ( {
1311 type : z . enum ( [ 'PAGE_VIEW' , 'EVENT' , 'CUSTOM' ] ) ,
1412 target : z . string ( ) . min ( 1 ) ,
1513 name : z . string ( ) . min ( 1 ) ,
16- conditions : z . record ( z . any ( ) ) . optional ( ) ,
14+ conditions : z . record ( z . string ( ) , z . any ( ) ) . optional ( ) ,
1715} ) ;
1816
1917const funnelFilterSchema = z . object ( {
@@ -52,7 +50,6 @@ const funnelAnalyticsSchema = z.object({
5250 endDate : z . string ( ) . optional ( ) ,
5351} ) ;
5452
55- // Security - allowed fields for filtering
5653const ALLOWED_FIELDS = new Set ( [
5754 'id' , 'client_id' , 'event_name' , 'anonymous_id' , 'time' , 'session_id' ,
5855 'event_type' , 'event_id' , 'session_start_time' , 'timestamp' ,
@@ -74,7 +71,6 @@ const ALLOWED_OPERATORS = new Set([
7471 'equals' , 'contains' , 'not_equals' , 'in' , 'not_in' ,
7572] ) ;
7673
77- // Utility functions
7874const buildFilterConditions = (
7975 filters : Array < { field : string ; operator : string ; value : string | string [ ] } > ,
8076 paramPrefix : string ,
@@ -117,7 +113,6 @@ const getDefaultDateRange = () => {
117113} ;
118114
119115export const funnelsRouter = createTRPCRouter ( {
120- // Get autocomplete data for funnel creation
121116 getAutocomplete : protectedProcedure
122117 . input ( analyticsDateRangeSchema )
123118 . query ( async ( { ctx, input } ) => {
@@ -236,7 +231,6 @@ export const funnelsRouter = createTRPCRouter({
236231 }
237232 } ) ,
238233
239- // Get all funnels for a website
240234 list : protectedProcedure
241235 . input ( z . object ( { websiteId : z . string ( ) } ) )
242236 . query ( async ( { ctx, input } ) => {
@@ -272,7 +266,6 @@ export const funnelsRouter = createTRPCRouter({
272266 }
273267 } ) ,
274268
275- // Get a specific funnel
276269 getById : protectedProcedure
277270 . input ( z . object ( { id : z . string ( ) , websiteId : z . string ( ) } ) )
278271 . query ( async ( { ctx, input } ) => {
@@ -311,7 +304,6 @@ export const funnelsRouter = createTRPCRouter({
311304 }
312305 } ) ,
313306
314- // Create a new funnel
315307 create : protectedProcedure
316308 . input ( createFunnelSchema )
317309 . mutation ( async ( { ctx, input } ) => {
@@ -708,7 +700,6 @@ export const funnelsRouter = createTRPCRouter({
708700 } > ( sessionReferrerQuery , params ) ;
709701
710702
711- // Group events by session
712703 const sessionEvents = new Map < string , Array < {
713704 step_number : number ,
714705 step_name : string ,
@@ -723,7 +714,6 @@ export const funnelsRouter = createTRPCRouter({
723714 sessionEvents . get ( event . session_id ) ?. push ( event ) ;
724715 }
725716
726- // Group sessions strictly by lowercased domain (fallback to 'direct')
727717 const referrerGroups = new Map < string , { parsed : ReturnType < typeof parseReferrer > , sessionIds : Set < string > } > ( ) ;
728718 for ( const [ sessionId , events ] of sessionEvents ) {
729719 if ( events . length > 0 ) {
@@ -740,7 +730,6 @@ export const funnelsRouter = createTRPCRouter({
740730 }
741731 }
742732
743- // Calculate analytics for each referrer group
744733 const referrerAnalytics = [ ] ;
745734 for ( const [ groupKey , group ] of referrerGroups ) {
746735 const stepCounts = new Map < number , Set < string > > ( ) ;
@@ -771,7 +760,6 @@ export const funnelsRouter = createTRPCRouter({
771760 } ) ;
772761 }
773762
774- // AGGREGATE BY DOMAIN
775763 const aggregated = new Map < string , {
776764 parsed : ReturnType < typeof parseReferrer > ,
777765 total_users : number ,
@@ -780,7 +768,7 @@ export const funnelsRouter = createTRPCRouter({
780768 conversion_rate_count : number
781769 } > ( ) ;
782770 for ( const { referrer, referrer_parsed, total_users, completed_users, conversion_rate } of referrerAnalytics ) {
783- const key = referrer ; // This is the domain, e.g., "github.com"
771+ const key = referrer ;
784772 if ( ! aggregated . has ( key ) ) {
785773 aggregated . set ( key , {
786774 parsed : referrer_parsed ,
0 commit comments