@litemetrics/node 0.1.1 → 0.1.3
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/README.md +25 -1
- package/dist/index.cjs +1088 -103
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +17 -6
- package/dist/index.d.ts +17 -6
- package/dist/index.js +1088 -103
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/adapters/clickhouse.ts","../src/adapters/utils.ts","../src/adapters/mongodb.ts","../src/geoip.ts","../src/useragent.ts","../src/botfilter.ts","../src/collector.ts"],"sourcesContent":["export { createCollector } from './collector';\nexport type { Collector } from './collector';\nexport { ClickHouseAdapter } from './adapters/clickhouse';\nexport { MongoDBAdapter } from './adapters/mongodb';\nexport { isBot } from './botfilter';\n\n// Re-export types from core\nexport type {\n CollectorConfig,\n DBConfig,\n DBAdapter,\n QueryParams,\n QueryResult,\n EnrichedEvent,\n Metric,\n Period,\n Site,\n CreateSiteRequest,\n UpdateSiteRequest,\n EventListParams,\n EventListResult,\n EventListItem,\n UserListParams,\n UserListResult,\n UserDetail,\n} from '@litemetrics/core';\n","import type { DBAdapter, EnrichedEvent, QueryParams, QueryResult, QueryDataPoint, Granularity, TimeSeriesParams, TimeSeriesResult, RetentionParams, RetentionResult, RetentionCohort, Site, CreateSiteRequest, UpdateSiteRequest, EventListParams, EventListResult, EventListItem, UserListParams, UserListResult, UserDetail } from '@litemetrics/core';\nimport { createClient, type ClickHouseClient } from '@clickhouse/client';\nimport { resolvePeriod, previousPeriodRange, autoGranularity, fillBuckets, granularityToDateFormat, getISOWeek, generateSiteId, generateSecretKey } from './utils';\n\nconst EVENTS_TABLE = 'litemetrics_events';\nconst SITES_TABLE = 'litemetrics_sites';\n\nconst CREATE_EVENTS_TABLE = `\nCREATE TABLE IF NOT EXISTS ${EVENTS_TABLE} (\n event_id UUID DEFAULT generateUUIDv4(),\n site_id LowCardinality(String),\n type LowCardinality(String),\n timestamp DateTime64(3),\n session_id String,\n visitor_id String,\n url Nullable(String),\n referrer Nullable(String),\n title Nullable(String),\n event_name Nullable(String),\n properties Nullable(String),\n user_id Nullable(String),\n traits Nullable(String),\n country LowCardinality(Nullable(String)),\n city Nullable(String),\n region Nullable(String),\n device_type LowCardinality(Nullable(String)),\n browser LowCardinality(Nullable(String)),\n os LowCardinality(Nullable(String)),\n language LowCardinality(Nullable(String)),\n timezone Nullable(String),\n screen_width Nullable(UInt16),\n screen_height Nullable(UInt16),\n utm_source Nullable(String),\n utm_medium Nullable(String),\n utm_campaign Nullable(String),\n utm_term Nullable(String),\n utm_content Nullable(String),\n ip Nullable(String),\n created_at DateTime64(3) DEFAULT now64(3)\n) ENGINE = MergeTree()\n PARTITION BY toYYYYMM(timestamp)\n ORDER BY (site_id, timestamp, visitor_id)\n SETTINGS index_granularity = 8192\n`;\n\nconst CREATE_SITES_TABLE = `\nCREATE TABLE IF NOT EXISTS ${SITES_TABLE} (\n site_id String,\n secret_key String,\n name String,\n domain Nullable(String),\n allowed_origins Nullable(String),\n created_at DateTime64(3),\n updated_at DateTime64(3),\n version UInt64,\n is_deleted UInt8 DEFAULT 0\n) ENGINE = ReplacingMergeTree(version)\n ORDER BY (site_id)\n SETTINGS index_granularity = 8192\n`;\n\n/** Convert JS date/ISO string to ClickHouse DateTime64 format: '2026-02-07 14:22:08.339' */\nfunction toCHDateTime(d: string | Date): string {\n const iso = typeof d === 'string' ? d : d.toISOString();\n return iso.replace('T', ' ').replace('Z', '');\n}\n\nexport class ClickHouseAdapter implements DBAdapter {\n private client: ClickHouseClient;\n\n constructor(url: string) {\n this.client = createClient({\n url,\n clickhouse_settings: {\n wait_end_of_query: 1,\n },\n });\n }\n\n async init(): Promise<void> {\n await this.client.command({ query: CREATE_EVENTS_TABLE });\n await this.client.command({ query: CREATE_SITES_TABLE });\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n\n // ─── Event Insertion ──────────────────────────────────────\n\n async insertEvents(events: EnrichedEvent[]): Promise<void> {\n if (events.length === 0) return;\n\n const rows = events.map((e) => ({\n site_id: e.siteId,\n type: e.type,\n timestamp: toCHDateTime(new Date(e.timestamp)),\n session_id: e.sessionId,\n visitor_id: e.visitorId,\n url: e.url ?? null,\n referrer: e.referrer ?? null,\n title: e.title ?? null,\n event_name: e.name ?? null,\n properties: e.properties ? JSON.stringify(e.properties) : null,\n user_id: e.userId ?? null,\n traits: e.traits ? JSON.stringify(e.traits) : null,\n country: e.geo?.country ?? null,\n city: e.geo?.city ?? null,\n region: e.geo?.region ?? null,\n device_type: e.device?.type ?? null,\n browser: e.device?.browser ?? null,\n os: e.device?.os ?? null,\n language: e.language ?? null,\n timezone: e.timezone ?? null,\n screen_width: e.screen?.width ?? null,\n screen_height: e.screen?.height ?? null,\n utm_source: e.utm?.source ?? null,\n utm_medium: e.utm?.medium ?? null,\n utm_campaign: e.utm?.campaign ?? null,\n utm_term: e.utm?.term ?? null,\n utm_content: e.utm?.content ?? null,\n ip: e.ip ?? null,\n }));\n\n await this.client.insert({\n table: EVENTS_TABLE,\n values: rows,\n format: 'JSONEachRow',\n });\n }\n\n // ─── Analytics Queries ──────────────────────────────────────\n\n async query(q: QueryParams): Promise<QueryResult> {\n const { dateRange, period } = resolvePeriod(q);\n const siteId = q.siteId;\n const limit = q.limit ?? 10;\n\n const params = {\n siteId,\n from: toCHDateTime(dateRange.from),\n to: toCHDateTime(dateRange.to),\n limit,\n };\n\n let data: QueryDataPoint[] = [];\n let total = 0;\n\n switch (q.metric) {\n case 'pageviews': {\n const rows = await this.queryRows<{ value: string }>(\n `SELECT count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'pageview'`,\n params,\n );\n total = Number(rows[0]?.value ?? 0);\n data = [{ key: 'pageviews', value: total }];\n break;\n }\n\n case 'visitors': {\n const rows = await this.queryRows<{ value: string }>(\n `SELECT uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}`,\n params,\n );\n total = Number(rows[0]?.value ?? 0);\n data = [{ key: 'visitors', value: total }];\n break;\n }\n\n case 'sessions': {\n const rows = await this.queryRows<{ value: string }>(\n `SELECT uniq(session_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}`,\n params,\n );\n total = Number(rows[0]?.value ?? 0);\n data = [{ key: 'sessions', value: total }];\n break;\n }\n\n case 'events': {\n const rows = await this.queryRows<{ value: string }>(\n `SELECT count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'event'`,\n params,\n );\n total = Number(rows[0]?.value ?? 0);\n data = [{ key: 'events', value: total }];\n break;\n }\n\n case 'top_pages': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT url AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'pageview'\n AND url IS NOT NULL\n GROUP BY url\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n params,\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_referrers': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT referrer AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'pageview'\n AND referrer IS NOT NULL\n AND referrer != ''\n GROUP BY referrer\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n params,\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_countries': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT country AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND country IS NOT NULL\n GROUP BY country\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n params,\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_cities': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT city AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND city IS NOT NULL\n GROUP BY city\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n params,\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_events': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT event_name AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'event'\n AND event_name IS NOT NULL\n GROUP BY event_name\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n params,\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_devices': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT device_type AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND device_type IS NOT NULL\n GROUP BY device_type\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n params,\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_browsers': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT browser AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND browser IS NOT NULL\n GROUP BY browser\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n params,\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_os': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT os AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND os IS NOT NULL\n GROUP BY os\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n params,\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n }\n\n const result: QueryResult = { metric: q.metric, period, data, total };\n\n // Comparison with previous period\n if (q.compare && ['pageviews', 'visitors', 'sessions', 'events'].includes(q.metric)) {\n const prevRange = previousPeriodRange(dateRange);\n const prevResult = await this.query({\n ...q,\n compare: false,\n period: 'custom',\n dateFrom: prevRange.from,\n dateTo: prevRange.to,\n });\n result.previousTotal = prevResult.total;\n if (prevResult.total > 0) {\n result.changePercent = Math.round(((total - prevResult.total) / prevResult.total) * 1000) / 10;\n } else if (total > 0) {\n result.changePercent = 100;\n } else {\n result.changePercent = 0;\n }\n }\n\n return result;\n }\n\n // ─── Time Series ──────────────────────────────────────\n\n async queryTimeSeries(params: TimeSeriesParams): Promise<TimeSeriesResult> {\n const { dateRange, period } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n\n const granularity = params.granularity ?? autoGranularity(period);\n const bucketFn = this.granularityToClickHouseFunc(granularity);\n const dateFormat = granularityToDateFormat(granularity);\n\n const typeFilter = params.metric === 'pageviews' ? `AND type = 'pageview'` : '';\n\n let sql: string;\n if (params.metric === 'visitors' || params.metric === 'sessions') {\n const field = params.metric === 'visitors' ? 'visitor_id' : 'session_id';\n sql = `\n SELECT ${bucketFn} AS bucket, uniq(${field}) AS value\n FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n ${typeFilter}\n GROUP BY bucket\n ORDER BY bucket ASC\n `;\n } else {\n sql = `\n SELECT ${bucketFn} AS bucket, count() AS value\n FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n ${typeFilter}\n GROUP BY bucket\n ORDER BY bucket ASC\n `;\n }\n\n const rows = await this.queryRows<{ bucket: string; value: string }>(sql, {\n siteId: params.siteId,\n from: toCHDateTime(dateRange.from),\n to: toCHDateTime(dateRange.to),\n });\n\n // Convert ClickHouse bucket format to match the dateFormat used by fillBuckets\n const mappedRows = rows.map((r) => ({\n _id: this.convertClickHouseBucket(r.bucket, granularity),\n value: Number(r.value),\n }));\n\n const data = fillBuckets(\n new Date(dateRange.from),\n new Date(dateRange.to),\n granularity,\n dateFormat,\n mappedRows,\n );\n\n return { metric: params.metric, granularity, data };\n }\n\n private granularityToClickHouseFunc(g: Granularity): string {\n switch (g) {\n case 'hour': return 'toStartOfHour(timestamp)';\n case 'day': return 'toStartOfDay(timestamp)';\n case 'week': return 'toStartOfWeek(timestamp, 1)'; // 1 = Monday\n case 'month': return 'toStartOfMonth(timestamp)';\n }\n }\n\n private convertClickHouseBucket(bucket: string, granularity: Granularity): string {\n // ClickHouse returns ISO datetime strings, convert to the format used by fillBuckets\n const date = new Date(bucket);\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n const h = String(date.getHours()).padStart(2, '0');\n\n switch (granularity) {\n case 'hour': return `${y}-${m}-${d}T${h}:00`;\n case 'day': return `${y}-${m}-${d}`;\n case 'week': {\n const jan4 = new Date(y, 0, 4);\n const dayOfYear = Math.ceil((date.getTime() - new Date(y, 0, 1).getTime()) / 86400000) + 1;\n const jan4Day = jan4.getDay() || 7;\n const weekNum = Math.ceil((dayOfYear + jan4Day - 1) / 7);\n return `${y}-W${String(weekNum).padStart(2, '0')}`;\n }\n case 'month': return `${y}-${m}`;\n }\n }\n\n // ─── Retention ──────────────────────────────────────\n\n async queryRetention(params: RetentionParams): Promise<RetentionResult> {\n const weeks = params.weeks ?? 8;\n const now = new Date();\n const startDate = new Date(now.getTime() - weeks * 7 * 24 * 60 * 60 * 1000);\n\n const rows = await this.queryRows<{\n visitor_id: string;\n first_event: string;\n active_weeks: string[];\n }>(\n `SELECT\n visitor_id,\n min(timestamp) AS first_event,\n groupUniqArray(toStartOfWeek(timestamp, 1)) AS active_weeks\n FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {since:String}\n GROUP BY visitor_id`,\n {\n siteId: params.siteId,\n since: toCHDateTime(startDate),\n },\n );\n\n // Group visitors by cohort week\n const cohortMap = new Map<string, { visitors: Set<string>; weekSets: Map<string, Set<string>> }>();\n\n for (const v of rows) {\n const firstDate = new Date(v.first_event);\n const cohortWeek = getISOWeek(firstDate);\n if (!cohortMap.has(cohortWeek)) {\n cohortMap.set(cohortWeek, { visitors: new Set(), weekSets: new Map() });\n }\n const cohort = cohortMap.get(cohortWeek)!;\n cohort.visitors.add(v.visitor_id);\n\n // active_weeks from ClickHouse are DateTime strings\n const eventWeeks = (Array.isArray(v.active_weeks) ? v.active_weeks : []).map((w: string) => {\n const d = new Date(w);\n return getISOWeek(d);\n });\n\n for (const w of eventWeeks) {\n if (!cohort.weekSets.has(w)) {\n cohort.weekSets.set(w, new Set());\n }\n cohort.weekSets.get(w)!.add(v.visitor_id);\n }\n }\n\n const sortedWeeks = Array.from(cohortMap.keys()).sort();\n const cohorts: RetentionCohort[] = sortedWeeks.map((week) => {\n const cohort = cohortMap.get(week)!;\n const size = cohort.visitors.size;\n\n const retention: number[] = [];\n const weekIndex = sortedWeeks.indexOf(week);\n for (let i = 0; i < weeks && (weekIndex + i) < sortedWeeks.length; i++) {\n const targetWeek = sortedWeeks[weekIndex + i];\n const returnedCount = cohort.weekSets.get(targetWeek)?.size ?? 0;\n retention.push(size > 0 ? Math.round((returnedCount / size) * 1000) / 10 : 0);\n }\n\n return { week, size, retention };\n });\n\n return { cohorts };\n }\n\n // ─── Event Listing ──────────────────────────────────────\n\n async listEvents(params: EventListParams): Promise<EventListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const conditions: string[] = [`site_id = {siteId:String}`];\n const queryParams: Record<string, unknown> = { siteId: params.siteId, limit, offset };\n\n if (params.type) {\n conditions.push(`type = {type:String}`);\n queryParams.type = params.type;\n }\n if (params.eventName) {\n conditions.push(`event_name = {eventName:String}`);\n queryParams.eventName = params.eventName;\n }\n if (params.visitorId) {\n conditions.push(`visitor_id = {visitorId:String}`);\n queryParams.visitorId = params.visitorId;\n }\n if (params.userId) {\n conditions.push(`user_id = {userId:String}`);\n queryParams.userId = params.userId;\n }\n\n if (params.period || params.dateFrom) {\n const { dateRange } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n conditions.push(`timestamp >= {from:String} AND timestamp <= {to:String}`);\n queryParams.from = toCHDateTime(dateRange.from);\n queryParams.to = toCHDateTime(dateRange.to);\n }\n\n const where = conditions.join(' AND ');\n\n const [events, countRows] = await Promise.all([\n this.queryRows<Record<string, unknown>>(\n `SELECT event_id, type, timestamp, session_id, visitor_id, url, referrer, title,\n event_name, properties, user_id, traits, country, city, region,\n device_type, browser, os, language,\n utm_source, utm_medium, utm_campaign, utm_term, utm_content\n FROM ${EVENTS_TABLE}\n WHERE ${where}\n ORDER BY timestamp DESC\n LIMIT {limit:UInt32}\n OFFSET {offset:UInt32}`,\n queryParams,\n ),\n this.queryRows<{ total: string }>(\n `SELECT count() AS total FROM ${EVENTS_TABLE} WHERE ${where}`,\n queryParams,\n ),\n ]);\n\n return {\n events: events.map((e) => this.toEventListItem(e)),\n total: Number(countRows[0]?.total ?? 0),\n limit,\n offset,\n };\n }\n\n // ─── User Listing ──────────────────────────────────────\n\n async listUsers(params: UserListParams): Promise<UserListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const conditions: string[] = [`site_id = {siteId:String}`];\n const queryParams: Record<string, unknown> = { siteId: params.siteId, limit, offset };\n\n if (params.search) {\n conditions.push(`(visitor_id ILIKE {search:String} OR user_id ILIKE {search:String})`);\n queryParams.search = `%${params.search}%`;\n }\n\n const where = conditions.join(' AND ');\n\n const [userRows, countRows] = await Promise.all([\n this.queryRows<Record<string, unknown>>(\n `SELECT\n visitor_id,\n anyLast(user_id) AS userId,\n anyLast(traits) AS traits,\n min(timestamp) AS firstSeen,\n max(timestamp) AS lastSeen,\n count() AS totalEvents,\n countIf(type = 'pageview') AS totalPageviews,\n uniq(session_id) AS totalSessions,\n anyLast(url) AS lastUrl,\n anyLast(device_type) AS device_type,\n anyLast(browser) AS browser,\n anyLast(os) AS os,\n anyLast(country) AS country,\n anyLast(city) AS city,\n anyLast(region) AS region,\n anyLast(language) AS language\n FROM ${EVENTS_TABLE}\n WHERE ${where}\n GROUP BY visitor_id\n ORDER BY lastSeen DESC\n LIMIT {limit:UInt32}\n OFFSET {offset:UInt32}`,\n queryParams,\n ),\n this.queryRows<{ total: string }>(\n `SELECT count() AS total FROM (\n SELECT visitor_id FROM ${EVENTS_TABLE}\n WHERE ${where}\n GROUP BY visitor_id\n )`,\n queryParams,\n ),\n ]);\n\n const users: UserDetail[] = userRows.map((u) => ({\n visitorId: String(u.visitor_id),\n userId: u.userId ? String(u.userId) : undefined,\n traits: this.parseJSON(u.traits as string | null),\n firstSeen: new Date(String(u.firstSeen)).toISOString(),\n lastSeen: new Date(String(u.lastSeen)).toISOString(),\n totalEvents: Number(u.totalEvents),\n totalPageviews: Number(u.totalPageviews),\n totalSessions: Number(u.totalSessions),\n lastUrl: u.lastUrl ? String(u.lastUrl) : undefined,\n device: u.device_type ? { type: String(u.device_type), browser: String(u.browser ?? ''), os: String(u.os ?? '') } : undefined,\n geo: u.country ? { country: String(u.country), city: u.city ? String(u.city) : undefined, region: u.region ? String(u.region) : undefined } : undefined,\n language: u.language ? String(u.language) : undefined,\n }));\n\n return {\n users,\n total: Number(countRows[0]?.total ?? 0),\n limit,\n offset,\n };\n }\n\n async getUserDetail(siteId: string, visitorId: string): Promise<UserDetail | null> {\n const result = await this.listUsers({ siteId, search: visitorId, limit: 1 });\n const user = result.users.find((u) => u.visitorId === visitorId);\n return user ?? null;\n }\n\n async getUserEvents(siteId: string, visitorId: string, params: EventListParams): Promise<EventListResult> {\n return this.listEvents({ ...params, siteId, visitorId });\n }\n\n // ─── Site Management ──────────────────────────────────────\n\n async createSite(data: CreateSiteRequest): Promise<Site> {\n const now = new Date();\n const nowISO = now.toISOString();\n const nowCH = toCHDateTime(now);\n const site: Site = {\n siteId: generateSiteId(),\n secretKey: generateSecretKey(),\n name: data.name,\n domain: data.domain,\n allowedOrigins: data.allowedOrigins,\n createdAt: nowISO,\n updatedAt: nowISO,\n };\n\n await this.client.insert({\n table: SITES_TABLE,\n values: [{\n site_id: site.siteId,\n secret_key: site.secretKey,\n name: site.name,\n domain: site.domain ?? null,\n allowed_origins: site.allowedOrigins ? JSON.stringify(site.allowedOrigins) : null,\n created_at: nowCH,\n updated_at: nowCH,\n version: 1,\n is_deleted: 0,\n }],\n format: 'JSONEachRow',\n });\n\n return site;\n }\n\n async getSite(siteId: string): Promise<Site | null> {\n const rows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, created_at, updated_at\n FROM ${SITES_TABLE} FINAL\n WHERE site_id = {siteId:String} AND is_deleted = 0`,\n { siteId },\n );\n return rows.length > 0 ? this.toSite(rows[0]) : null;\n }\n\n async getSiteBySecret(secretKey: string): Promise<Site | null> {\n const rows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, created_at, updated_at\n FROM ${SITES_TABLE} FINAL\n WHERE secret_key = {secretKey:String} AND is_deleted = 0`,\n { secretKey },\n );\n return rows.length > 0 ? this.toSite(rows[0]) : null;\n }\n\n async listSites(): Promise<Site[]> {\n const rows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, created_at, updated_at\n FROM ${SITES_TABLE} FINAL\n WHERE is_deleted = 0\n ORDER BY created_at DESC`,\n {},\n );\n return rows.map((r) => this.toSite(r));\n }\n\n async updateSite(siteId: string, data: UpdateSiteRequest): Promise<Site | null> {\n // Read current site with version\n const currentRows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, created_at, updated_at, version\n FROM ${SITES_TABLE} FINAL\n WHERE site_id = {siteId:String} AND is_deleted = 0`,\n { siteId },\n );\n if (currentRows.length === 0) return null;\n\n const current = currentRows[0];\n const now = new Date();\n const nowISO = now.toISOString();\n const nowCH = toCHDateTime(now);\n const newVersion = Number(current.version) + 1;\n\n const newName = data.name !== undefined ? data.name : String(current.name);\n const newDomain = data.domain !== undefined ? (data.domain || null) : (current.domain ? String(current.domain) : null);\n const newOrigins = data.allowedOrigins !== undefined\n ? (data.allowedOrigins.length > 0 ? JSON.stringify(data.allowedOrigins) : null)\n : (current.allowed_origins ? String(current.allowed_origins) : null);\n\n await this.client.insert({\n table: SITES_TABLE,\n values: [{\n site_id: String(current.site_id),\n secret_key: String(current.secret_key),\n name: newName,\n domain: newDomain,\n allowed_origins: newOrigins,\n created_at: toCHDateTime(String(current.created_at)),\n updated_at: nowCH,\n version: newVersion,\n is_deleted: 0,\n }],\n format: 'JSONEachRow',\n });\n\n return {\n siteId: String(current.site_id),\n secretKey: String(current.secret_key),\n name: newName,\n domain: newDomain ?? undefined,\n allowedOrigins: newOrigins ? JSON.parse(newOrigins) : undefined,\n createdAt: String(current.created_at),\n updatedAt: nowISO,\n };\n }\n\n async deleteSite(siteId: string): Promise<boolean> {\n const currentRows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, created_at, version\n FROM ${SITES_TABLE} FINAL\n WHERE site_id = {siteId:String} AND is_deleted = 0`,\n { siteId },\n );\n if (currentRows.length === 0) return false;\n\n const current = currentRows[0];\n const nowCH = toCHDateTime(new Date());\n\n await this.client.insert({\n table: SITES_TABLE,\n values: [{\n site_id: String(current.site_id),\n secret_key: String(current.secret_key),\n name: String(current.name),\n domain: current.domain ? String(current.domain) : null,\n allowed_origins: current.allowed_origins ? String(current.allowed_origins) : null,\n created_at: toCHDateTime(String(current.created_at)),\n updated_at: nowCH,\n version: Number(current.version) + 1,\n is_deleted: 1,\n }],\n format: 'JSONEachRow',\n });\n\n return true;\n }\n\n async regenerateSecret(siteId: string): Promise<Site | null> {\n const currentRows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, created_at, version\n FROM ${SITES_TABLE} FINAL\n WHERE site_id = {siteId:String} AND is_deleted = 0`,\n { siteId },\n );\n if (currentRows.length === 0) return null;\n\n const current = currentRows[0];\n const now = new Date();\n const nowISO = now.toISOString();\n const nowCH = toCHDateTime(now);\n const newSecret = generateSecretKey();\n\n await this.client.insert({\n table: SITES_TABLE,\n values: [{\n site_id: String(current.site_id),\n secret_key: newSecret,\n name: String(current.name),\n domain: current.domain ? String(current.domain) : null,\n allowed_origins: current.allowed_origins ? String(current.allowed_origins) : null,\n created_at: toCHDateTime(String(current.created_at)),\n updated_at: nowCH,\n version: Number(current.version) + 1,\n is_deleted: 0,\n }],\n format: 'JSONEachRow',\n });\n\n return {\n siteId: String(current.site_id),\n secretKey: newSecret,\n name: String(current.name),\n domain: current.domain ? String(current.domain) : undefined,\n allowedOrigins: current.allowed_origins ? JSON.parse(String(current.allowed_origins)) : undefined,\n createdAt: String(current.created_at),\n updatedAt: nowISO,\n };\n }\n\n // ─── Helpers ─────────────────────────────────────────────\n\n private async queryRows<T>(query: string, query_params: Record<string, unknown>): Promise<T[]> {\n const result = await this.client.query({\n query,\n query_params,\n format: 'JSONEachRow',\n });\n return result.json<T>();\n }\n\n private toSite(row: Record<string, unknown>): Site {\n return {\n siteId: String(row.site_id),\n secretKey: String(row.secret_key),\n name: String(row.name),\n domain: row.domain ? String(row.domain) : undefined,\n allowedOrigins: row.allowed_origins ? JSON.parse(String(row.allowed_origins)) : undefined,\n createdAt: new Date(String(row.created_at)).toISOString(),\n updatedAt: new Date(String(row.updated_at)).toISOString(),\n };\n }\n\n private toEventListItem(row: Record<string, unknown>): EventListItem {\n return {\n id: String(row.event_id ?? ''),\n type: String(row.type) as EventListItem['type'],\n timestamp: new Date(String(row.timestamp)).toISOString(),\n visitorId: String(row.visitor_id),\n sessionId: String(row.session_id),\n url: row.url ? String(row.url) : undefined,\n referrer: row.referrer ? String(row.referrer) : undefined,\n title: row.title ? String(row.title) : undefined,\n name: row.event_name ? String(row.event_name) : undefined,\n properties: this.parseJSON(row.properties as string | null),\n userId: row.user_id ? String(row.user_id) : undefined,\n traits: this.parseJSON(row.traits as string | null),\n geo: row.country ? {\n country: String(row.country),\n city: row.city ? String(row.city) : undefined,\n region: row.region ? String(row.region) : undefined,\n } : undefined,\n device: row.device_type ? {\n type: String(row.device_type),\n browser: String(row.browser ?? ''),\n os: String(row.os ?? ''),\n } : undefined,\n language: row.language ? String(row.language) : undefined,\n utm: row.utm_source ? {\n source: row.utm_source ? String(row.utm_source) : undefined,\n medium: row.utm_medium ? String(row.utm_medium) : undefined,\n campaign: row.utm_campaign ? String(row.utm_campaign) : undefined,\n term: row.utm_term ? String(row.utm_term) : undefined,\n content: row.utm_content ? String(row.utm_content) : undefined,\n } : undefined,\n };\n }\n\n private parseJSON(str: string | null | undefined): Record<string, unknown> | undefined {\n if (!str) return undefined;\n try {\n return JSON.parse(str);\n } catch {\n return undefined;\n }\n }\n}\n","import type { QueryParams, Period, Granularity, TimeSeriesPoint } from '@litemetrics/core';\nimport { randomBytes } from 'crypto';\n\nexport function resolvePeriod(q: Pick<QueryParams, 'period' | 'dateFrom' | 'dateTo'>): { dateRange: { from: string; to: string }; period: Period } {\n const now = new Date();\n const period = q.period ?? '7d';\n\n if (period === 'custom' && q.dateFrom && q.dateTo) {\n return { dateRange: { from: q.dateFrom, to: q.dateTo }, period };\n }\n\n const to = now.toISOString();\n let from: Date;\n\n switch (period) {\n case '1h': from = new Date(now.getTime() - 60 * 60 * 1000); break;\n case '24h': from = new Date(now.getTime() - 24 * 60 * 60 * 1000); break;\n case '7d': from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); break;\n case '30d': from = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); break;\n case '90d': from = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000); break;\n default: from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); break;\n }\n\n return { dateRange: { from: from.toISOString(), to }, period };\n}\n\nexport function previousPeriodRange(currentRange: { from: string; to: string }): { from: string; to: string } {\n const from = new Date(currentRange.from);\n const to = new Date(currentRange.to);\n const duration = to.getTime() - from.getTime();\n const prevTo = new Date(from.getTime() - 1);\n const prevFrom = new Date(prevTo.getTime() - duration);\n return { from: prevFrom.toISOString(), to: prevTo.toISOString() };\n}\n\nexport function autoGranularity(period: Period): Granularity {\n switch (period) {\n case '1h': return 'hour';\n case '24h': return 'hour';\n case '7d': return 'day';\n case '30d': return 'day';\n case '90d': return 'week';\n default: return 'day';\n }\n}\n\nexport function granularityToDateFormat(g: Granularity): string {\n switch (g) {\n case 'hour': return '%Y-%m-%dT%H:00';\n case 'day': return '%Y-%m-%d';\n case 'week': return '%G-W%V';\n case 'month': return '%Y-%m';\n }\n}\n\nexport function fillBuckets(\n from: Date,\n to: Date,\n granularity: Granularity,\n dateFormat: string,\n rows: { _id: string; value: number }[],\n): TimeSeriesPoint[] {\n const map = new Map(rows.map((r) => [r._id, r.value]));\n const points: TimeSeriesPoint[] = [];\n const current = new Date(from);\n\n // Align to bucket start\n if (granularity === 'hour') {\n current.setMinutes(0, 0, 0);\n } else if (granularity === 'day') {\n current.setHours(0, 0, 0, 0);\n } else if (granularity === 'week') {\n const day = current.getDay();\n const diff = day === 0 ? -6 : 1 - day;\n current.setDate(current.getDate() + diff);\n current.setHours(0, 0, 0, 0);\n } else if (granularity === 'month') {\n current.setDate(1);\n current.setHours(0, 0, 0, 0);\n }\n\n while (current <= to) {\n const key = formatDateBucket(current, dateFormat);\n points.push({ date: current.toISOString(), value: map.get(key) ?? 0 });\n\n if (granularity === 'hour') {\n current.setHours(current.getHours() + 1);\n } else if (granularity === 'day') {\n current.setDate(current.getDate() + 1);\n } else if (granularity === 'week') {\n current.setDate(current.getDate() + 7);\n } else if (granularity === 'month') {\n current.setMonth(current.getMonth() + 1);\n }\n }\n\n return points;\n}\n\nexport function formatDateBucket(date: Date, format: string): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n const h = String(date.getHours()).padStart(2, '0');\n\n if (format === '%Y-%m-%dT%H:00') return `${y}-${m}-${d}T${h}:00`;\n if (format === '%Y-%m-%d') return `${y}-${m}-${d}`;\n if (format === '%Y-%m') return `${y}-${m}`;\n if (format === '%G-W%V') {\n const jan4 = new Date(y, 0, 4);\n const dayOfYear = Math.ceil((date.getTime() - new Date(y, 0, 1).getTime()) / 86400000) + 1;\n const jan4Day = jan4.getDay() || 7;\n const weekNum = Math.ceil((dayOfYear + jan4Day - 1) / 7);\n return `${y}-W${String(weekNum).padStart(2, '0')}`;\n }\n return date.toISOString();\n}\n\nexport function getISOWeek(date: Date): string {\n const y = date.getFullYear();\n const jan4 = new Date(y, 0, 4);\n const dayOfYear = Math.ceil((date.getTime() - new Date(y, 0, 1).getTime()) / 86400000) + 1;\n const jan4Day = jan4.getDay() || 7;\n const weekNum = Math.ceil((dayOfYear + jan4Day - 1) / 7);\n return `${y}-W${String(weekNum).padStart(2, '0')}`;\n}\n\nexport function generateSiteId(): string {\n const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';\n const bytes = randomBytes(12);\n let id = '';\n for (let i = 0; i < 12; i++) id += chars[bytes[i] % chars.length];\n return `site_${id}`;\n}\n\nexport function generateSecretKey(): string {\n return `sk_${randomBytes(32).toString('hex')}`;\n}\n","import type { DBAdapter, EnrichedEvent, QueryParams, QueryResult, QueryDataPoint, TimeSeriesParams, TimeSeriesResult, RetentionParams, RetentionResult, RetentionCohort, Site, CreateSiteRequest, UpdateSiteRequest, EventListParams, EventListResult, EventListItem, UserListParams, UserListResult, UserDetail } from '@litemetrics/core';\nimport { MongoClient, type Collection, type Db } from 'mongodb';\nimport { resolvePeriod, previousPeriodRange, autoGranularity, granularityToDateFormat, fillBuckets, getISOWeek, generateSiteId, generateSecretKey } from './utils';\n\ninterface EventDocument {\n site_id: string;\n type: string;\n timestamp: Date;\n session_id: string;\n visitor_id: string;\n url: string | null;\n referrer: string | null;\n title: string | null;\n event_name: string | null;\n properties: Record<string, unknown> | null;\n user_id: string | null;\n traits: Record<string, unknown> | null;\n country: string | null;\n city: string | null;\n region: string | null;\n device_type: string | null;\n browser: string | null;\n os: string | null;\n language: string | null;\n timezone: string | null;\n screen_width: number | null;\n screen_height: number | null;\n utm_source: string | null;\n utm_medium: string | null;\n utm_campaign: string | null;\n utm_term: string | null;\n utm_content: string | null;\n ip: string | null;\n created_at: Date;\n}\n\ninterface SiteDocument {\n site_id: string;\n secret_key: string;\n name: string;\n domain: string | null;\n allowed_origins: string[] | null;\n created_at: Date;\n updated_at: Date;\n}\n\nconst EVENTS_COLLECTION = 'litemetrics_events';\nconst SITES_COLLECTION = 'litemetrics_sites';\n\nexport class MongoDBAdapter implements DBAdapter {\n private client: MongoClient;\n private db!: Db;\n private collection!: Collection<EventDocument>;\n private sites!: Collection<SiteDocument>;\n\n constructor(url: string) {\n this.client = new MongoClient(url);\n }\n\n async init(): Promise<void> {\n await this.client.connect();\n this.db = this.client.db();\n this.collection = this.db.collection<EventDocument>(EVENTS_COLLECTION);\n this.sites = this.db.collection<SiteDocument>(SITES_COLLECTION);\n\n await Promise.all([\n this.collection.createIndex({ site_id: 1, timestamp: -1 }),\n this.collection.createIndex({ site_id: 1, type: 1 }),\n this.collection.createIndex({ site_id: 1, visitor_id: 1 }),\n this.collection.createIndex({ site_id: 1, session_id: 1 }),\n this.sites.createIndex({ site_id: 1 }, { unique: true }),\n this.sites.createIndex({ secret_key: 1 }),\n ]);\n }\n\n async insertEvents(events: EnrichedEvent[]): Promise<void> {\n if (events.length === 0) return;\n\n const docs: EventDocument[] = events.map((e) => ({\n site_id: e.siteId,\n type: e.type,\n timestamp: new Date(e.timestamp),\n session_id: e.sessionId,\n visitor_id: e.visitorId,\n url: e.url ?? null,\n referrer: e.referrer ?? null,\n title: e.title ?? null,\n event_name: e.name ?? null,\n properties: e.properties ?? null,\n user_id: e.userId ?? null,\n traits: e.traits ?? null,\n country: e.geo?.country ?? null,\n city: e.geo?.city ?? null,\n region: e.geo?.region ?? null,\n device_type: e.device?.type ?? null,\n browser: e.device?.browser ?? null,\n os: e.device?.os ?? null,\n language: e.language ?? null,\n timezone: e.timezone ?? null,\n screen_width: e.screen?.width ?? null,\n screen_height: e.screen?.height ?? null,\n utm_source: e.utm?.source ?? null,\n utm_medium: e.utm?.medium ?? null,\n utm_campaign: e.utm?.campaign ?? null,\n utm_term: e.utm?.term ?? null,\n utm_content: e.utm?.content ?? null,\n ip: e.ip ?? null,\n created_at: new Date(),\n }));\n\n await this.collection.insertMany(docs);\n }\n\n async query(q: QueryParams): Promise<QueryResult> {\n const { dateRange, period } = resolvePeriod(q);\n const siteId = q.siteId;\n const limit = q.limit ?? 10;\n\n const baseMatch = {\n site_id: siteId,\n timestamp: { $gte: new Date(dateRange.from), $lte: new Date(dateRange.to) },\n };\n\n let data: QueryDataPoint[] = [];\n let total = 0;\n\n switch (q.metric) {\n case 'pageviews': {\n const [result] = await this.collection.aggregate<{ count: number }>([\n { $match: { ...baseMatch, type: 'pageview' } },\n { $count: 'count' },\n ]).toArray();\n total = result?.count ?? 0;\n data = [{ key: 'pageviews', value: total }];\n break;\n }\n\n case 'visitors': {\n const [result] = await this.collection.aggregate<{ count: number }>([\n { $match: baseMatch },\n { $group: { _id: '$visitor_id' } },\n { $count: 'count' },\n ]).toArray();\n total = result?.count ?? 0;\n data = [{ key: 'visitors', value: total }];\n break;\n }\n\n case 'sessions': {\n const [result] = await this.collection.aggregate<{ count: number }>([\n { $match: baseMatch },\n { $group: { _id: '$session_id' } },\n { $count: 'count' },\n ]).toArray();\n total = result?.count ?? 0;\n data = [{ key: 'sessions', value: total }];\n break;\n }\n\n case 'events': {\n const [result] = await this.collection.aggregate<{ count: number }>([\n { $match: { ...baseMatch, type: 'event' } },\n { $count: 'count' },\n ]).toArray();\n total = result?.count ?? 0;\n data = [{ key: 'events', value: total }];\n break;\n }\n\n case 'top_pages': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, type: 'pageview', url: { $ne: null } } },\n { $group: { _id: '$url', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_referrers': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, type: 'pageview', referrer: { $nin: [null, ''] } } },\n { $group: { _id: '$referrer', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_countries': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, country: { $ne: null } } },\n { $group: { _id: '$country', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_cities': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, city: { $ne: null } } },\n { $group: { _id: '$city', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_events': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, type: 'event', event_name: { $ne: null } } },\n { $group: { _id: '$event_name', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_devices': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, device_type: { $ne: null } } },\n { $group: { _id: '$device_type', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_browsers': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, browser: { $ne: null } } },\n { $group: { _id: '$browser', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_os': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, os: { $ne: null } } },\n { $group: { _id: '$os', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n }\n\n const result: QueryResult = { metric: q.metric, period, data, total };\n\n // Comparison with previous period\n if (q.compare && ['pageviews', 'visitors', 'sessions', 'events'].includes(q.metric)) {\n const prevRange = previousPeriodRange(dateRange);\n const prevResult = await this.query({\n ...q,\n compare: false,\n period: 'custom',\n dateFrom: prevRange.from,\n dateTo: prevRange.to,\n });\n result.previousTotal = prevResult.total;\n if (prevResult.total > 0) {\n result.changePercent = Math.round(((total - prevResult.total) / prevResult.total) * 1000) / 10;\n } else if (total > 0) {\n result.changePercent = 100;\n } else {\n result.changePercent = 0;\n }\n }\n\n return result;\n }\n\n // ─── Time Series ──────────────────────────────────────\n\n async queryTimeSeries(params: TimeSeriesParams): Promise<TimeSeriesResult> {\n const { dateRange, period } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n\n const granularity = params.granularity ?? autoGranularity(period);\n\n const baseMatch: Record<string, unknown> = {\n site_id: params.siteId,\n timestamp: { $gte: new Date(dateRange.from), $lte: new Date(dateRange.to) },\n };\n\n if (params.metric === 'pageviews') {\n baseMatch.type = 'pageview';\n }\n\n const dateFormat = granularityToDateFormat(granularity);\n\n let pipeline: object[];\n\n if (params.metric === 'visitors' || params.metric === 'sessions') {\n const groupField = params.metric === 'visitors' ? '$visitor_id' : '$session_id';\n pipeline = [\n { $match: baseMatch },\n {\n $group: {\n _id: {\n bucket: { $dateToString: { format: dateFormat, date: '$timestamp' } },\n entity: groupField,\n },\n },\n },\n {\n $group: {\n _id: '$_id.bucket',\n value: { $sum: 1 },\n },\n },\n { $sort: { _id: 1 } },\n ];\n } else {\n pipeline = [\n { $match: baseMatch },\n {\n $group: {\n _id: { $dateToString: { format: dateFormat, date: '$timestamp' } },\n value: { $sum: 1 },\n },\n },\n { $sort: { _id: 1 } },\n ];\n }\n\n const rows = await this.collection.aggregate<{ _id: string; value: number }>(pipeline).toArray();\n\n const data = fillBuckets(\n new Date(dateRange.from),\n new Date(dateRange.to),\n granularity,\n dateFormat,\n rows,\n );\n\n return { metric: params.metric, granularity, data };\n }\n\n // ─── Retention ──────────────────────────────────────\n\n async queryRetention(params: RetentionParams): Promise<RetentionResult> {\n const weeks = params.weeks ?? 8;\n const now = new Date();\n const startDate = new Date(now.getTime() - weeks * 7 * 24 * 60 * 60 * 1000);\n\n const pipeline: object[] = [\n {\n $match: {\n site_id: params.siteId,\n timestamp: { $gte: startDate },\n },\n },\n {\n $group: {\n _id: '$visitor_id',\n firstEvent: { $min: '$timestamp' },\n eventWeeks: {\n $addToSet: {\n $dateToString: { format: '%G-W%V', date: '$timestamp' },\n },\n },\n },\n },\n ];\n\n const visitors = await this.collection.aggregate<{\n _id: string;\n firstEvent: Date;\n eventWeeks: string[];\n }>(pipeline).toArray();\n\n const cohortMap = new Map<string, { visitors: Set<string>; weekSets: Map<string, Set<string>> }>();\n\n for (const v of visitors) {\n const cohortWeek = getISOWeek(v.firstEvent);\n if (!cohortMap.has(cohortWeek)) {\n cohortMap.set(cohortWeek, { visitors: new Set(), weekSets: new Map() });\n }\n const cohort = cohortMap.get(cohortWeek)!;\n cohort.visitors.add(v._id);\n\n for (const w of v.eventWeeks) {\n if (!cohort.weekSets.has(w)) {\n cohort.weekSets.set(w, new Set());\n }\n cohort.weekSets.get(w)!.add(v._id);\n }\n }\n\n const sortedWeeks = Array.from(cohortMap.keys()).sort();\n const cohorts: RetentionCohort[] = sortedWeeks.map((week) => {\n const cohort = cohortMap.get(week)!;\n const size = cohort.visitors.size;\n\n const retention: number[] = [];\n const weekIndex = sortedWeeks.indexOf(week);\n for (let i = 0; i < weeks && (weekIndex + i) < sortedWeeks.length; i++) {\n const targetWeek = sortedWeeks[weekIndex + i];\n const returnedCount = cohort.weekSets.get(targetWeek)?.size ?? 0;\n retention.push(size > 0 ? Math.round((returnedCount / size) * 1000) / 10 : 0);\n }\n\n return { week, size, retention };\n });\n\n return { cohorts };\n }\n\n // ─── Event Listing ──────────────────────────────────────\n\n async listEvents(params: EventListParams): Promise<EventListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const match: Record<string, unknown> = { site_id: params.siteId };\n\n if (params.type) match.type = params.type;\n if (params.eventName) match.event_name = params.eventName;\n if (params.visitorId) match.visitor_id = params.visitorId;\n if (params.userId) match.user_id = params.userId;\n\n if (params.period || params.dateFrom) {\n const { dateRange } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n match.timestamp = { $gte: new Date(dateRange.from), $lte: new Date(dateRange.to) };\n }\n\n const [events, countResult] = await Promise.all([\n this.collection\n .find(match)\n .sort({ timestamp: -1 })\n .skip(offset)\n .limit(limit)\n .toArray(),\n this.collection.countDocuments(match),\n ]);\n\n return {\n events: events.map((e) => this.toEventListItem(e)),\n total: countResult,\n limit,\n offset,\n };\n }\n\n // ─── User Listing ──────────────────────────────────────\n\n async listUsers(params: UserListParams): Promise<UserListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const match: Record<string, unknown> = { site_id: params.siteId };\n\n if (params.search) {\n match.$or = [\n { visitor_id: { $regex: params.search, $options: 'i' } },\n { user_id: { $regex: params.search, $options: 'i' } },\n ];\n }\n\n const pipeline: object[] = [\n { $match: match },\n {\n $group: {\n _id: '$visitor_id',\n userId: { $last: '$user_id' },\n traits: { $last: '$traits' },\n firstSeen: { $min: '$timestamp' },\n lastSeen: { $max: '$timestamp' },\n totalEvents: { $sum: 1 },\n totalPageviews: { $sum: { $cond: [{ $eq: ['$type', 'pageview'] }, 1, 0] } },\n sessions: { $addToSet: '$session_id' },\n lastUrl: { $last: '$url' },\n device_type: { $last: '$device_type' },\n browser: { $last: '$browser' },\n os: { $last: '$os' },\n country: { $last: '$country' },\n city: { $last: '$city' },\n region: { $last: '$region' },\n language: { $last: '$language' },\n },\n },\n { $sort: { lastSeen: -1 } },\n {\n $facet: {\n data: [{ $skip: offset }, { $limit: limit }],\n count: [{ $count: 'total' }],\n },\n },\n ];\n\n const [result] = await this.collection.aggregate<{\n data: Array<{\n _id: string;\n userId: string | null;\n traits: Record<string, unknown> | null;\n firstSeen: Date;\n lastSeen: Date;\n totalEvents: number;\n totalPageviews: number;\n sessions: string[];\n lastUrl: string | null;\n device_type: string | null;\n browser: string | null;\n os: string | null;\n country: string | null;\n city: string | null;\n region: string | null;\n language: string | null;\n }>;\n count: Array<{ total: number }>;\n }>(pipeline).toArray();\n\n const users: UserDetail[] = (result?.data ?? []).map((u) => ({\n visitorId: u._id,\n userId: u.userId ?? undefined,\n traits: u.traits ?? undefined,\n firstSeen: u.firstSeen.toISOString(),\n lastSeen: u.lastSeen.toISOString(),\n totalEvents: u.totalEvents,\n totalPageviews: u.totalPageviews,\n totalSessions: u.sessions.length,\n lastUrl: u.lastUrl ?? undefined,\n device: u.device_type ? { type: u.device_type, browser: u.browser ?? '', os: u.os ?? '' } : undefined,\n geo: u.country ? { country: u.country, city: u.city ?? undefined, region: u.region ?? undefined } : undefined,\n language: u.language ?? undefined,\n }));\n\n return {\n users,\n total: result?.count?.[0]?.total ?? 0,\n limit,\n offset,\n };\n }\n\n async getUserDetail(siteId: string, visitorId: string): Promise<UserDetail | null> {\n const result = await this.listUsers({ siteId, search: visitorId, limit: 1 });\n const user = result.users.find((u) => u.visitorId === visitorId);\n return user ?? null;\n }\n\n async getUserEvents(siteId: string, visitorId: string, params: EventListParams): Promise<EventListResult> {\n return this.listEvents({ ...params, siteId, visitorId });\n }\n\n private toEventListItem(doc: EventDocument): EventListItem {\n return {\n id: (doc as any)._id?.toString?.() ?? '',\n type: doc.type as EventListItem['type'],\n timestamp: doc.timestamp.toISOString(),\n visitorId: doc.visitor_id,\n sessionId: doc.session_id,\n url: doc.url ?? undefined,\n referrer: doc.referrer ?? undefined,\n title: doc.title ?? undefined,\n name: doc.event_name ?? undefined,\n properties: doc.properties ?? undefined,\n userId: doc.user_id ?? undefined,\n traits: doc.traits ?? undefined,\n geo: doc.country ? { country: doc.country, city: doc.city ?? undefined, region: doc.region ?? undefined } : undefined,\n device: doc.device_type ? { type: doc.device_type, browser: doc.browser ?? '', os: doc.os ?? '' } : undefined,\n language: doc.language ?? undefined,\n utm: doc.utm_source ? {\n source: doc.utm_source ?? undefined,\n medium: doc.utm_medium ?? undefined,\n campaign: doc.utm_campaign ?? undefined,\n term: doc.utm_term ?? undefined,\n content: doc.utm_content ?? undefined,\n } : undefined,\n };\n }\n\n // ─── Site Management ──────────────────────────────────────\n\n async createSite(data: CreateSiteRequest): Promise<Site> {\n const now = new Date();\n const doc: SiteDocument = {\n site_id: generateSiteId(),\n secret_key: generateSecretKey(),\n name: data.name,\n domain: data.domain ?? null,\n allowed_origins: data.allowedOrigins ?? null,\n created_at: now,\n updated_at: now,\n };\n await this.sites.insertOne(doc);\n return this.toSite(doc);\n }\n\n async getSite(siteId: string): Promise<Site | null> {\n const doc = await this.sites.findOne({ site_id: siteId });\n return doc ? this.toSite(doc) : null;\n }\n\n async getSiteBySecret(secretKey: string): Promise<Site | null> {\n const doc = await this.sites.findOne({ secret_key: secretKey });\n return doc ? this.toSite(doc) : null;\n }\n\n async listSites(): Promise<Site[]> {\n const docs = await this.sites.find({}).sort({ created_at: -1 }).toArray();\n return docs.map((d) => this.toSite(d));\n }\n\n async updateSite(siteId: string, data: UpdateSiteRequest): Promise<Site | null> {\n const updates: Record<string, unknown> = { updated_at: new Date() };\n if (data.name !== undefined) updates.name = data.name;\n if (data.domain !== undefined) updates.domain = data.domain || null;\n if (data.allowedOrigins !== undefined) updates.allowed_origins = data.allowedOrigins.length > 0 ? data.allowedOrigins : null;\n\n const result = await this.sites.findOneAndUpdate(\n { site_id: siteId },\n { $set: updates },\n { returnDocument: 'after' },\n );\n return result ? this.toSite(result) : null;\n }\n\n async deleteSite(siteId: string): Promise<boolean> {\n const result = await this.sites.deleteOne({ site_id: siteId });\n return result.deletedCount > 0;\n }\n\n async regenerateSecret(siteId: string): Promise<Site | null> {\n const result = await this.sites.findOneAndUpdate(\n { site_id: siteId },\n { $set: { secret_key: generateSecretKey(), updated_at: new Date() } },\n { returnDocument: 'after' },\n );\n return result ? this.toSite(result) : null;\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n\n // ─── Helpers ─────────────────────────────────────────────\n\n private toSite(doc: SiteDocument): Site {\n return {\n siteId: doc.site_id,\n secretKey: doc.secret_key,\n name: doc.name,\n domain: doc.domain ?? undefined,\n allowedOrigins: doc.allowed_origins ?? undefined,\n createdAt: doc.created_at.toISOString(),\n updatedAt: doc.updated_at.toISOString(),\n };\n }\n}\n","import type { GeoInfo } from '@litemetrics/core';\n\nlet reader: any = null;\n\n// Timezone → country code fallback for localhost/private IPs\nconst TZ_COUNTRY: Record<string, string> = {\n // Americas\n 'America/New_York': 'US', 'America/Chicago': 'US', 'America/Denver': 'US',\n 'America/Los_Angeles': 'US', 'America/Anchorage': 'US', 'Pacific/Honolulu': 'US',\n 'America/Phoenix': 'US', 'America/Detroit': 'US', 'America/Indiana/Indianapolis': 'US',\n 'America/Toronto': 'CA', 'America/Vancouver': 'CA', 'America/Edmonton': 'CA',\n 'America/Winnipeg': 'CA', 'America/Halifax': 'CA', 'America/Montreal': 'CA',\n 'America/Mexico_City': 'MX', 'America/Cancun': 'MX', 'America/Tijuana': 'MX',\n 'America/Sao_Paulo': 'BR', 'America/Fortaleza': 'BR', 'America/Manaus': 'BR',\n 'America/Argentina/Buenos_Aires': 'AR', 'America/Bogota': 'CO',\n 'America/Santiago': 'CL', 'America/Lima': 'PE',\n // Europe\n 'Europe/London': 'GB', 'Europe/Dublin': 'IE', 'Europe/Paris': 'FR',\n 'Europe/Berlin': 'DE', 'Europe/Amsterdam': 'NL', 'Europe/Brussels': 'BE',\n 'Europe/Zurich': 'CH', 'Europe/Vienna': 'AT', 'Europe/Rome': 'IT',\n 'Europe/Madrid': 'ES', 'Europe/Lisbon': 'PT', 'Europe/Warsaw': 'PL',\n 'Europe/Prague': 'CZ', 'Europe/Budapest': 'HU', 'Europe/Bucharest': 'RO',\n 'Europe/Sofia': 'BG', 'Europe/Athens': 'GR', 'Europe/Helsinki': 'FI',\n 'Europe/Stockholm': 'SE', 'Europe/Oslo': 'NO', 'Europe/Copenhagen': 'DK',\n 'Europe/Istanbul': 'TR', 'Europe/Moscow': 'RU', 'Europe/Kiev': 'UA',\n 'Europe/Belgrade': 'RS', 'Europe/Zagreb': 'HR',\n // Asia\n 'Asia/Tokyo': 'JP', 'Asia/Seoul': 'KR', 'Asia/Shanghai': 'CN',\n 'Asia/Hong_Kong': 'HK', 'Asia/Taipei': 'TW', 'Asia/Singapore': 'SG',\n 'Asia/Kolkata': 'IN', 'Asia/Mumbai': 'IN', 'Asia/Karachi': 'PK',\n 'Asia/Dubai': 'AE', 'Asia/Riyadh': 'SA', 'Asia/Tehran': 'IR',\n 'Asia/Baghdad': 'IQ', 'Asia/Bangkok': 'TH', 'Asia/Jakarta': 'ID',\n 'Asia/Manila': 'PH', 'Asia/Ho_Chi_Minh': 'VN', 'Asia/Kuala_Lumpur': 'MY',\n 'Asia/Dhaka': 'BD', 'Asia/Colombo': 'LK', 'Asia/Jerusalem': 'IL',\n // Oceania\n 'Australia/Sydney': 'AU', 'Australia/Melbourne': 'AU', 'Australia/Brisbane': 'AU',\n 'Australia/Perth': 'AU', 'Australia/Adelaide': 'AU',\n 'Pacific/Auckland': 'NZ', 'Pacific/Fiji': 'FJ',\n // Africa\n 'Africa/Cairo': 'EG', 'Africa/Lagos': 'NG', 'Africa/Johannesburg': 'ZA',\n 'Africa/Nairobi': 'KE', 'Africa/Casablanca': 'MA', 'Africa/Algiers': 'DZ',\n 'Africa/Accra': 'GH', 'Africa/Tunis': 'TN',\n};\n\nexport async function initGeoIP(dbPath?: string): Promise<void> {\n try {\n const maxmind = await import('maxmind');\n const path = dbPath || await findGeoLiteDB();\n if (path) {\n reader = await maxmind.open(path);\n }\n } catch {\n // GeoIP not available - will return empty geo info\n }\n}\n\nexport function resolveGeo(ip: string, timezone?: string): GeoInfo {\n if (!ip && !timezone) return {};\n\n // Try MaxMind lookup first\n if (reader && ip) {\n try {\n const cleanIp = ip.replace(/^::ffff:/, '');\n const result = reader.get(cleanIp);\n if (result?.country?.iso_code) {\n return {\n country: result.country.iso_code,\n city: result.city?.names?.en || undefined,\n region: result.subdivisions?.[0]?.names?.en || undefined,\n };\n }\n } catch {\n // fall through to timezone fallback\n }\n }\n\n // Timezone fallback for localhost/private IPs or when MaxMind fails\n if (timezone) {\n const country = TZ_COUNTRY[timezone];\n if (country) {\n return { country };\n }\n }\n\n return {};\n}\n\nasync function findGeoLiteDB(): Promise<string | null> {\n const { existsSync } = await import('fs');\n const { join } = await import('path');\n const { homedir } = await import('os');\n\n const candidates = [\n join(process.cwd(), 'GeoLite2-City.mmdb'),\n join(homedir(), '.litemetrics', 'GeoLite2-City.mmdb'),\n '/usr/share/GeoIP/GeoLite2-City.mmdb',\n '/var/lib/GeoIP/GeoLite2-City.mmdb',\n ];\n\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n\n return null;\n}\n","import type { DeviceInfo } from '@litemetrics/core';\nimport { UAParser } from 'ua-parser-js';\n\nconst parser = new UAParser();\n\nexport function parseUserAgent(ua: string): DeviceInfo {\n parser.setUA(ua);\n const result = parser.getResult();\n\n return {\n type: resolveDeviceType(result.device?.type),\n browser: result.browser?.name || 'Unknown',\n os: result.os?.name || 'Unknown',\n };\n}\n\nfunction resolveDeviceType(type?: string): string {\n if (type === 'mobile') return 'mobile';\n if (type === 'tablet') return 'tablet';\n return 'desktop';\n}\n","const BOT_PATTERNS = [\n // Headless browsers\n /HeadlessChrome/i,\n /PhantomJS/i,\n /Selenium/i,\n /Puppeteer/i,\n /Playwright/i,\n\n // Common bots\n /bot\\b/i,\n /spider/i,\n /crawl/i,\n /slurp/i,\n /mediapartners/i,\n /facebookexternalhit/i,\n /Twitterbot/i,\n /LinkedInBot/i,\n /WhatsApp/i,\n /Discordbot/i,\n /TelegramBot/i,\n /Applebot/i,\n /Baiduspider/i,\n /YandexBot/i,\n /DuckDuckBot/i,\n /Sogou/i,\n /Exabot/i,\n /ia_archiver/i,\n\n // HTTP libraries & API tools\n /PostmanRuntime/i,\n /axios/i,\n /node-fetch/i,\n /python-requests/i,\n /Go-http-client/i,\n /Java\\//i,\n /libwww-perl/i,\n /wget/i,\n /curl/i,\n /httpie/i,\n\n // Monitoring / uptime\n /UptimeRobot/i,\n /Pingdom/i,\n /StatusCake/i,\n /Site24x7/i,\n /NewRelic/i,\n /Datadog/i,\n\n // Preview/embed\n /Slackbot/i,\n /Embedly/i,\n /Quora Link Preview/i,\n /redditbot/i,\n /Pinterestbot/i,\n];\n\nexport function isBot(ua: string): boolean {\n if (!ua || ua.length === 0) return true;\n return BOT_PATTERNS.some((re) => re.test(ua));\n}\n","import type {\n CollectorConfig,\n DBAdapter,\n EnrichedEvent,\n ClientEvent,\n CollectPayload,\n QueryParams,\n QueryResult,\n TimeSeriesParams,\n RetentionParams,\n EventListParams,\n EventListResult,\n UserListParams,\n UserListResult,\n UserDetail,\n Site,\n CreateSiteRequest,\n UpdateSiteRequest,\n} from '@litemetrics/core';\nimport { ClickHouseAdapter } from './adapters/clickhouse';\nimport { MongoDBAdapter } from './adapters/mongodb';\nimport { initGeoIP, resolveGeo } from './geoip';\nimport { parseUserAgent } from './useragent';\nimport { isBot } from './botfilter';\n\nexport interface Collector {\n handler(): (req: any, res: any) => void | Promise<void>;\n queryHandler(): (req: any, res: any) => void | Promise<void>;\n eventsHandler(): (req: any, res: any) => void | Promise<void>;\n usersHandler(): (req: any, res: any) => void | Promise<void>;\n sitesHandler(): (req: any, res: any) => void | Promise<void>;\n query(params: QueryParams): Promise<QueryResult>;\n listEvents(params: EventListParams): Promise<EventListResult>;\n listUsers(params: UserListParams): Promise<UserListResult>;\n getUserDetail(siteId: string, visitorId: string): Promise<UserDetail | null>;\n getUserEvents(siteId: string, visitorId: string, params: EventListParams): Promise<EventListResult>;\n track(siteId: string, name: string, properties?: Record<string, unknown>, options?: { userId?: string; ip?: string }): Promise<void>;\n identify(siteId: string, userId: string, traits?: Record<string, unknown>, options?: { ip?: string }): Promise<void>;\n createSite(data: CreateSiteRequest): Promise<Site>;\n listSites(): Promise<Site[]>;\n getSite(siteId: string): Promise<Site | null>;\n updateSite(siteId: string, data: UpdateSiteRequest): Promise<Site | null>;\n deleteSite(siteId: string): Promise<boolean>;\n regenerateSecret(siteId: string): Promise<Site | null>;\n close(): Promise<void>;\n}\n\nexport async function createCollector(config: CollectorConfig): Promise<Collector> {\n const db = createAdapter(config.db);\n await db.init();\n\n if (config.geoip) {\n const geoipConfig = typeof config.geoip === 'object' ? config.geoip : {};\n await initGeoIP(geoipConfig.dbPath);\n }\n\n // ─── Auth helpers ──────────────────────────────────────\n\n function isAdmin(req: any): boolean {\n if (!config.adminSecret) return false;\n return req.headers?.['x-litemetrics-admin-secret'] === config.adminSecret;\n }\n\n async function isAuthorizedForSite(req: any, siteId: string): Promise<boolean> {\n // Admin can access everything\n if (isAdmin(req)) return true;\n // Check site secret\n const secret = req.headers?.['x-litemetrics-secret'];\n if (!secret) return false;\n const site = await db.getSiteBySecret(secret);\n return site !== null && site.siteId === siteId;\n }\n\n // ─── CORS helper ──────────────────────────────────────\n\n function setCors(req: any, res: any, methods: string, extraHeaders?: string): boolean {\n if (!config.cors) return false;\n const origin = req.headers?.origin;\n const allowed = !config.cors.origins || config.cors.origins.length === 0 || config.cors.origins.includes(origin);\n if (allowed) {\n res.setHeader?.('Access-Control-Allow-Origin', origin || '*');\n res.setHeader?.('Access-Control-Allow-Methods', methods);\n const headers = ['Content-Type', extraHeaders].filter(Boolean).join(', ');\n res.setHeader?.('Access-Control-Allow-Headers', headers);\n }\n if (req.method === 'OPTIONS') {\n res.writeHead?.(204);\n res.end?.();\n return true;\n }\n return false;\n }\n\n // ─── Event helpers ────────────────────────────────────\n\n function enrichEvents(events: ClientEvent[], ip: string, userAgent: string): EnrichedEvent[] {\n const device = parseUserAgent(userAgent);\n return events.map((event) => {\n const geo = resolveGeo(ip, event.timezone);\n return { ...event, ip, geo, device };\n });\n }\n\n function extractIp(req: any): string {\n if (config.trustProxy ?? true) {\n const forwarded = req.headers?.['x-forwarded-for'];\n if (forwarded) {\n const first = typeof forwarded === 'string' ? forwarded.split(',')[0] : forwarded[0];\n return first.trim();\n }\n if (req.headers?.['x-real-ip']) return req.headers['x-real-ip'];\n }\n return req.ip || req.socket?.remoteAddress || req.connection?.remoteAddress || '';\n }\n\n // ─── Handlers ─────────────────────────────────────────\n\n function handler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n if (setCors(req, res, 'POST, OPTIONS')) return;\n\n if (req.method !== 'POST') {\n sendJson(res, 405, { ok: false, error: 'Method not allowed' });\n return;\n }\n\n try {\n const body = await parseBody(req);\n const payload = body as CollectPayload;\n\n if (!payload?.events || !Array.isArray(payload.events) || payload.events.length === 0) {\n sendJson(res, 400, { ok: false, error: 'No events provided' });\n return;\n }\n if (payload.events.length > 100) {\n sendJson(res, 400, { ok: false, error: 'Too many events (max 100)' });\n return;\n }\n\n const userAgent = req.headers?.['user-agent'] || '';\n\n // Bot check - silent drop\n if (isBot(userAgent)) {\n sendJson(res, 200, { ok: true });\n return;\n }\n\n const ip = extractIp(req);\n const enriched = enrichEvents(payload.events, ip, userAgent);\n\n // Hostname filtering: check site's allowedOrigins\n const siteId = enriched[0]?.siteId;\n if (siteId) {\n const site = await db.getSite(siteId);\n if (site?.allowedOrigins && site.allowedOrigins.length > 0) {\n const allowed = new Set(site.allowedOrigins.map((h) => h.toLowerCase()));\n const filtered = enriched.filter((event) => {\n if (!event.url) return true; // non-pageview events pass through\n try {\n const hostname = new URL(event.url).hostname.toLowerCase();\n return allowed.has(hostname);\n } catch {\n return true; // malformed URLs pass through\n }\n });\n if (filtered.length === 0) {\n sendJson(res, 200, { ok: true });\n return;\n }\n await db.insertEvents(filtered);\n sendJson(res, 200, { ok: true });\n return;\n }\n }\n\n await db.insertEvents(enriched);\n sendJson(res, 200, { ok: true });\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n function queryHandler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n if (setCors(req, res, 'GET, OPTIONS', 'X-Litemetrics-Secret, X-Litemetrics-Admin-Secret')) return;\n\n try {\n const params = extractQueryParams(req);\n\n if (!params.siteId) {\n sendJson(res, 400, { ok: false, error: 'siteId is required' });\n return;\n }\n if (!params.metric) {\n sendJson(res, 400, { ok: false, error: 'metric is required' });\n return;\n }\n\n // Auth check\n const authorized = await isAuthorizedForSite(req, params.siteId);\n if (!authorized) {\n sendJson(res, 401, { ok: false, error: 'Invalid or missing secret key' });\n return;\n }\n\n // Time series query\n if (params.metric === 'timeseries' as any) {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n const tsParams: TimeSeriesParams = {\n siteId: params.siteId,\n metric: (q.tsMetric as TimeSeriesParams['metric']) || 'pageviews',\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n granularity: q.granularity as TimeSeriesParams['granularity'],\n };\n const result = await db.queryTimeSeries(tsParams);\n sendJson(res, 200, result);\n return;\n }\n\n // Retention query\n if (params.metric === 'retention' as any) {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n const retentionParams: RetentionParams = {\n siteId: params.siteId,\n period: params.period,\n weeks: q.weeks ? parseInt(q.weeks as string, 10) : undefined,\n };\n const result = await db.queryRetention(retentionParams);\n sendJson(res, 200, result);\n return;\n }\n\n const result = await db.query(params);\n sendJson(res, 200, result);\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n function sitesHandler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n if (setCors(req, res, 'GET, POST, PUT, DELETE, OPTIONS', 'X-Litemetrics-Admin-Secret')) return;\n\n // Admin auth required for all site management\n if (!isAdmin(req)) {\n sendJson(res, 401, { ok: false, error: 'Unauthorized - invalid or missing admin secret' });\n return;\n }\n\n try {\n const method = req.method;\n // Extract path - works with Express params or raw URL\n const url = new URL(req.url || '/', 'http://localhost');\n const pathSegments = url.pathname.split('/').filter(Boolean);\n // Find the segment after 'sites' in the path\n const sitesIdx = pathSegments.indexOf('sites');\n const siteId = sitesIdx >= 0 ? pathSegments[sitesIdx + 1] : undefined;\n const action = sitesIdx >= 0 ? pathSegments[sitesIdx + 2] : undefined;\n\n // POST /api/sites/:siteId/regenerate\n if (method === 'POST' && siteId && action === 'regenerate') {\n const site = await db.regenerateSecret(siteId);\n if (!site) { sendJson(res, 404, { ok: false, error: 'Site not found' }); return; }\n sendJson(res, 200, { site });\n return;\n }\n\n // GET /api/sites - list\n if (method === 'GET' && !siteId) {\n const sites = await db.listSites();\n sendJson(res, 200, { sites, total: sites.length });\n return;\n }\n\n // GET /api/sites/:siteId - get one\n if (method === 'GET' && siteId) {\n const site = await db.getSite(siteId);\n if (!site) { sendJson(res, 404, { ok: false, error: 'Site not found' }); return; }\n sendJson(res, 200, { site });\n return;\n }\n\n // POST /api/sites - create\n if (method === 'POST' && !siteId) {\n const body = await parseBody(req) as CreateSiteRequest;\n if (!body.name || typeof body.name !== 'string' || !body.name.trim()) {\n sendJson(res, 400, { ok: false, error: 'Site name is required' });\n return;\n }\n const site = await db.createSite(body);\n sendJson(res, 201, { site });\n return;\n }\n\n // PUT /api/sites/:siteId - update\n if (method === 'PUT' && siteId) {\n const body = await parseBody(req) as UpdateSiteRequest;\n const site = await db.updateSite(siteId, body);\n if (!site) { sendJson(res, 404, { ok: false, error: 'Site not found' }); return; }\n sendJson(res, 200, { site });\n return;\n }\n\n // DELETE /api/sites/:siteId - delete\n if (method === 'DELETE' && siteId) {\n const deleted = await db.deleteSite(siteId);\n if (!deleted) { sendJson(res, 404, { ok: false, error: 'Site not found' }); return; }\n sendJson(res, 200, { ok: true });\n return;\n }\n\n sendJson(res, 404, { ok: false, error: 'Not found' });\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n // ─── Events handler ────────────────────────────────────\n\n function eventsHandler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n if (setCors(req, res, 'GET, OPTIONS', 'X-Litemetrics-Secret, X-Litemetrics-Admin-Secret')) return;\n\n if (req.method !== 'GET') {\n sendJson(res, 405, { ok: false, error: 'Method not allowed' });\n return;\n }\n\n try {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n\n if (!q.siteId) {\n sendJson(res, 400, { ok: false, error: 'siteId is required' });\n return;\n }\n\n const authorized = await isAuthorizedForSite(req, q.siteId as string);\n if (!authorized) {\n sendJson(res, 401, { ok: false, error: 'Invalid or missing secret key' });\n return;\n }\n\n const params: EventListParams = {\n siteId: q.siteId as string,\n type: q.type as EventListParams['type'],\n eventName: q.eventName as string | undefined,\n visitorId: q.visitorId as string | undefined,\n userId: q.userId as string | undefined,\n period: q.period as EventListParams['period'],\n dateFrom: q.dateFrom as string | undefined,\n dateTo: q.dateTo as string | undefined,\n limit: q.limit ? parseInt(q.limit as string, 10) : undefined,\n offset: q.offset ? parseInt(q.offset as string, 10) : undefined,\n };\n\n const result = await db.listEvents(params);\n sendJson(res, 200, result);\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n // ─── Users handler ────────────────────────────────────\n\n function usersHandler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n if (setCors(req, res, 'GET, OPTIONS', 'X-Litemetrics-Secret, X-Litemetrics-Admin-Secret')) return;\n\n if (req.method !== 'GET') {\n sendJson(res, 405, { ok: false, error: 'Method not allowed' });\n return;\n }\n\n try {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n\n if (!q.siteId) {\n sendJson(res, 400, { ok: false, error: 'siteId is required' });\n return;\n }\n\n const authorized = await isAuthorizedForSite(req, q.siteId as string);\n if (!authorized) {\n sendJson(res, 401, { ok: false, error: 'Invalid or missing secret key' });\n return;\n }\n\n // Extract path to check for /api/users/:visitorId or /api/users/:visitorId/events\n const url = new URL(req.url || '/', 'http://localhost');\n const pathSegments = url.pathname.split('/').filter(Boolean);\n const usersIdx = pathSegments.indexOf('users');\n const visitorId = usersIdx >= 0 ? pathSegments[usersIdx + 1] : undefined;\n const action = usersIdx >= 0 ? pathSegments[usersIdx + 2] : undefined;\n\n // GET /api/users/:visitorId/events\n if (visitorId && action === 'events') {\n const params: EventListParams = {\n siteId: q.siteId as string,\n type: q.type as EventListParams['type'],\n period: q.period as EventListParams['period'],\n dateFrom: q.dateFrom as string | undefined,\n dateTo: q.dateTo as string | undefined,\n limit: q.limit ? parseInt(q.limit as string, 10) : undefined,\n offset: q.offset ? parseInt(q.offset as string, 10) : undefined,\n };\n const result = await db.getUserEvents(q.siteId as string, decodeURIComponent(visitorId), params);\n sendJson(res, 200, result);\n return;\n }\n\n // GET /api/users/:visitorId\n if (visitorId) {\n const user = await db.getUserDetail(q.siteId as string, decodeURIComponent(visitorId));\n if (!user) {\n sendJson(res, 404, { ok: false, error: 'User not found' });\n return;\n }\n sendJson(res, 200, { user });\n return;\n }\n\n // GET /api/users - list\n const params: UserListParams = {\n siteId: q.siteId as string,\n search: q.search as string | undefined,\n limit: q.limit ? parseInt(q.limit as string, 10) : undefined,\n offset: q.offset ? parseInt(q.offset as string, 10) : undefined,\n };\n const result = await db.listUsers(params);\n sendJson(res, 200, result);\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n // ─── Return collector ─────────────────────────────────\n\n return {\n handler,\n queryHandler,\n eventsHandler,\n usersHandler,\n sitesHandler,\n\n async query(params: QueryParams): Promise<QueryResult> {\n return db.query(params);\n },\n\n async listEvents(params: EventListParams): Promise<EventListResult> {\n return db.listEvents(params);\n },\n\n async listUsers(params: UserListParams): Promise<UserListResult> {\n return db.listUsers(params);\n },\n\n async getUserDetail(siteId: string, visitorId: string): Promise<UserDetail | null> {\n return db.getUserDetail(siteId, visitorId);\n },\n\n async getUserEvents(siteId: string, visitorId: string, params: EventListParams): Promise<EventListResult> {\n return db.getUserEvents(siteId, visitorId, params);\n },\n\n async track(siteId, name, properties, options) {\n const event: EnrichedEvent = {\n type: 'event', siteId, timestamp: Date.now(), sessionId: 'server', visitorId: 'server',\n name, properties, userId: options?.userId, ip: options?.ip,\n geo: options?.ip ? resolveGeo(options.ip) : undefined,\n };\n await db.insertEvents([event]);\n },\n\n async identify(siteId, userId, traits, options) {\n const event: EnrichedEvent = {\n type: 'identify', siteId, timestamp: Date.now(), sessionId: 'server', visitorId: 'server',\n userId, traits, ip: options?.ip,\n geo: options?.ip ? resolveGeo(options.ip) : undefined,\n };\n await db.insertEvents([event]);\n },\n\n // Programmatic site management\n createSite: (data) => db.createSite(data),\n listSites: () => db.listSites(),\n getSite: (siteId) => db.getSite(siteId),\n updateSite: (siteId, data) => db.updateSite(siteId, data),\n deleteSite: (siteId) => db.deleteSite(siteId),\n regenerateSecret: (siteId) => db.regenerateSecret(siteId),\n\n async close() {\n await db.close();\n },\n };\n}\n\nfunction createAdapter(config: CollectorConfig['db']): DBAdapter {\n const adapter = config.adapter ?? 'clickhouse';\n switch (adapter) {\n case 'clickhouse':\n return new ClickHouseAdapter(config.url);\n case 'mongodb':\n return new MongoDBAdapter(config.url);\n default:\n throw new Error(`Unknown DB adapter: ${adapter}. Supported: clickhouse, mongodb`);\n }\n}\n\nasync function parseBody(req: any): Promise<unknown> {\n if (req.body) return req.body;\n return new Promise((resolve, reject) => {\n let data = '';\n req.on('data', (chunk: Buffer) => { data += chunk.toString(); });\n req.on('end', () => {\n try { resolve(JSON.parse(data)); } catch { reject(new Error('Invalid JSON')); }\n });\n req.on('error', reject);\n });\n}\n\nfunction extractQueryParams(req: any): QueryParams {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n return {\n siteId: q.siteId as string,\n metric: q.metric as QueryParams['metric'],\n period: q.period as QueryParams['period'],\n dateFrom: q.dateFrom as string | undefined,\n dateTo: q.dateTo as string | undefined,\n limit: q.limit ? parseInt(q.limit as string, 10) : undefined,\n filters: q.filters ? JSON.parse(q.filters as string) : undefined,\n compare: q.compare === 'true' || q.compare === '1',\n };\n}\n\nfunction sendJson(res: any, status: number, body: unknown): void {\n if (typeof res.status === 'function' && typeof res.json === 'function') {\n res.status(status).json(body);\n return;\n }\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(body));\n}\n"],"mappings":"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","names":["result","result","params"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/adapters/clickhouse.ts","../src/adapters/utils.ts","../src/adapters/mongodb.ts","../src/geoip.ts","../src/useragent.ts","../src/botfilter.ts","../src/collector.ts"],"sourcesContent":["export { createCollector } from './collector';\nexport type { Collector } from './collector';\nexport { ClickHouseAdapter } from './adapters/clickhouse';\nexport { MongoDBAdapter } from './adapters/mongodb';\nexport { isBot } from './botfilter';\n\n// Re-export types from core\nexport type {\n CollectorConfig,\n DBConfig,\n DBAdapter,\n QueryParams,\n QueryResult,\n EnrichedEvent,\n Metric,\n Period,\n Site,\n CreateSiteRequest,\n UpdateSiteRequest,\n EventListParams,\n EventListResult,\n EventListItem,\n UserListParams,\n UserListResult,\n UserDetail,\n} from '@litemetrics/core';\n","import type { DBAdapter, EnrichedEvent, QueryParams, QueryResult, QueryDataPoint, Granularity, TimeSeriesParams, TimeSeriesResult, RetentionParams, RetentionResult, RetentionCohort, Site, CreateSiteRequest, UpdateSiteRequest, EventListParams, EventListResult, EventListItem, UserListParams, UserListResult, UserDetail } from '@litemetrics/core';\nimport { createClient, type ClickHouseClient } from '@clickhouse/client';\nimport { resolvePeriod, previousPeriodRange, autoGranularity, fillBuckets, granularityToDateFormat, getISOWeek, generateSiteId, generateSecretKey } from './utils';\n\nconst EVENTS_TABLE = 'litemetrics_events';\nconst SITES_TABLE = 'litemetrics_sites';\nconst IDENTITY_MAP_TABLE = 'litemetrics_identity_map';\n\nconst CREATE_IDENTITY_MAP_TABLE = `\nCREATE TABLE IF NOT EXISTS ${IDENTITY_MAP_TABLE} (\n site_id LowCardinality(String),\n visitor_id String,\n user_id String,\n identified_at DateTime64(3),\n created_at DateTime64(3) DEFAULT now64(3)\n) ENGINE = ReplacingMergeTree(created_at)\n ORDER BY (site_id, visitor_id)\n SETTINGS index_granularity = 8192\n`;\n\nconst CREATE_EVENTS_TABLE = `\nCREATE TABLE IF NOT EXISTS ${EVENTS_TABLE} (\n event_id UUID DEFAULT generateUUIDv4(),\n site_id LowCardinality(String),\n type LowCardinality(String),\n timestamp DateTime64(3),\n session_id String,\n visitor_id String,\n url Nullable(String),\n referrer Nullable(String),\n title Nullable(String),\n event_name Nullable(String),\n properties Nullable(String),\n event_source LowCardinality(Nullable(String)),\n event_subtype LowCardinality(Nullable(String)),\n page_path Nullable(String),\n target_url_path Nullable(String),\n element_selector Nullable(String),\n element_text Nullable(String),\n scroll_depth_pct Nullable(UInt8),\n user_id Nullable(String),\n traits Nullable(String),\n country LowCardinality(Nullable(String)),\n city Nullable(String),\n region Nullable(String),\n device_type LowCardinality(Nullable(String)),\n browser LowCardinality(Nullable(String)),\n os LowCardinality(Nullable(String)),\n language LowCardinality(Nullable(String)),\n timezone Nullable(String),\n screen_width Nullable(UInt16),\n screen_height Nullable(UInt16),\n utm_source Nullable(String),\n utm_medium Nullable(String),\n utm_campaign Nullable(String),\n utm_term Nullable(String),\n utm_content Nullable(String),\n ip Nullable(String),\n created_at DateTime64(3) DEFAULT now64(3)\n) ENGINE = MergeTree()\n PARTITION BY toYYYYMM(timestamp)\n ORDER BY (site_id, timestamp, visitor_id)\n SETTINGS index_granularity = 8192\n`;\n\nconst CREATE_SITES_TABLE = `\nCREATE TABLE IF NOT EXISTS ${SITES_TABLE} (\n site_id String,\n secret_key String,\n name String,\n domain Nullable(String),\n allowed_origins Nullable(String),\n conversion_events Nullable(String),\n created_at DateTime64(3),\n updated_at DateTime64(3),\n version UInt64,\n is_deleted UInt8 DEFAULT 0\n) ENGINE = ReplacingMergeTree(version)\n ORDER BY (site_id)\n SETTINGS index_granularity = 8192\n`;\n\n/** Convert JS date/ISO string to ClickHouse DateTime64 format: '2026-02-07 14:22:08.339' */\nfunction toCHDateTime(d: string | Date): string {\n const iso = typeof d === 'string' ? d : d.toISOString();\n return iso.replace('T', ' ').replace('Z', '');\n}\n\nfunction buildFilterConditions(filters?: Record<string, string>): { conditions: string[]; params: Record<string, unknown> } {\n if (!filters) return { conditions: [], params: {} };\n const map: Record<string, string> = {\n 'geo.country': 'country',\n 'geo.city': 'city',\n 'geo.region': 'region',\n 'language': 'language',\n 'device.type': 'device_type',\n 'device.browser': 'browser',\n 'device.os': 'os',\n 'utm.source': 'utm_source',\n 'utm.medium': 'utm_medium',\n 'utm.campaign': 'utm_campaign',\n 'utm.term': 'utm_term',\n 'utm.content': 'utm_content',\n 'referrer': 'referrer',\n 'event_source': 'event_source',\n 'event_subtype': 'event_subtype',\n 'page_path': 'page_path',\n 'target_url_path': 'target_url_path',\n 'event_name': 'event_name',\n 'type': 'type',\n };\n const conditions: string[] = [];\n const params: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(filters)) {\n if (!value || !map[key]) continue;\n const paramKey = `f_${key.replace(/[^a-zA-Z0-9]/g, '_')}`;\n conditions.push(`${map[key]} = {${paramKey}:String}`);\n params[paramKey] = value;\n }\n return { conditions, params };\n}\n\nexport class ClickHouseAdapter implements DBAdapter {\n private client: ClickHouseClient;\n\n constructor(url: string) {\n this.client = createClient({\n url,\n clickhouse_settings: {\n wait_end_of_query: 1,\n },\n });\n }\n\n async init(): Promise<void> {\n await this.client.command({ query: CREATE_EVENTS_TABLE });\n await this.client.command({ query: CREATE_SITES_TABLE });\n await this.client.command({ query: CREATE_IDENTITY_MAP_TABLE });\n await this.client.command({ query: `ALTER TABLE ${EVENTS_TABLE} ADD COLUMN IF NOT EXISTS event_source LowCardinality(Nullable(String))` });\n await this.client.command({ query: `ALTER TABLE ${EVENTS_TABLE} ADD COLUMN IF NOT EXISTS event_subtype LowCardinality(Nullable(String))` });\n await this.client.command({ query: `ALTER TABLE ${EVENTS_TABLE} ADD COLUMN IF NOT EXISTS page_path Nullable(String)` });\n await this.client.command({ query: `ALTER TABLE ${EVENTS_TABLE} ADD COLUMN IF NOT EXISTS target_url_path Nullable(String)` });\n await this.client.command({ query: `ALTER TABLE ${EVENTS_TABLE} ADD COLUMN IF NOT EXISTS element_selector Nullable(String)` });\n await this.client.command({ query: `ALTER TABLE ${EVENTS_TABLE} ADD COLUMN IF NOT EXISTS element_text Nullable(String)` });\n await this.client.command({ query: `ALTER TABLE ${EVENTS_TABLE} ADD COLUMN IF NOT EXISTS scroll_depth_pct Nullable(UInt8)` });\n await this.client.command({\n query: `ALTER TABLE ${SITES_TABLE} ADD COLUMN IF NOT EXISTS conversion_events Nullable(String)`,\n });\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n\n // ─── Event Insertion ──────────────────────────────────────\n\n async insertEvents(events: EnrichedEvent[]): Promise<void> {\n if (events.length === 0) return;\n\n const rows = events.map((e) => ({\n site_id: e.siteId,\n type: e.type,\n timestamp: toCHDateTime(new Date(e.timestamp)),\n session_id: e.sessionId,\n visitor_id: e.visitorId,\n url: e.url ?? null,\n referrer: e.referrer ?? null,\n title: e.title ?? null,\n event_name: e.name ?? null,\n properties: e.properties ? JSON.stringify(e.properties) : null,\n event_source: e.eventSource ?? null,\n event_subtype: e.eventSubtype ?? null,\n page_path: e.pagePath ?? null,\n target_url_path: e.targetUrlPath ?? null,\n element_selector: e.elementSelector ?? null,\n element_text: e.elementText ?? null,\n scroll_depth_pct: e.scrollDepthPct ?? null,\n user_id: e.userId ?? null,\n traits: e.traits ? JSON.stringify(e.traits) : null,\n country: e.geo?.country ?? null,\n city: e.geo?.city ?? null,\n region: e.geo?.region ?? null,\n device_type: e.device?.type ?? null,\n browser: e.device?.browser ?? null,\n os: e.device?.os ?? null,\n language: e.language ?? null,\n timezone: e.timezone ?? null,\n screen_width: e.screen?.width ?? null,\n screen_height: e.screen?.height ?? null,\n utm_source: e.utm?.source ?? null,\n utm_medium: e.utm?.medium ?? null,\n utm_campaign: e.utm?.campaign ?? null,\n utm_term: e.utm?.term ?? null,\n utm_content: e.utm?.content ?? null,\n ip: e.ip ?? null,\n }));\n\n await this.client.insert({\n table: EVENTS_TABLE,\n values: rows,\n format: 'JSONEachRow',\n });\n }\n\n // ─── Analytics Queries ──────────────────────────────────────\n\n async query(q: QueryParams): Promise<QueryResult> {\n const { dateRange, period } = resolvePeriod(q);\n const siteId = q.siteId;\n const limit = q.limit ?? 10;\n\n const params = {\n siteId,\n from: toCHDateTime(dateRange.from),\n to: toCHDateTime(dateRange.to),\n limit,\n };\n const filter = buildFilterConditions(q.filters);\n const filterSql = filter.conditions.length > 0 ? ` AND ${filter.conditions.join(' AND ')}` : '';\n\n let data: QueryDataPoint[] = [];\n let total = 0;\n\n switch (q.metric) {\n case 'pageviews': {\n const rows = await this.queryRows<{ value: string }>(\n `SELECT count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'pageview'${filterSql}`,\n { ...params, ...filter.params },\n );\n total = Number(rows[0]?.value ?? 0);\n data = [{ key: 'pageviews', value: total }];\n break;\n }\n\n case 'visitors': {\n const rows = await this.queryRows<{ value: string }>(\n `SELECT uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}${filterSql}`,\n { ...params, ...filter.params },\n );\n total = Number(rows[0]?.value ?? 0);\n data = [{ key: 'visitors', value: total }];\n break;\n }\n\n case 'sessions': {\n const rows = await this.queryRows<{ value: string }>(\n `SELECT uniq(session_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}${filterSql}`,\n { ...params, ...filter.params },\n );\n total = Number(rows[0]?.value ?? 0);\n data = [{ key: 'sessions', value: total }];\n break;\n }\n\n case 'events': {\n const rows = await this.queryRows<{ value: string }>(\n `SELECT count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'event'${filterSql}`,\n { ...params, ...filter.params },\n );\n total = Number(rows[0]?.value ?? 0);\n data = [{ key: 'events', value: total }];\n break;\n }\n case 'conversions': {\n const conversionEvents = q.conversionEvents ?? [];\n if (conversionEvents.length === 0) {\n total = 0;\n data = [{ key: 'conversions', value: 0 }];\n break;\n }\n const rows = await this.queryRows<{ value: string }>(\n `SELECT count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'event'\n AND event_name IN {eventNames:Array(String)}${filterSql}`,\n { ...params, eventNames: conversionEvents, ...filter.params },\n );\n total = Number(rows[0]?.value ?? 0);\n data = [{ key: 'conversions', value: total }];\n break;\n }\n\n case 'top_pages': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT url AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'pageview'\n AND url IS NOT NULL${filterSql}\n GROUP BY url\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_referrers': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT referrer AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'pageview'\n AND referrer IS NOT NULL\n AND referrer != ''${filterSql}\n GROUP BY referrer\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_countries': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT country AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND country IS NOT NULL${filterSql}\n GROUP BY country\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_cities': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT city AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND city IS NOT NULL${filterSql}\n GROUP BY city\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_events': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT event_name AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'event'\n AND event_name IS NOT NULL\n ${filterSql}\n GROUP BY event_name\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_conversions': {\n const conversionEvents = q.conversionEvents ?? [];\n if (conversionEvents.length === 0) {\n total = 0;\n data = [];\n break;\n }\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT event_name AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'event'\n AND event_name IN {eventNames:Array(String)}\n ${filterSql}\n GROUP BY event_name\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, eventNames: conversionEvents, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_exit_pages': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT exit_url AS key, count() AS value FROM (\n SELECT session_id, argMax(url, timestamp) AS exit_url\n FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'pageview'\n AND url IS NOT NULL${filterSql}\n GROUP BY session_id\n )\n GROUP BY exit_url\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_transitions': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT concat(prev_url, ' → ', curr_url) AS key, count() AS value FROM (\n SELECT session_id, url AS curr_url,\n lagInFrame(url, 1) OVER (PARTITION BY session_id ORDER BY timestamp ASC) AS prev_url\n FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'pageview'\n AND url IS NOT NULL${filterSql}\n )\n WHERE prev_url IS NOT NULL AND prev_url != ''\n GROUP BY key\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_scroll_pages': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT page_path AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'event'\n AND event_subtype = 'scroll_depth'\n AND page_path IS NOT NULL${filterSql}\n GROUP BY page_path\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_button_clicks': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT ifNull(element_text, element_selector) AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'event'\n AND event_subtype = 'button_click'\n AND (element_text IS NOT NULL OR element_selector IS NOT NULL)${filterSql}\n GROUP BY key\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_link_targets': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT target_url_path AS key, count() AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND type = 'event'\n AND event_subtype IN ('link_click','outbound_click')\n AND target_url_path IS NOT NULL${filterSql}\n GROUP BY target_url_path\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_devices': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT device_type AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND device_type IS NOT NULL\n ${filterSql}\n GROUP BY device_type\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_browsers': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT browser AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND browser IS NOT NULL\n ${filterSql}\n GROUP BY browser\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_os': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT os AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND os IS NOT NULL\n ${filterSql}\n GROUP BY os\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_utm_sources': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT utm_source AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND utm_source IS NOT NULL AND utm_source != ''\n ${filterSql}\n GROUP BY utm_source\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_utm_mediums': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT utm_medium AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND utm_medium IS NOT NULL AND utm_medium != ''\n ${filterSql}\n GROUP BY utm_medium\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_utm_campaigns': {\n const rows = await this.queryRows<{ key: string; value: string }>(\n `SELECT utm_campaign AS key, uniq(visitor_id) AS value FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n AND utm_campaign IS NOT NULL AND utm_campaign != ''\n ${filterSql}\n GROUP BY utm_campaign\n ORDER BY value DESC\n LIMIT {limit:UInt32}`,\n { ...params, ...filter.params },\n );\n data = rows.map((r) => ({ key: r.key, value: Number(r.value) }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n }\n\n const result: QueryResult = { metric: q.metric, period, data, total };\n\n // Comparison with previous period\n if (q.compare && ['pageviews', 'visitors', 'sessions', 'events', 'conversions'].includes(q.metric)) {\n const prevRange = previousPeriodRange(dateRange);\n const prevResult = await this.query({\n ...q,\n compare: false,\n period: 'custom',\n dateFrom: prevRange.from,\n dateTo: prevRange.to,\n });\n result.previousTotal = prevResult.total;\n if (prevResult.total > 0) {\n result.changePercent = Math.round(((total - prevResult.total) / prevResult.total) * 1000) / 10;\n } else if (total > 0) {\n result.changePercent = 100;\n } else {\n result.changePercent = 0;\n }\n }\n\n return result;\n }\n\n // ─── Time Series ──────────────────────────────────────\n\n async queryTimeSeries(params: TimeSeriesParams): Promise<TimeSeriesResult> {\n const { dateRange, period } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n\n const granularity = params.granularity ?? autoGranularity(period);\n const bucketFn = this.granularityToClickHouseFunc(granularity);\n const dateFormat = granularityToDateFormat(granularity);\n\n const filter = buildFilterConditions(params.filters);\n const filterSql = filter.conditions.length > 0 ? ` AND ${filter.conditions.join(' AND ')}` : '';\n const typeFilter = params.metric === 'pageviews' ? `AND type = 'pageview'` : '';\n const eventsFilter = params.metric === 'events' ? `AND type = 'event'` : '';\n const conversionsFilter = params.metric === 'conversions'\n ? `AND type = 'event' AND event_name IN {eventNames:Array(String)}`\n : '';\n const extraFilters = [typeFilter, eventsFilter, conversionsFilter, filterSql].filter(Boolean).join(' ');\n\n let sql: string;\n if (params.metric === 'visitors' || params.metric === 'sessions') {\n const field = params.metric === 'visitors' ? 'visitor_id' : 'session_id';\n sql = `\n SELECT ${bucketFn} AS bucket, uniq(${field}) AS value\n FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n ${extraFilters}\n GROUP BY bucket\n ORDER BY bucket ASC\n `;\n } else {\n sql = `\n SELECT ${bucketFn} AS bucket, count() AS value\n FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {from:String}\n AND timestamp <= {to:String}\n ${extraFilters}\n GROUP BY bucket\n ORDER BY bucket ASC\n `;\n }\n\n const rows = await this.queryRows<{ bucket: string; value: string }>(sql, {\n siteId: params.siteId,\n from: toCHDateTime(dateRange.from),\n to: toCHDateTime(dateRange.to),\n eventNames: params.conversionEvents ?? [],\n ...filter.params,\n });\n\n // Convert ClickHouse bucket format to match the dateFormat used by fillBuckets\n const mappedRows = rows.map((r) => ({\n _id: this.convertClickHouseBucket(r.bucket, granularity),\n value: Number(r.value),\n }));\n\n const data = fillBuckets(\n new Date(dateRange.from),\n new Date(dateRange.to),\n granularity,\n dateFormat,\n mappedRows,\n );\n\n return { metric: params.metric, granularity, data };\n }\n\n private granularityToClickHouseFunc(g: Granularity): string {\n switch (g) {\n case 'hour': return 'toStartOfHour(timestamp)';\n case 'day': return 'toStartOfDay(timestamp)';\n case 'week': return 'toStartOfWeek(timestamp, 1)'; // 1 = Monday\n case 'month': return 'toStartOfMonth(timestamp)';\n }\n }\n\n private convertClickHouseBucket(bucket: string, granularity: Granularity): string {\n // ClickHouse returns ISO datetime strings, convert to the format used by fillBuckets\n const date = new Date(bucket);\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n const h = String(date.getHours()).padStart(2, '0');\n\n switch (granularity) {\n case 'hour': return `${y}-${m}-${d}T${h}:00`;\n case 'day': return `${y}-${m}-${d}`;\n case 'week': {\n const jan4 = new Date(y, 0, 4);\n const dayOfYear = Math.ceil((date.getTime() - new Date(y, 0, 1).getTime()) / 86400000) + 1;\n const jan4Day = jan4.getDay() || 7;\n const weekNum = Math.ceil((dayOfYear + jan4Day - 1) / 7);\n return `${y}-W${String(weekNum).padStart(2, '0')}`;\n }\n case 'month': return `${y}-${m}`;\n }\n }\n\n // ─── Retention ──────────────────────────────────────\n\n async queryRetention(params: RetentionParams): Promise<RetentionResult> {\n const weeks = params.weeks ?? 8;\n const now = new Date();\n const startDate = new Date(now.getTime() - weeks * 7 * 24 * 60 * 60 * 1000);\n\n const rows = await this.queryRows<{\n visitor_id: string;\n first_event: string;\n active_weeks: string[];\n }>(\n `SELECT\n visitor_id,\n min(timestamp) AS first_event,\n groupUniqArray(toStartOfWeek(timestamp, 1)) AS active_weeks\n FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND timestamp >= {since:String}\n GROUP BY visitor_id`,\n {\n siteId: params.siteId,\n since: toCHDateTime(startDate),\n },\n );\n\n // Group visitors by cohort week\n const cohortMap = new Map<string, { visitors: Set<string>; weekSets: Map<string, Set<string>> }>();\n\n for (const v of rows) {\n const firstDate = new Date(v.first_event);\n const cohortWeek = getISOWeek(firstDate);\n if (!cohortMap.has(cohortWeek)) {\n cohortMap.set(cohortWeek, { visitors: new Set(), weekSets: new Map() });\n }\n const cohort = cohortMap.get(cohortWeek)!;\n cohort.visitors.add(v.visitor_id);\n\n // active_weeks from ClickHouse are DateTime strings\n const eventWeeks = (Array.isArray(v.active_weeks) ? v.active_weeks : []).map((w: string) => {\n const d = new Date(w);\n return getISOWeek(d);\n });\n\n for (const w of eventWeeks) {\n if (!cohort.weekSets.has(w)) {\n cohort.weekSets.set(w, new Set());\n }\n cohort.weekSets.get(w)!.add(v.visitor_id);\n }\n }\n\n const sortedWeeks = Array.from(cohortMap.keys()).sort();\n const cohorts: RetentionCohort[] = sortedWeeks.map((week) => {\n const cohort = cohortMap.get(week)!;\n const size = cohort.visitors.size;\n\n const retention: number[] = [];\n const weekIndex = sortedWeeks.indexOf(week);\n for (let i = 0; i < weeks && (weekIndex + i) < sortedWeeks.length; i++) {\n const targetWeek = sortedWeeks[weekIndex + i];\n const returnedCount = cohort.weekSets.get(targetWeek)?.size ?? 0;\n retention.push(size > 0 ? Math.round((returnedCount / size) * 1000) / 10 : 0);\n }\n\n return { week, size, retention };\n });\n\n return { cohorts };\n }\n\n // ─── Event Listing ──────────────────────────────────────\n\n async listEvents(params: EventListParams): Promise<EventListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const conditions: string[] = [`site_id = {siteId:String}`];\n const queryParams: Record<string, unknown> = { siteId: params.siteId, limit, offset };\n\n if (params.type) {\n conditions.push(`type = {type:String}`);\n queryParams.type = params.type;\n }\n if (params.eventName) {\n conditions.push(`event_name = {eventName:String}`);\n queryParams.eventName = params.eventName;\n }\n if (params.eventSource) {\n conditions.push(`event_source = {eventSource:String}`);\n queryParams.eventSource = params.eventSource;\n }\n if (params.eventNames && params.eventNames.length > 0) {\n conditions.push(`event_name IN {eventNames:Array(String)}`);\n queryParams.eventNames = params.eventNames;\n }\n if (params.visitorId) {\n conditions.push(`visitor_id = {visitorId:String}`);\n queryParams.visitorId = params.visitorId;\n }\n if (params.userId) {\n conditions.push(`user_id = {userId:String}`);\n queryParams.userId = params.userId;\n }\n\n if (params.period || params.dateFrom) {\n const { dateRange } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n conditions.push(`timestamp >= {from:String} AND timestamp <= {to:String}`);\n queryParams.from = toCHDateTime(dateRange.from);\n queryParams.to = toCHDateTime(dateRange.to);\n }\n\n const where = conditions.join(' AND ');\n\n const [events, countRows] = await Promise.all([\n this.queryRows<Record<string, unknown>>(\n `SELECT event_id, type, timestamp, session_id, visitor_id, url, referrer, title,\n event_name, properties, event_source, event_subtype, page_path, target_url_path,\n element_selector, element_text, scroll_depth_pct,\n user_id, traits, country, city, region,\n device_type, browser, os, language,\n utm_source, utm_medium, utm_campaign, utm_term, utm_content\n FROM ${EVENTS_TABLE}\n WHERE ${where}\n ORDER BY timestamp DESC\n LIMIT {limit:UInt32}\n OFFSET {offset:UInt32}`,\n queryParams,\n ),\n this.queryRows<{ total: string }>(\n `SELECT count() AS total FROM ${EVENTS_TABLE} WHERE ${where}`,\n queryParams,\n ),\n ]);\n\n return {\n events: events.map((e) => this.toEventListItem(e)),\n total: Number(countRows[0]?.total ?? 0),\n limit,\n offset,\n };\n }\n\n // ─── User Listing ──────────────────────────────────────\n\n async listUsers(params: UserListParams): Promise<UserListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const conditions: string[] = [`site_id = {siteId:String}`];\n const queryParams: Record<string, unknown> = { siteId: params.siteId, limit, offset };\n\n if (params.search) {\n conditions.push(`(visitor_id ILIKE {search:String} OR user_id ILIKE {search:String})`);\n queryParams.search = `%${params.search}%`;\n }\n\n const where = conditions.join(' AND ');\n\n const [userRows, countRows] = await Promise.all([\n this.queryRows<Record<string, unknown>>(\n `WITH identity AS (\n SELECT visitor_id, user_id\n FROM ${IDENTITY_MAP_TABLE} FINAL\n WHERE site_id = {siteId:String}\n )\n SELECT\n if(i.user_id IS NOT NULL AND i.user_id != '', i.user_id, e.visitor_id) AS group_key,\n anyLast(e.visitor_id) AS visitor_id,\n anyLast(i.user_id) AS userId,\n anyLast(e.traits) AS traits,\n min(e.timestamp) AS firstSeen,\n max(e.timestamp) AS lastSeen,\n count() AS totalEvents,\n countIf(e.type = 'pageview') AS totalPageviews,\n uniq(e.session_id) AS totalSessions,\n anyLast(e.url) AS lastUrl,\n anyLast(e.referrer) AS referrer,\n anyLast(e.device_type) AS device_type,\n anyLast(e.browser) AS browser,\n anyLast(e.os) AS os,\n anyLast(e.country) AS country,\n anyLast(e.city) AS city,\n anyLast(e.region) AS region,\n anyLast(e.language) AS language,\n anyLast(e.timezone) AS timezone,\n anyLast(e.screen_width) AS screen_width,\n anyLast(e.screen_height) AS screen_height,\n anyLast(e.utm_source) AS utm_source,\n anyLast(e.utm_medium) AS utm_medium,\n anyLast(e.utm_campaign) AS utm_campaign,\n anyLast(e.utm_term) AS utm_term,\n anyLast(e.utm_content) AS utm_content\n FROM ${EVENTS_TABLE} e\n LEFT JOIN identity i ON e.visitor_id = i.visitor_id\n WHERE e.site_id = {siteId:String}${where.includes('ILIKE') ? ` AND (e.visitor_id ILIKE {search:String} OR i.user_id ILIKE {search:String})` : ''}\n GROUP BY group_key\n ORDER BY lastSeen DESC\n LIMIT {limit:UInt32}\n OFFSET {offset:UInt32}`,\n queryParams,\n ),\n this.queryRows<{ total: string }>(\n `WITH identity AS (\n SELECT visitor_id, user_id\n FROM ${IDENTITY_MAP_TABLE} FINAL\n WHERE site_id = {siteId:String}\n )\n SELECT count() AS total FROM (\n SELECT if(i.user_id IS NOT NULL AND i.user_id != '', i.user_id, e.visitor_id) AS group_key\n FROM ${EVENTS_TABLE} e\n LEFT JOIN identity i ON e.visitor_id = i.visitor_id\n WHERE e.site_id = {siteId:String}${where.includes('ILIKE') ? ` AND (e.visitor_id ILIKE {search:String} OR i.user_id ILIKE {search:String})` : ''}\n GROUP BY group_key\n )`,\n queryParams,\n ),\n ]);\n\n const users: UserDetail[] = userRows.map((u) => ({\n visitorId: String(u.visitor_id),\n userId: u.userId ? String(u.userId) : undefined,\n traits: this.parseJSON(u.traits as string | null),\n firstSeen: new Date(String(u.firstSeen)).toISOString(),\n lastSeen: new Date(String(u.lastSeen)).toISOString(),\n totalEvents: Number(u.totalEvents),\n totalPageviews: Number(u.totalPageviews),\n totalSessions: Number(u.totalSessions),\n lastUrl: u.lastUrl ? String(u.lastUrl) : undefined,\n referrer: u.referrer ? String(u.referrer) : undefined,\n device: u.device_type ? { type: String(u.device_type), browser: String(u.browser ?? ''), os: String(u.os ?? '') } : undefined,\n geo: u.country ? { country: String(u.country), city: u.city ? String(u.city) : undefined, region: u.region ? String(u.region) : undefined } : undefined,\n language: u.language ? String(u.language) : undefined,\n timezone: u.timezone ? String(u.timezone) : undefined,\n screen: (u.screen_width || u.screen_height) ? { width: Number(u.screen_width ?? 0), height: Number(u.screen_height ?? 0) } : undefined,\n utm: u.utm_source ? {\n source: String(u.utm_source),\n medium: u.utm_medium ? String(u.utm_medium) : undefined,\n campaign: u.utm_campaign ? String(u.utm_campaign) : undefined,\n term: u.utm_term ? String(u.utm_term) : undefined,\n content: u.utm_content ? String(u.utm_content) : undefined,\n } : undefined,\n }));\n\n return {\n users,\n total: Number(countRows[0]?.total ?? 0),\n limit,\n offset,\n };\n }\n\n async getUserDetail(siteId: string, identifier: string): Promise<UserDetail | null> {\n // Try as userId first (check identity map)\n const visitorIds = await this.getVisitorIdsForUser(siteId, identifier);\n if (visitorIds.length > 0) {\n return this.getMergedUserDetail(siteId, identifier, visitorIds);\n }\n // Try as visitorId — check if it has a known userId\n const userId = await this.getUserIdForVisitor(siteId, identifier);\n if (userId) {\n const allVisitorIds = await this.getVisitorIdsForUser(siteId, userId);\n return this.getMergedUserDetail(siteId, userId, allVisitorIds.length > 0 ? allVisitorIds : [identifier]);\n }\n // Pure anonymous — single visitorId\n const result = await this.listUsers({ siteId, search: identifier, limit: 1 });\n const user = result.users.find((u) => u.visitorId === identifier);\n return user ?? null;\n }\n\n async getUserEvents(siteId: string, identifier: string, params: EventListParams): Promise<EventListResult> {\n // Resolve all linked visitorIds\n const visitorIds = await this.getVisitorIdsForUser(siteId, identifier);\n if (visitorIds.length > 0) {\n return this.listEventsForVisitorIds(siteId, visitorIds, params);\n }\n // Check if identifier is a visitorId with a known userId\n const userId = await this.getUserIdForVisitor(siteId, identifier);\n if (userId) {\n const allVisitorIds = await this.getVisitorIdsForUser(siteId, userId);\n if (allVisitorIds.length > 0) {\n return this.listEventsForVisitorIds(siteId, allVisitorIds, params);\n }\n }\n // Pure anonymous — single visitorId\n return this.listEvents({ ...params, siteId, visitorId: identifier });\n }\n\n // ─── Identity Mapping ──────────────────────────────────────\n\n async upsertIdentity(siteId: string, visitorId: string, userId: string): Promise<void> {\n await this.client.insert({\n table: IDENTITY_MAP_TABLE,\n values: [{\n site_id: siteId,\n visitor_id: visitorId,\n user_id: userId,\n identified_at: toCHDateTime(new Date()),\n }],\n format: 'JSONEachRow',\n });\n }\n\n async getVisitorIdsForUser(siteId: string, userId: string): Promise<string[]> {\n const rows = await this.queryRows<{ visitor_id: string }>(\n `SELECT visitor_id FROM ${IDENTITY_MAP_TABLE} FINAL\n WHERE site_id = {siteId:String} AND user_id = {userId:String}`,\n { siteId, userId },\n );\n return rows.map((r) => r.visitor_id);\n }\n\n async getUserIdForVisitor(siteId: string, visitorId: string): Promise<string | null> {\n const rows = await this.queryRows<{ user_id: string }>(\n `SELECT user_id FROM ${IDENTITY_MAP_TABLE} FINAL\n WHERE site_id = {siteId:String} AND visitor_id = {visitorId:String}\n LIMIT 1`,\n { siteId, visitorId },\n );\n return rows.length > 0 ? rows[0].user_id : null;\n }\n\n private async getMergedUserDetail(siteId: string, userId: string, visitorIds: string[]): Promise<UserDetail | null> {\n const rows = await this.queryRows<Record<string, unknown>>(\n `SELECT\n anyLast(visitor_id) AS visitor_id,\n anyLast(traits) AS traits,\n min(timestamp) AS firstSeen,\n max(timestamp) AS lastSeen,\n count() AS totalEvents,\n countIf(type = 'pageview') AS totalPageviews,\n uniq(session_id) AS totalSessions,\n anyLast(url) AS lastUrl,\n anyLast(referrer) AS referrer,\n anyLast(device_type) AS device_type,\n anyLast(browser) AS browser,\n anyLast(os) AS os,\n anyLast(country) AS country,\n anyLast(city) AS city,\n anyLast(region) AS region,\n anyLast(language) AS language,\n anyLast(timezone) AS timezone,\n anyLast(screen_width) AS screen_width,\n anyLast(screen_height) AS screen_height,\n anyLast(utm_source) AS utm_source,\n anyLast(utm_medium) AS utm_medium,\n anyLast(utm_campaign) AS utm_campaign,\n anyLast(utm_term) AS utm_term,\n anyLast(utm_content) AS utm_content\n FROM ${EVENTS_TABLE}\n WHERE site_id = {siteId:String}\n AND visitor_id IN {visitorIds:Array(String)}`,\n { siteId, visitorIds },\n );\n if (rows.length === 0) return null;\n const u = rows[0];\n return {\n visitorId: String(u.visitor_id),\n visitorIds,\n userId,\n traits: this.parseJSON(u.traits as string | null),\n firstSeen: new Date(String(u.firstSeen)).toISOString(),\n lastSeen: new Date(String(u.lastSeen)).toISOString(),\n totalEvents: Number(u.totalEvents),\n totalPageviews: Number(u.totalPageviews),\n totalSessions: Number(u.totalSessions),\n lastUrl: u.lastUrl ? String(u.lastUrl) : undefined,\n referrer: u.referrer ? String(u.referrer) : undefined,\n device: u.device_type ? { type: String(u.device_type), browser: String(u.browser ?? ''), os: String(u.os ?? '') } : undefined,\n geo: u.country ? { country: String(u.country), city: u.city ? String(u.city) : undefined, region: u.region ? String(u.region) : undefined } : undefined,\n language: u.language ? String(u.language) : undefined,\n timezone: u.timezone ? String(u.timezone) : undefined,\n screen: (u.screen_width || u.screen_height) ? { width: Number(u.screen_width ?? 0), height: Number(u.screen_height ?? 0) } : undefined,\n utm: u.utm_source ? {\n source: String(u.utm_source),\n medium: u.utm_medium ? String(u.utm_medium) : undefined,\n campaign: u.utm_campaign ? String(u.utm_campaign) : undefined,\n term: u.utm_term ? String(u.utm_term) : undefined,\n content: u.utm_content ? String(u.utm_content) : undefined,\n } : undefined,\n };\n }\n\n private async listEventsForVisitorIds(siteId: string, visitorIds: string[], params: EventListParams): Promise<EventListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const conditions: string[] = [`site_id = {siteId:String}`, `visitor_id IN {visitorIds:Array(String)}`];\n const queryParams: Record<string, unknown> = { siteId, visitorIds, limit, offset };\n\n if (params.type) {\n conditions.push(`type = {type:String}`);\n queryParams.type = params.type;\n }\n if (params.eventName) {\n conditions.push(`event_name = {eventName:String}`);\n queryParams.eventName = params.eventName;\n }\n if (params.eventNames && params.eventNames.length > 0) {\n conditions.push(`event_name IN {eventNames:Array(String)}`);\n queryParams.eventNames = params.eventNames;\n }\n if (params.period || params.dateFrom) {\n const { dateRange } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n conditions.push(`timestamp >= {from:String} AND timestamp <= {to:String}`);\n queryParams.from = toCHDateTime(dateRange.from);\n queryParams.to = toCHDateTime(dateRange.to);\n }\n\n const where = conditions.join(' AND ');\n\n const [events, countRows] = await Promise.all([\n this.queryRows<Record<string, unknown>>(\n `SELECT event_id, type, timestamp, session_id, visitor_id, url, referrer, title,\n event_name, properties, event_source, event_subtype, page_path, target_url_path,\n element_selector, element_text, scroll_depth_pct,\n user_id, traits, country, city, region,\n device_type, browser, os, language,\n utm_source, utm_medium, utm_campaign, utm_term, utm_content\n FROM ${EVENTS_TABLE}\n WHERE ${where}\n ORDER BY timestamp DESC\n LIMIT {limit:UInt32}\n OFFSET {offset:UInt32}`,\n queryParams,\n ),\n this.queryRows<{ total: string }>(\n `SELECT count() AS total FROM ${EVENTS_TABLE} WHERE ${where}`,\n queryParams,\n ),\n ]);\n\n return {\n events: events.map((e) => this.toEventListItem(e)),\n total: Number(countRows[0]?.total ?? 0),\n limit,\n offset,\n };\n }\n\n // ─── Site Management ──────────────────────────────────────\n\n async createSite(data: CreateSiteRequest): Promise<Site> {\n const now = new Date();\n const nowISO = now.toISOString();\n const nowCH = toCHDateTime(now);\n const site: Site = {\n siteId: generateSiteId(),\n secretKey: generateSecretKey(),\n name: data.name,\n domain: data.domain,\n allowedOrigins: data.allowedOrigins,\n conversionEvents: data.conversionEvents,\n createdAt: nowISO,\n updatedAt: nowISO,\n };\n\n await this.client.insert({\n table: SITES_TABLE,\n values: [{\n site_id: site.siteId,\n secret_key: site.secretKey,\n name: site.name,\n domain: site.domain ?? null,\n allowed_origins: site.allowedOrigins ? JSON.stringify(site.allowedOrigins) : null,\n conversion_events: site.conversionEvents ? JSON.stringify(site.conversionEvents) : null,\n created_at: nowCH,\n updated_at: nowCH,\n version: 1,\n is_deleted: 0,\n }],\n format: 'JSONEachRow',\n });\n\n return site;\n }\n\n async getSite(siteId: string): Promise<Site | null> {\n const rows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, conversion_events, created_at, updated_at\n FROM ${SITES_TABLE} FINAL\n WHERE site_id = {siteId:String} AND is_deleted = 0`,\n { siteId },\n );\n return rows.length > 0 ? this.toSite(rows[0]) : null;\n }\n\n async getSiteBySecret(secretKey: string): Promise<Site | null> {\n const rows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, conversion_events, created_at, updated_at\n FROM ${SITES_TABLE} FINAL\n WHERE secret_key = {secretKey:String} AND is_deleted = 0`,\n { secretKey },\n );\n return rows.length > 0 ? this.toSite(rows[0]) : null;\n }\n\n async listSites(): Promise<Site[]> {\n const rows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, conversion_events, created_at, updated_at\n FROM ${SITES_TABLE} FINAL\n WHERE is_deleted = 0\n ORDER BY created_at DESC`,\n {},\n );\n return rows.map((r) => this.toSite(r));\n }\n\n async updateSite(siteId: string, data: UpdateSiteRequest): Promise<Site | null> {\n // Read current site with version\n const currentRows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, conversion_events, created_at, updated_at, version\n FROM ${SITES_TABLE} FINAL\n WHERE site_id = {siteId:String} AND is_deleted = 0`,\n { siteId },\n );\n if (currentRows.length === 0) return null;\n\n const current = currentRows[0];\n const now = new Date();\n const nowISO = now.toISOString();\n const nowCH = toCHDateTime(now);\n const newVersion = Number(current.version) + 1;\n\n const newName = data.name !== undefined ? data.name : String(current.name);\n const newDomain = data.domain !== undefined ? (data.domain || null) : (current.domain ? String(current.domain) : null);\n const newOrigins = data.allowedOrigins !== undefined\n ? (data.allowedOrigins.length > 0 ? JSON.stringify(data.allowedOrigins) : null)\n : (current.allowed_origins ? String(current.allowed_origins) : null);\n const newConversions = data.conversionEvents !== undefined\n ? (data.conversionEvents.length > 0 ? JSON.stringify(data.conversionEvents) : null)\n : (current.conversion_events ? String(current.conversion_events) : null);\n\n await this.client.insert({\n table: SITES_TABLE,\n values: [{\n site_id: String(current.site_id),\n secret_key: String(current.secret_key),\n name: newName,\n domain: newDomain,\n allowed_origins: newOrigins,\n conversion_events: newConversions,\n created_at: toCHDateTime(String(current.created_at)),\n updated_at: nowCH,\n version: newVersion,\n is_deleted: 0,\n }],\n format: 'JSONEachRow',\n });\n\n return {\n siteId: String(current.site_id),\n secretKey: String(current.secret_key),\n name: newName,\n domain: newDomain ?? undefined,\n allowedOrigins: newOrigins ? JSON.parse(newOrigins) : undefined,\n conversionEvents: newConversions ? JSON.parse(newConversions) : undefined,\n createdAt: String(current.created_at),\n updatedAt: nowISO,\n };\n }\n\n async deleteSite(siteId: string): Promise<boolean> {\n const currentRows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, conversion_events, created_at, version\n FROM ${SITES_TABLE} FINAL\n WHERE site_id = {siteId:String} AND is_deleted = 0`,\n { siteId },\n );\n if (currentRows.length === 0) return false;\n\n const current = currentRows[0];\n const nowCH = toCHDateTime(new Date());\n\n await this.client.insert({\n table: SITES_TABLE,\n values: [{\n site_id: String(current.site_id),\n secret_key: String(current.secret_key),\n name: String(current.name),\n domain: current.domain ? String(current.domain) : null,\n allowed_origins: current.allowed_origins ? String(current.allowed_origins) : null,\n conversion_events: current.conversion_events ? String(current.conversion_events) : null,\n created_at: toCHDateTime(String(current.created_at)),\n updated_at: nowCH,\n version: Number(current.version) + 1,\n is_deleted: 1,\n }],\n format: 'JSONEachRow',\n });\n\n return true;\n }\n\n async regenerateSecret(siteId: string): Promise<Site | null> {\n const currentRows = await this.queryRows<Record<string, unknown>>(\n `SELECT site_id, secret_key, name, domain, allowed_origins, conversion_events, created_at, version\n FROM ${SITES_TABLE} FINAL\n WHERE site_id = {siteId:String} AND is_deleted = 0`,\n { siteId },\n );\n if (currentRows.length === 0) return null;\n\n const current = currentRows[0];\n const now = new Date();\n const nowISO = now.toISOString();\n const nowCH = toCHDateTime(now);\n const newSecret = generateSecretKey();\n\n await this.client.insert({\n table: SITES_TABLE,\n values: [{\n site_id: String(current.site_id),\n secret_key: newSecret,\n name: String(current.name),\n domain: current.domain ? String(current.domain) : null,\n allowed_origins: current.allowed_origins ? String(current.allowed_origins) : null,\n conversion_events: current.conversion_events ? String(current.conversion_events) : null,\n created_at: toCHDateTime(String(current.created_at)),\n updated_at: nowCH,\n version: Number(current.version) + 1,\n is_deleted: 0,\n }],\n format: 'JSONEachRow',\n });\n\n return {\n siteId: String(current.site_id),\n secretKey: newSecret,\n name: String(current.name),\n domain: current.domain ? String(current.domain) : undefined,\n allowedOrigins: current.allowed_origins ? JSON.parse(String(current.allowed_origins)) : undefined,\n conversionEvents: current.conversion_events ? JSON.parse(String(current.conversion_events)) : undefined,\n createdAt: String(current.created_at),\n updatedAt: nowISO,\n };\n }\n\n // ─── Helpers ─────────────────────────────────────────────\n\n private async queryRows<T>(query: string, query_params: Record<string, unknown>): Promise<T[]> {\n const result = await this.client.query({\n query,\n query_params,\n format: 'JSONEachRow',\n });\n return result.json<T>();\n }\n\n private toSite(row: Record<string, unknown>): Site {\n return {\n siteId: String(row.site_id),\n secretKey: String(row.secret_key),\n name: String(row.name),\n domain: row.domain ? String(row.domain) : undefined,\n allowedOrigins: row.allowed_origins ? JSON.parse(String(row.allowed_origins)) : undefined,\n conversionEvents: row.conversion_events ? JSON.parse(String(row.conversion_events)) : undefined,\n createdAt: new Date(String(row.created_at)).toISOString(),\n updatedAt: new Date(String(row.updated_at)).toISOString(),\n };\n }\n\n private toEventListItem(row: Record<string, unknown>): EventListItem {\n return {\n id: String(row.event_id ?? ''),\n type: String(row.type) as EventListItem['type'],\n timestamp: new Date(String(row.timestamp)).toISOString(),\n visitorId: String(row.visitor_id),\n sessionId: String(row.session_id),\n url: row.url ? String(row.url) : undefined,\n referrer: row.referrer ? String(row.referrer) : undefined,\n title: row.title ? String(row.title) : undefined,\n name: row.event_name ? String(row.event_name) : undefined,\n properties: this.parseJSON(row.properties as string | null),\n eventSource: row.event_source ? String(row.event_source) as EventListItem['eventSource'] : undefined,\n eventSubtype: row.event_subtype ? String(row.event_subtype) as EventListItem['eventSubtype'] : undefined,\n pagePath: row.page_path ? String(row.page_path) : undefined,\n targetUrlPath: row.target_url_path ? String(row.target_url_path) : undefined,\n elementSelector: row.element_selector ? String(row.element_selector) : undefined,\n elementText: row.element_text ? String(row.element_text) : undefined,\n scrollDepthPct: row.scroll_depth_pct !== null && row.scroll_depth_pct !== undefined ? Number(row.scroll_depth_pct) : undefined,\n userId: row.user_id ? String(row.user_id) : undefined,\n traits: this.parseJSON(row.traits as string | null),\n geo: row.country ? {\n country: String(row.country),\n city: row.city ? String(row.city) : undefined,\n region: row.region ? String(row.region) : undefined,\n } : undefined,\n device: row.device_type ? {\n type: String(row.device_type),\n browser: String(row.browser ?? ''),\n os: String(row.os ?? ''),\n } : undefined,\n language: row.language ? String(row.language) : undefined,\n utm: row.utm_source ? {\n source: row.utm_source ? String(row.utm_source) : undefined,\n medium: row.utm_medium ? String(row.utm_medium) : undefined,\n campaign: row.utm_campaign ? String(row.utm_campaign) : undefined,\n term: row.utm_term ? String(row.utm_term) : undefined,\n content: row.utm_content ? String(row.utm_content) : undefined,\n } : undefined,\n };\n }\n\n private parseJSON(str: string | null | undefined): Record<string, unknown> | undefined {\n if (!str) return undefined;\n try {\n return JSON.parse(str);\n } catch {\n return undefined;\n }\n }\n}\n","import type { QueryParams, Period, Granularity, TimeSeriesPoint } from '@litemetrics/core';\nimport { randomBytes } from 'crypto';\n\nexport function resolvePeriod(q: Pick<QueryParams, 'period' | 'dateFrom' | 'dateTo'>): { dateRange: { from: string; to: string }; period: Period } {\n const now = new Date();\n const period = q.period ?? '7d';\n\n if (period === 'custom' && q.dateFrom && q.dateTo) {\n return { dateRange: { from: q.dateFrom, to: q.dateTo }, period };\n }\n\n const to = now.toISOString();\n let from: Date;\n\n switch (period) {\n case '1h': from = new Date(now.getTime() - 60 * 60 * 1000); break;\n case '24h': from = new Date(now.getTime() - 24 * 60 * 60 * 1000); break;\n case '7d': from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); break;\n case '30d': from = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000); break;\n case '90d': from = new Date(now.getTime() - 90 * 24 * 60 * 60 * 1000); break;\n default: from = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000); break;\n }\n\n return { dateRange: { from: from.toISOString(), to }, period };\n}\n\nexport function previousPeriodRange(currentRange: { from: string; to: string }): { from: string; to: string } {\n const from = new Date(currentRange.from);\n const to = new Date(currentRange.to);\n const duration = to.getTime() - from.getTime();\n const prevTo = new Date(from.getTime() - 1);\n const prevFrom = new Date(prevTo.getTime() - duration);\n return { from: prevFrom.toISOString(), to: prevTo.toISOString() };\n}\n\nexport function autoGranularity(period: Period): Granularity {\n switch (period) {\n case '1h': return 'hour';\n case '24h': return 'hour';\n case '7d': return 'day';\n case '30d': return 'day';\n case '90d': return 'week';\n default: return 'day';\n }\n}\n\nexport function granularityToDateFormat(g: Granularity): string {\n switch (g) {\n case 'hour': return '%Y-%m-%dT%H:00';\n case 'day': return '%Y-%m-%d';\n case 'week': return '%G-W%V';\n case 'month': return '%Y-%m';\n }\n}\n\nexport function fillBuckets(\n from: Date,\n to: Date,\n granularity: Granularity,\n dateFormat: string,\n rows: { _id: string; value: number }[],\n): TimeSeriesPoint[] {\n const map = new Map(rows.map((r) => [r._id, r.value]));\n const points: TimeSeriesPoint[] = [];\n const current = new Date(from);\n\n // Align to bucket start\n if (granularity === 'hour') {\n current.setMinutes(0, 0, 0);\n } else if (granularity === 'day') {\n current.setHours(0, 0, 0, 0);\n } else if (granularity === 'week') {\n const day = current.getDay();\n const diff = day === 0 ? -6 : 1 - day;\n current.setDate(current.getDate() + diff);\n current.setHours(0, 0, 0, 0);\n } else if (granularity === 'month') {\n current.setDate(1);\n current.setHours(0, 0, 0, 0);\n }\n\n while (current <= to) {\n const key = formatDateBucket(current, dateFormat);\n points.push({ date: current.toISOString(), value: map.get(key) ?? 0 });\n\n if (granularity === 'hour') {\n current.setHours(current.getHours() + 1);\n } else if (granularity === 'day') {\n current.setDate(current.getDate() + 1);\n } else if (granularity === 'week') {\n current.setDate(current.getDate() + 7);\n } else if (granularity === 'month') {\n current.setMonth(current.getMonth() + 1);\n }\n }\n\n return points;\n}\n\nexport function formatDateBucket(date: Date, format: string): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, '0');\n const d = String(date.getDate()).padStart(2, '0');\n const h = String(date.getHours()).padStart(2, '0');\n\n if (format === '%Y-%m-%dT%H:00') return `${y}-${m}-${d}T${h}:00`;\n if (format === '%Y-%m-%d') return `${y}-${m}-${d}`;\n if (format === '%Y-%m') return `${y}-${m}`;\n if (format === '%G-W%V') {\n const jan4 = new Date(y, 0, 4);\n const dayOfYear = Math.ceil((date.getTime() - new Date(y, 0, 1).getTime()) / 86400000) + 1;\n const jan4Day = jan4.getDay() || 7;\n const weekNum = Math.ceil((dayOfYear + jan4Day - 1) / 7);\n return `${y}-W${String(weekNum).padStart(2, '0')}`;\n }\n return date.toISOString();\n}\n\nexport function getISOWeek(date: Date): string {\n const y = date.getFullYear();\n const jan4 = new Date(y, 0, 4);\n const dayOfYear = Math.ceil((date.getTime() - new Date(y, 0, 1).getTime()) / 86400000) + 1;\n const jan4Day = jan4.getDay() || 7;\n const weekNum = Math.ceil((dayOfYear + jan4Day - 1) / 7);\n return `${y}-W${String(weekNum).padStart(2, '0')}`;\n}\n\nexport function generateSiteId(): string {\n const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';\n const bytes = randomBytes(12);\n let id = '';\n for (let i = 0; i < 12; i++) id += chars[bytes[i] % chars.length];\n return `site_${id}`;\n}\n\nexport function generateSecretKey(): string {\n return `sk_${randomBytes(32).toString('hex')}`;\n}\n","import type { DBAdapter, EnrichedEvent, QueryParams, QueryResult, QueryDataPoint, TimeSeriesParams, TimeSeriesResult, RetentionParams, RetentionResult, RetentionCohort, Site, CreateSiteRequest, UpdateSiteRequest, EventListParams, EventListResult, EventListItem, UserListParams, UserListResult, UserDetail } from '@litemetrics/core';\nimport { MongoClient, type Collection, type Db } from 'mongodb';\nimport { resolvePeriod, previousPeriodRange, autoGranularity, granularityToDateFormat, fillBuckets, getISOWeek, generateSiteId, generateSecretKey } from './utils';\n\ninterface EventDocument {\n site_id: string;\n type: string;\n timestamp: Date;\n session_id: string;\n visitor_id: string;\n url: string | null;\n referrer: string | null;\n title: string | null;\n event_name: string | null;\n properties: Record<string, unknown> | null;\n event_source: string | null;\n event_subtype: string | null;\n page_path: string | null;\n target_url_path: string | null;\n element_selector: string | null;\n element_text: string | null;\n scroll_depth_pct: number | null;\n user_id: string | null;\n traits: Record<string, unknown> | null;\n country: string | null;\n city: string | null;\n region: string | null;\n device_type: string | null;\n browser: string | null;\n os: string | null;\n language: string | null;\n timezone: string | null;\n screen_width: number | null;\n screen_height: number | null;\n utm_source: string | null;\n utm_medium: string | null;\n utm_campaign: string | null;\n utm_term: string | null;\n utm_content: string | null;\n ip: string | null;\n created_at: Date;\n}\n\ninterface SiteDocument {\n site_id: string;\n secret_key: string;\n name: string;\n domain: string | null;\n allowed_origins: string[] | null;\n conversion_events: string[] | null;\n created_at: Date;\n updated_at: Date;\n}\n\ninterface IdentityMapDocument {\n site_id: string;\n visitor_id: string;\n user_id: string;\n identified_at: Date;\n created_at: Date;\n}\n\nconst EVENTS_COLLECTION = 'litemetrics_events';\nconst SITES_COLLECTION = 'litemetrics_sites';\nconst IDENTITY_MAP_COLLECTION = 'litemetrics_identity_map';\n\nfunction buildFilterMatch(filters?: Record<string, string>): Record<string, unknown> {\n if (!filters) return {};\n const map: Record<string, string> = {\n 'geo.country': 'country',\n 'geo.city': 'city',\n 'geo.region': 'region',\n 'language': 'language',\n 'device.type': 'device_type',\n 'device.browser': 'browser',\n 'device.os': 'os',\n 'utm.source': 'utm_source',\n 'utm.medium': 'utm_medium',\n 'utm.campaign': 'utm_campaign',\n 'utm.term': 'utm_term',\n 'utm.content': 'utm_content',\n 'referrer': 'referrer',\n 'event_source': 'event_source',\n 'event_subtype': 'event_subtype',\n 'page_path': 'page_path',\n 'target_url_path': 'target_url_path',\n 'event_name': 'event_name',\n 'type': 'type',\n };\n const match: Record<string, unknown> = {};\n for (const [key, value] of Object.entries(filters)) {\n if (!value || !map[key]) continue;\n match[map[key]] = value;\n }\n return match;\n}\n\nexport class MongoDBAdapter implements DBAdapter {\n private client: MongoClient;\n private db!: Db;\n private collection!: Collection<EventDocument>;\n private sites!: Collection<SiteDocument>;\n private identityMap!: Collection<IdentityMapDocument>;\n\n constructor(url: string) {\n this.client = new MongoClient(url);\n }\n\n async init(): Promise<void> {\n await this.client.connect();\n this.db = this.client.db();\n this.collection = this.db.collection<EventDocument>(EVENTS_COLLECTION);\n this.sites = this.db.collection<SiteDocument>(SITES_COLLECTION);\n this.identityMap = this.db.collection<IdentityMapDocument>(IDENTITY_MAP_COLLECTION);\n\n await Promise.all([\n this.collection.createIndex({ site_id: 1, timestamp: -1 }),\n this.collection.createIndex({ site_id: 1, type: 1 }),\n this.collection.createIndex({ site_id: 1, visitor_id: 1 }),\n this.collection.createIndex({ site_id: 1, session_id: 1 }),\n this.sites.createIndex({ site_id: 1 }, { unique: true }),\n this.sites.createIndex({ secret_key: 1 }),\n this.identityMap.createIndex({ site_id: 1, visitor_id: 1 }, { unique: true }),\n this.identityMap.createIndex({ site_id: 1, user_id: 1 }),\n ]);\n }\n\n async insertEvents(events: EnrichedEvent[]): Promise<void> {\n if (events.length === 0) return;\n\n const docs: EventDocument[] = events.map((e) => ({\n site_id: e.siteId,\n type: e.type,\n timestamp: new Date(e.timestamp),\n session_id: e.sessionId,\n visitor_id: e.visitorId,\n url: e.url ?? null,\n referrer: e.referrer ?? null,\n title: e.title ?? null,\n event_name: e.name ?? null,\n properties: e.properties ?? null,\n event_source: e.eventSource ?? null,\n event_subtype: e.eventSubtype ?? null,\n page_path: e.pagePath ?? null,\n target_url_path: e.targetUrlPath ?? null,\n element_selector: e.elementSelector ?? null,\n element_text: e.elementText ?? null,\n scroll_depth_pct: e.scrollDepthPct ?? null,\n user_id: e.userId ?? null,\n traits: e.traits ?? null,\n country: e.geo?.country ?? null,\n city: e.geo?.city ?? null,\n region: e.geo?.region ?? null,\n device_type: e.device?.type ?? null,\n browser: e.device?.browser ?? null,\n os: e.device?.os ?? null,\n language: e.language ?? null,\n timezone: e.timezone ?? null,\n screen_width: e.screen?.width ?? null,\n screen_height: e.screen?.height ?? null,\n utm_source: e.utm?.source ?? null,\n utm_medium: e.utm?.medium ?? null,\n utm_campaign: e.utm?.campaign ?? null,\n utm_term: e.utm?.term ?? null,\n utm_content: e.utm?.content ?? null,\n ip: e.ip ?? null,\n created_at: new Date(),\n }));\n\n await this.collection.insertMany(docs);\n }\n\n async query(q: QueryParams): Promise<QueryResult> {\n const { dateRange, period } = resolvePeriod(q);\n const siteId = q.siteId;\n const limit = q.limit ?? 10;\n\n const baseMatch = {\n site_id: siteId,\n timestamp: { $gte: new Date(dateRange.from), $lte: new Date(dateRange.to) },\n };\n const filterMatch = buildFilterMatch(q.filters);\n\n let data: QueryDataPoint[] = [];\n let total = 0;\n\n switch (q.metric) {\n case 'pageviews': {\n const [result] = await this.collection.aggregate<{ count: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'pageview' } },\n { $count: 'count' },\n ]).toArray();\n total = result?.count ?? 0;\n data = [{ key: 'pageviews', value: total }];\n break;\n }\n\n case 'visitors': {\n const [result] = await this.collection.aggregate<{ count: number }>([\n { $match: { ...baseMatch, ...filterMatch } },\n { $group: { _id: '$visitor_id' } },\n { $count: 'count' },\n ]).toArray();\n total = result?.count ?? 0;\n data = [{ key: 'visitors', value: total }];\n break;\n }\n\n case 'sessions': {\n const [result] = await this.collection.aggregate<{ count: number }>([\n { $match: { ...baseMatch, ...filterMatch } },\n { $group: { _id: '$session_id' } },\n { $count: 'count' },\n ]).toArray();\n total = result?.count ?? 0;\n data = [{ key: 'sessions', value: total }];\n break;\n }\n\n case 'events': {\n const [result] = await this.collection.aggregate<{ count: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'event' } },\n { $count: 'count' },\n ]).toArray();\n total = result?.count ?? 0;\n data = [{ key: 'events', value: total }];\n break;\n }\n case 'conversions': {\n const conversionEvents = q.conversionEvents ?? [];\n if (conversionEvents.length === 0) {\n total = 0;\n data = [{ key: 'conversions', value: 0 }];\n break;\n }\n const [result] = await this.collection.aggregate<{ count: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'event', event_name: { $in: conversionEvents } } },\n { $count: 'count' },\n ]).toArray();\n total = result?.count ?? 0;\n data = [{ key: 'conversions', value: total }];\n break;\n }\n\n case 'top_pages': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'pageview', url: { $ne: null } } },\n { $group: { _id: '$url', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_referrers': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'pageview', referrer: { $nin: [null, ''] } } },\n { $group: { _id: '$referrer', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_countries': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, country: { $ne: null } } },\n { $group: { _id: '$country', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_cities': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, city: { $ne: null } } },\n { $group: { _id: '$city', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_events': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'event', event_name: { $ne: null } } },\n { $group: { _id: '$event_name', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_conversions': {\n const conversionEvents = q.conversionEvents ?? [];\n if (conversionEvents.length === 0) {\n total = 0;\n data = [];\n break;\n }\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'event', event_name: { $in: conversionEvents } } },\n { $group: { _id: '$event_name', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_exit_pages': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'pageview', url: { $ne: null } } },\n { $sort: { timestamp: 1 } },\n { $group: { _id: '$session_id', url: { $last: '$url' } } },\n { $group: { _id: '$url', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_transitions': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'pageview', url: { $ne: null } } },\n {\n $setWindowFields: {\n partitionBy: '$session_id',\n sortBy: { timestamp: 1 },\n output: {\n prev_url: { $shift: { output: '$url', by: -1 } },\n },\n },\n },\n { $match: { prev_url: { $ne: null } } },\n { $group: { _id: { $concat: ['$prev_url', ' → ', '$url'] }, value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_scroll_pages': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, type: 'event', event_subtype: 'scroll_depth', page_path: { $ne: null } } },\n { $group: { _id: '$page_path', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_button_clicks': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n {\n $match: {\n ...baseMatch,\n ...filterMatch,\n type: 'event',\n event_subtype: 'button_click',\n $or: [{ element_text: { $ne: null } }, { element_selector: { $ne: null } }],\n },\n },\n { $group: { _id: { $ifNull: ['$element_text', '$element_selector'] }, value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n case 'top_link_targets': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n {\n $match: {\n ...baseMatch,\n ...filterMatch,\n type: 'event',\n event_subtype: { $in: ['link_click', 'outbound_click'] },\n target_url_path: { $ne: null },\n },\n },\n { $group: { _id: '$target_url_path', value: { $sum: 1 } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_devices': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, device_type: { $ne: null } } },\n { $group: { _id: '$device_type', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_browsers': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, browser: { $ne: null } } },\n { $group: { _id: '$browser', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_os': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, os: { $ne: null } } },\n { $group: { _id: '$os', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_utm_sources': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, utm_source: { $nin: [null, ''] } } },\n { $group: { _id: '$utm_source', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_utm_mediums': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, utm_medium: { $nin: [null, ''] } } },\n { $group: { _id: '$utm_medium', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n\n case 'top_utm_campaigns': {\n const rows = await this.collection.aggregate<{ _id: string; value: number }>([\n { $match: { ...baseMatch, ...filterMatch, utm_campaign: { $nin: [null, ''] } } },\n { $group: { _id: '$utm_campaign', value: { $addToSet: '$visitor_id' } } },\n { $project: { _id: 1, value: { $size: '$value' } } },\n { $sort: { value: -1 } },\n { $limit: limit },\n ]).toArray();\n data = rows.map((r) => ({ key: r._id, value: r.value }));\n total = data.reduce((sum, d) => sum + d.value, 0);\n break;\n }\n }\n\n const result: QueryResult = { metric: q.metric, period, data, total };\n\n // Comparison with previous period\n if (q.compare && ['pageviews', 'visitors', 'sessions', 'events', 'conversions'].includes(q.metric)) {\n const prevRange = previousPeriodRange(dateRange);\n const prevResult = await this.query({\n ...q,\n compare: false,\n period: 'custom',\n dateFrom: prevRange.from,\n dateTo: prevRange.to,\n });\n result.previousTotal = prevResult.total;\n if (prevResult.total > 0) {\n result.changePercent = Math.round(((total - prevResult.total) / prevResult.total) * 1000) / 10;\n } else if (total > 0) {\n result.changePercent = 100;\n } else {\n result.changePercent = 0;\n }\n }\n\n return result;\n }\n\n // ─── Time Series ──────────────────────────────────────\n\n async queryTimeSeries(params: TimeSeriesParams): Promise<TimeSeriesResult> {\n const { dateRange, period } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n\n const granularity = params.granularity ?? autoGranularity(period);\n\n const baseMatch: Record<string, unknown> = {\n site_id: params.siteId,\n timestamp: { $gte: new Date(dateRange.from), $lte: new Date(dateRange.to) },\n };\n const filterMatch = buildFilterMatch(params.filters);\n\n if (params.metric === 'pageviews') {\n baseMatch.type = 'pageview';\n }\n if (params.metric === 'events') {\n baseMatch.type = 'event';\n }\n if (params.metric === 'conversions') {\n baseMatch.type = 'event';\n const conversionEvents = params.conversionEvents ?? [];\n if (conversionEvents.length === 0) {\n const data = fillBuckets(\n new Date(dateRange.from),\n new Date(dateRange.to),\n granularity,\n granularityToDateFormat(granularity),\n [],\n );\n return { metric: params.metric, granularity, data };\n }\n baseMatch.event_name = { $in: conversionEvents };\n }\n\n const dateFormat = granularityToDateFormat(granularity);\n\n let pipeline: object[];\n\n if (params.metric === 'visitors' || params.metric === 'sessions') {\n const groupField = params.metric === 'visitors' ? '$visitor_id' : '$session_id';\n pipeline = [\n { $match: { ...baseMatch, ...filterMatch } },\n {\n $group: {\n _id: {\n bucket: { $dateToString: { format: dateFormat, date: '$timestamp' } },\n entity: groupField,\n },\n },\n },\n {\n $group: {\n _id: '$_id.bucket',\n value: { $sum: 1 },\n },\n },\n { $sort: { _id: 1 } },\n ];\n } else {\n pipeline = [\n { $match: { ...baseMatch, ...filterMatch } },\n {\n $group: {\n _id: { $dateToString: { format: dateFormat, date: '$timestamp' } },\n value: { $sum: 1 },\n },\n },\n { $sort: { _id: 1 } },\n ];\n }\n\n const rows = await this.collection.aggregate<{ _id: string; value: number }>(pipeline).toArray();\n\n const data = fillBuckets(\n new Date(dateRange.from),\n new Date(dateRange.to),\n granularity,\n dateFormat,\n rows,\n );\n\n return { metric: params.metric, granularity, data };\n }\n\n // ─── Retention ──────────────────────────────────────\n\n async queryRetention(params: RetentionParams): Promise<RetentionResult> {\n const weeks = params.weeks ?? 8;\n const now = new Date();\n const startDate = new Date(now.getTime() - weeks * 7 * 24 * 60 * 60 * 1000);\n\n const pipeline: object[] = [\n {\n $match: {\n site_id: params.siteId,\n timestamp: { $gte: startDate },\n },\n },\n {\n $group: {\n _id: '$visitor_id',\n firstEvent: { $min: '$timestamp' },\n eventWeeks: {\n $addToSet: {\n $dateToString: { format: '%G-W%V', date: '$timestamp' },\n },\n },\n },\n },\n ];\n\n const visitors = await this.collection.aggregate<{\n _id: string;\n firstEvent: Date;\n eventWeeks: string[];\n }>(pipeline).toArray();\n\n const cohortMap = new Map<string, { visitors: Set<string>; weekSets: Map<string, Set<string>> }>();\n\n for (const v of visitors) {\n const cohortWeek = getISOWeek(v.firstEvent);\n if (!cohortMap.has(cohortWeek)) {\n cohortMap.set(cohortWeek, { visitors: new Set(), weekSets: new Map() });\n }\n const cohort = cohortMap.get(cohortWeek)!;\n cohort.visitors.add(v._id);\n\n for (const w of v.eventWeeks) {\n if (!cohort.weekSets.has(w)) {\n cohort.weekSets.set(w, new Set());\n }\n cohort.weekSets.get(w)!.add(v._id);\n }\n }\n\n const sortedWeeks = Array.from(cohortMap.keys()).sort();\n const cohorts: RetentionCohort[] = sortedWeeks.map((week) => {\n const cohort = cohortMap.get(week)!;\n const size = cohort.visitors.size;\n\n const retention: number[] = [];\n const weekIndex = sortedWeeks.indexOf(week);\n for (let i = 0; i < weeks && (weekIndex + i) < sortedWeeks.length; i++) {\n const targetWeek = sortedWeeks[weekIndex + i];\n const returnedCount = cohort.weekSets.get(targetWeek)?.size ?? 0;\n retention.push(size > 0 ? Math.round((returnedCount / size) * 1000) / 10 : 0);\n }\n\n return { week, size, retention };\n });\n\n return { cohorts };\n }\n\n // ─── Event Listing ──────────────────────────────────────\n\n async listEvents(params: EventListParams): Promise<EventListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const match: Record<string, unknown> = { site_id: params.siteId };\n\n if (params.type) match.type = params.type;\n if (params.eventName) {\n match.event_name = params.eventName;\n } else if (params.eventNames && params.eventNames.length > 0) {\n match.event_name = { $in: params.eventNames };\n }\n if (params.eventSource) match.event_source = params.eventSource;\n if (params.visitorId) match.visitor_id = params.visitorId;\n if (params.userId) match.user_id = params.userId;\n\n if (params.period || params.dateFrom) {\n const { dateRange } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n match.timestamp = { $gte: new Date(dateRange.from), $lte: new Date(dateRange.to) };\n }\n\n const [events, countResult] = await Promise.all([\n this.collection\n .find(match)\n .sort({ timestamp: -1 })\n .skip(offset)\n .limit(limit)\n .toArray(),\n this.collection.countDocuments(match),\n ]);\n\n return {\n events: events.map((e) => this.toEventListItem(e)),\n total: countResult,\n limit,\n offset,\n };\n }\n\n // ─── Identity Mapping ──────────────────────────────────────\n\n async upsertIdentity(siteId: string, visitorId: string, userId: string): Promise<void> {\n await this.identityMap.updateOne(\n { site_id: siteId, visitor_id: visitorId },\n {\n $set: { user_id: userId, identified_at: new Date() },\n $setOnInsert: { site_id: siteId, visitor_id: visitorId, created_at: new Date() },\n },\n { upsert: true },\n );\n }\n\n async getVisitorIdsForUser(siteId: string, userId: string): Promise<string[]> {\n const docs = await this.identityMap.find({ site_id: siteId, user_id: userId }).toArray();\n return docs.map((d) => d.visitor_id);\n }\n\n async getUserIdForVisitor(siteId: string, visitorId: string): Promise<string | null> {\n const doc = await this.identityMap.findOne({ site_id: siteId, visitor_id: visitorId });\n return doc?.user_id ?? null;\n }\n\n // ─── User Listing ──────────────────────────────────────\n\n async listUsers(params: UserListParams): Promise<UserListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const match: Record<string, unknown> = { site_id: params.siteId };\n\n const pipeline: object[] = [\n { $match: match },\n // Join with identity map to resolve visitor → user\n {\n $lookup: {\n from: IDENTITY_MAP_COLLECTION,\n let: { vid: '$visitor_id', sid: '$site_id' },\n pipeline: [\n { $match: { $expr: { $and: [{ $eq: ['$visitor_id', '$$vid'] }, { $eq: ['$site_id', '$$sid'] }] } } },\n ],\n as: '_identity',\n },\n },\n {\n $addFields: {\n _resolved_id: {\n $ifNull: [{ $arrayElemAt: ['$_identity.user_id', 0] }, '$visitor_id'],\n },\n _resolved_user_id: {\n $arrayElemAt: ['$_identity.user_id', 0],\n },\n },\n },\n ];\n\n // Search filter after $lookup so it can match identity_map userId\n if (params.search) {\n pipeline.push({\n $match: {\n $or: [\n { visitor_id: { $regex: params.search, $options: 'i' } },\n { user_id: { $regex: params.search, $options: 'i' } },\n { _resolved_user_id: { $regex: params.search, $options: 'i' } },\n ],\n },\n });\n }\n\n pipeline.push(\n { $sort: { timestamp: 1 } },\n {\n $group: {\n _id: '$_resolved_id',\n visitorIds: { $addToSet: '$visitor_id' },\n userId: { $last: { $ifNull: ['$_resolved_user_id', '$user_id'] } },\n traits: { $last: '$traits' },\n firstSeen: { $min: '$timestamp' },\n lastSeen: { $max: '$timestamp' },\n totalEvents: { $sum: 1 },\n totalPageviews: { $sum: { $cond: [{ $eq: ['$type', 'pageview'] }, 1, 0] } },\n sessions: { $addToSet: '$session_id' },\n lastUrl: { $last: '$url' },\n referrer: { $last: '$referrer' },\n device_type: { $last: '$device_type' },\n browser: { $last: '$browser' },\n os: { $last: '$os' },\n country: { $last: '$country' },\n city: { $last: '$city' },\n region: { $last: '$region' },\n language: { $last: '$language' },\n timezone: { $last: '$timezone' },\n screen_width: { $last: '$screen_width' },\n screen_height: { $last: '$screen_height' },\n utm_source: { $last: '$utm_source' },\n utm_medium: { $last: '$utm_medium' },\n utm_campaign: { $last: '$utm_campaign' },\n utm_term: { $last: '$utm_term' },\n utm_content: { $last: '$utm_content' },\n },\n },\n { $sort: { lastSeen: -1 } },\n {\n $facet: {\n data: [{ $skip: offset }, { $limit: limit }],\n count: [{ $count: 'total' }],\n },\n },\n );\n\n const [result] = await this.collection.aggregate<{\n data: Array<{\n _id: string;\n visitorIds: string[];\n userId: string | null;\n traits: Record<string, unknown> | null;\n firstSeen: Date;\n lastSeen: Date;\n totalEvents: number;\n totalPageviews: number;\n sessions: string[];\n lastUrl: string | null;\n referrer: string | null;\n device_type: string | null;\n browser: string | null;\n os: string | null;\n country: string | null;\n city: string | null;\n region: string | null;\n language: string | null;\n timezone: string | null;\n screen_width: number | null;\n screen_height: number | null;\n utm_source: string | null;\n utm_medium: string | null;\n utm_campaign: string | null;\n utm_term: string | null;\n utm_content: string | null;\n }>;\n count: Array<{ total: number }>;\n }>(pipeline).toArray();\n\n const users: UserDetail[] = (result?.data ?? []).map((u) => ({\n visitorId: u.visitorIds[0] ?? u._id,\n visitorIds: u.visitorIds.length > 1 ? u.visitorIds : undefined,\n userId: u.userId ?? undefined,\n traits: u.traits ?? undefined,\n firstSeen: u.firstSeen.toISOString(),\n lastSeen: u.lastSeen.toISOString(),\n totalEvents: u.totalEvents,\n totalPageviews: u.totalPageviews,\n totalSessions: u.sessions.length,\n lastUrl: u.lastUrl ?? undefined,\n referrer: u.referrer ?? undefined,\n device: u.device_type ? { type: u.device_type, browser: u.browser ?? '', os: u.os ?? '' } : undefined,\n geo: u.country ? { country: u.country, city: u.city ?? undefined, region: u.region ?? undefined } : undefined,\n language: u.language ?? undefined,\n timezone: u.timezone ?? undefined,\n screen: (u.screen_width || u.screen_height) ? { width: u.screen_width ?? 0, height: u.screen_height ?? 0 } : undefined,\n utm: u.utm_source ? {\n source: u.utm_source ?? undefined,\n medium: u.utm_medium ?? undefined,\n campaign: u.utm_campaign ?? undefined,\n term: u.utm_term ?? undefined,\n content: u.utm_content ?? undefined,\n } : undefined,\n }));\n\n return {\n users,\n total: result?.count?.[0]?.total ?? 0,\n limit,\n offset,\n };\n }\n\n async getUserDetail(siteId: string, identifier: string): Promise<UserDetail | null> {\n // Try as userId first — find all linked visitorIds\n const visitorIds = await this.getVisitorIdsForUser(siteId, identifier);\n if (visitorIds.length > 0) {\n return this.getMergedUserDetail(siteId, identifier, visitorIds);\n }\n // Try as visitorId — resolve to userId\n const userId = await this.getUserIdForVisitor(siteId, identifier);\n if (userId) {\n const allVisitorIds = await this.getVisitorIdsForUser(siteId, userId);\n return this.getMergedUserDetail(siteId, userId, allVisitorIds);\n }\n // Pure anonymous — single visitorId\n return this.getMergedUserDetail(siteId, undefined, [identifier]);\n }\n\n async getUserEvents(siteId: string, identifier: string, params: EventListParams): Promise<EventListResult> {\n // Resolve all visitorIds for this identifier\n let visitorIds = await this.getVisitorIdsForUser(siteId, identifier);\n if (visitorIds.length === 0) {\n const userId = await this.getUserIdForVisitor(siteId, identifier);\n if (userId) {\n visitorIds = await this.getVisitorIdsForUser(siteId, userId);\n }\n }\n if (visitorIds.length === 0) {\n visitorIds = [identifier];\n }\n return this.listEventsForVisitorIds(siteId, visitorIds, params);\n }\n\n private async getMergedUserDetail(siteId: string, userId: string | undefined, visitorIds: string[]): Promise<UserDetail | null> {\n const pipeline: object[] = [\n { $match: { site_id: siteId, visitor_id: { $in: visitorIds } } },\n { $sort: { timestamp: 1 } },\n {\n $group: {\n _id: null,\n visitorIds: { $addToSet: '$visitor_id' },\n traits: { $last: '$traits' },\n firstSeen: { $min: '$timestamp' },\n lastSeen: { $max: '$timestamp' },\n totalEvents: { $sum: 1 },\n totalPageviews: { $sum: { $cond: [{ $eq: ['$type', 'pageview'] }, 1, 0] } },\n sessions: { $addToSet: '$session_id' },\n lastUrl: { $last: '$url' },\n referrer: { $last: '$referrer' },\n device_type: { $last: '$device_type' },\n browser: { $last: '$browser' },\n os: { $last: '$os' },\n country: { $last: '$country' },\n city: { $last: '$city' },\n region: { $last: '$region' },\n language: { $last: '$language' },\n timezone: { $last: '$timezone' },\n screen_width: { $last: '$screen_width' },\n screen_height: { $last: '$screen_height' },\n utm_source: { $last: '$utm_source' },\n utm_medium: { $last: '$utm_medium' },\n utm_campaign: { $last: '$utm_campaign' },\n utm_term: { $last: '$utm_term' },\n utm_content: { $last: '$utm_content' },\n },\n },\n ];\n\n const [row] = await this.collection.aggregate<{\n visitorIds: string[];\n traits: Record<string, unknown> | null;\n firstSeen: Date;\n lastSeen: Date;\n totalEvents: number;\n totalPageviews: number;\n sessions: string[];\n lastUrl: string | null;\n referrer: string | null;\n device_type: string | null;\n browser: string | null;\n os: string | null;\n country: string | null;\n city: string | null;\n region: string | null;\n language: string | null;\n timezone: string | null;\n screen_width: number | null;\n screen_height: number | null;\n utm_source: string | null;\n utm_medium: string | null;\n utm_campaign: string | null;\n utm_term: string | null;\n utm_content: string | null;\n }>(pipeline).toArray();\n\n if (!row) return null;\n\n return {\n visitorId: visitorIds[0],\n visitorIds: row.visitorIds.length > 1 ? row.visitorIds : undefined,\n userId: userId ?? undefined,\n traits: row.traits ?? undefined,\n firstSeen: row.firstSeen.toISOString(),\n lastSeen: row.lastSeen.toISOString(),\n totalEvents: row.totalEvents,\n totalPageviews: row.totalPageviews,\n totalSessions: row.sessions.length,\n lastUrl: row.lastUrl ?? undefined,\n referrer: row.referrer ?? undefined,\n device: row.device_type ? { type: row.device_type, browser: row.browser ?? '', os: row.os ?? '' } : undefined,\n geo: row.country ? { country: row.country, city: row.city ?? undefined, region: row.region ?? undefined } : undefined,\n language: row.language ?? undefined,\n timezone: row.timezone ?? undefined,\n screen: (row.screen_width || row.screen_height) ? { width: row.screen_width ?? 0, height: row.screen_height ?? 0 } : undefined,\n utm: row.utm_source ? {\n source: row.utm_source ?? undefined,\n medium: row.utm_medium ?? undefined,\n campaign: row.utm_campaign ?? undefined,\n term: row.utm_term ?? undefined,\n content: row.utm_content ?? undefined,\n } : undefined,\n };\n }\n\n private async listEventsForVisitorIds(siteId: string, visitorIds: string[], params: EventListParams): Promise<EventListResult> {\n const limit = Math.min(params.limit ?? 50, 200);\n const offset = params.offset ?? 0;\n\n const match: Record<string, unknown> = {\n site_id: siteId,\n visitor_id: { $in: visitorIds },\n };\n\n if (params.type) match.type = params.type;\n if (params.eventName) {\n match.event_name = params.eventName;\n } else if (params.eventNames && params.eventNames.length > 0) {\n match.event_name = { $in: params.eventNames };\n }\n if (params.eventSource) match.event_source = params.eventSource;\n\n if (params.period || params.dateFrom) {\n const { dateRange } = resolvePeriod({\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n });\n match.timestamp = { $gte: new Date(dateRange.from), $lte: new Date(dateRange.to) };\n }\n\n const [events, countResult] = await Promise.all([\n this.collection\n .find(match)\n .sort({ timestamp: -1 })\n .skip(offset)\n .limit(limit)\n .toArray(),\n this.collection.countDocuments(match),\n ]);\n\n return {\n events: events.map((e) => this.toEventListItem(e)),\n total: countResult,\n limit,\n offset,\n };\n }\n\n private toEventListItem(doc: EventDocument): EventListItem {\n return {\n id: (doc as any)._id?.toString?.() ?? '',\n type: doc.type as EventListItem['type'],\n timestamp: doc.timestamp.toISOString(),\n visitorId: doc.visitor_id,\n sessionId: doc.session_id,\n url: doc.url ?? undefined,\n referrer: doc.referrer ?? undefined,\n title: doc.title ?? undefined,\n name: doc.event_name ?? undefined,\n properties: doc.properties ?? undefined,\n eventSource: doc.event_source ? (doc.event_source as EventListItem['eventSource']) : undefined,\n eventSubtype: doc.event_subtype ? (doc.event_subtype as EventListItem['eventSubtype']) : undefined,\n pagePath: doc.page_path ?? undefined,\n targetUrlPath: doc.target_url_path ?? undefined,\n elementSelector: doc.element_selector ?? undefined,\n elementText: doc.element_text ?? undefined,\n scrollDepthPct: doc.scroll_depth_pct ?? undefined,\n userId: doc.user_id ?? undefined,\n traits: doc.traits ?? undefined,\n geo: doc.country ? { country: doc.country, city: doc.city ?? undefined, region: doc.region ?? undefined } : undefined,\n device: doc.device_type ? { type: doc.device_type, browser: doc.browser ?? '', os: doc.os ?? '' } : undefined,\n language: doc.language ?? undefined,\n utm: doc.utm_source ? {\n source: doc.utm_source ?? undefined,\n medium: doc.utm_medium ?? undefined,\n campaign: doc.utm_campaign ?? undefined,\n term: doc.utm_term ?? undefined,\n content: doc.utm_content ?? undefined,\n } : undefined,\n };\n }\n\n // ─── Site Management ──────────────────────────────────────\n\n async createSite(data: CreateSiteRequest): Promise<Site> {\n const now = new Date();\n const doc: SiteDocument = {\n site_id: generateSiteId(),\n secret_key: generateSecretKey(),\n name: data.name,\n domain: data.domain ?? null,\n allowed_origins: data.allowedOrigins ?? null,\n conversion_events: data.conversionEvents ?? null,\n created_at: now,\n updated_at: now,\n };\n await this.sites.insertOne(doc);\n return this.toSite(doc);\n }\n\n async getSite(siteId: string): Promise<Site | null> {\n const doc = await this.sites.findOne({ site_id: siteId });\n return doc ? this.toSite(doc) : null;\n }\n\n async getSiteBySecret(secretKey: string): Promise<Site | null> {\n const doc = await this.sites.findOne({ secret_key: secretKey });\n return doc ? this.toSite(doc) : null;\n }\n\n async listSites(): Promise<Site[]> {\n const docs = await this.sites.find({}).sort({ created_at: -1 }).toArray();\n return docs.map((d) => this.toSite(d));\n }\n\n async updateSite(siteId: string, data: UpdateSiteRequest): Promise<Site | null> {\n const updates: Record<string, unknown> = { updated_at: new Date() };\n if (data.name !== undefined) updates.name = data.name;\n if (data.domain !== undefined) updates.domain = data.domain || null;\n if (data.allowedOrigins !== undefined) updates.allowed_origins = data.allowedOrigins.length > 0 ? data.allowedOrigins : null;\n if (data.conversionEvents !== undefined) updates.conversion_events = data.conversionEvents.length > 0 ? data.conversionEvents : null;\n\n const result = await this.sites.findOneAndUpdate(\n { site_id: siteId },\n { $set: updates },\n { returnDocument: 'after' },\n );\n return result ? this.toSite(result) : null;\n }\n\n async deleteSite(siteId: string): Promise<boolean> {\n const result = await this.sites.deleteOne({ site_id: siteId });\n return result.deletedCount > 0;\n }\n\n async regenerateSecret(siteId: string): Promise<Site | null> {\n const result = await this.sites.findOneAndUpdate(\n { site_id: siteId },\n { $set: { secret_key: generateSecretKey(), updated_at: new Date() } },\n { returnDocument: 'after' },\n );\n return result ? this.toSite(result) : null;\n }\n\n async close(): Promise<void> {\n await this.client.close();\n }\n\n // ─── Helpers ─────────────────────────────────────────────\n\n private toSite(doc: SiteDocument): Site {\n return {\n siteId: doc.site_id,\n secretKey: doc.secret_key,\n name: doc.name,\n domain: doc.domain ?? undefined,\n allowedOrigins: doc.allowed_origins ?? undefined,\n conversionEvents: doc.conversion_events ?? undefined,\n createdAt: doc.created_at.toISOString(),\n updatedAt: doc.updated_at.toISOString(),\n };\n }\n}\n","import type { GeoInfo } from '@litemetrics/core';\n\nlet reader: any = null;\n\n// Timezone → country code fallback for localhost/private IPs\nconst TZ_COUNTRY: Record<string, string> = {\n // Americas\n 'America/New_York': 'US', 'America/Chicago': 'US', 'America/Denver': 'US',\n 'America/Los_Angeles': 'US', 'America/Anchorage': 'US', 'Pacific/Honolulu': 'US',\n 'America/Phoenix': 'US', 'America/Detroit': 'US', 'America/Indiana/Indianapolis': 'US',\n 'America/Toronto': 'CA', 'America/Vancouver': 'CA', 'America/Edmonton': 'CA',\n 'America/Winnipeg': 'CA', 'America/Halifax': 'CA', 'America/Montreal': 'CA',\n 'America/Mexico_City': 'MX', 'America/Cancun': 'MX', 'America/Tijuana': 'MX',\n 'America/Sao_Paulo': 'BR', 'America/Fortaleza': 'BR', 'America/Manaus': 'BR',\n 'America/Argentina/Buenos_Aires': 'AR', 'America/Bogota': 'CO',\n 'America/Santiago': 'CL', 'America/Lima': 'PE',\n // Europe\n 'Europe/London': 'GB', 'Europe/Dublin': 'IE', 'Europe/Paris': 'FR',\n 'Europe/Berlin': 'DE', 'Europe/Amsterdam': 'NL', 'Europe/Brussels': 'BE',\n 'Europe/Zurich': 'CH', 'Europe/Vienna': 'AT', 'Europe/Rome': 'IT',\n 'Europe/Madrid': 'ES', 'Europe/Lisbon': 'PT', 'Europe/Warsaw': 'PL',\n 'Europe/Prague': 'CZ', 'Europe/Budapest': 'HU', 'Europe/Bucharest': 'RO',\n 'Europe/Sofia': 'BG', 'Europe/Athens': 'GR', 'Europe/Helsinki': 'FI',\n 'Europe/Stockholm': 'SE', 'Europe/Oslo': 'NO', 'Europe/Copenhagen': 'DK',\n 'Europe/Istanbul': 'TR', 'Europe/Moscow': 'RU', 'Europe/Kiev': 'UA',\n 'Europe/Belgrade': 'RS', 'Europe/Zagreb': 'HR',\n // Asia\n 'Asia/Tokyo': 'JP', 'Asia/Seoul': 'KR', 'Asia/Shanghai': 'CN',\n 'Asia/Hong_Kong': 'HK', 'Asia/Taipei': 'TW', 'Asia/Singapore': 'SG',\n 'Asia/Kolkata': 'IN', 'Asia/Mumbai': 'IN', 'Asia/Karachi': 'PK',\n 'Asia/Dubai': 'AE', 'Asia/Riyadh': 'SA', 'Asia/Tehran': 'IR',\n 'Asia/Baghdad': 'IQ', 'Asia/Bangkok': 'TH', 'Asia/Jakarta': 'ID',\n 'Asia/Manila': 'PH', 'Asia/Ho_Chi_Minh': 'VN', 'Asia/Kuala_Lumpur': 'MY',\n 'Asia/Dhaka': 'BD', 'Asia/Colombo': 'LK', 'Asia/Jerusalem': 'IL',\n // Oceania\n 'Australia/Sydney': 'AU', 'Australia/Melbourne': 'AU', 'Australia/Brisbane': 'AU',\n 'Australia/Perth': 'AU', 'Australia/Adelaide': 'AU',\n 'Pacific/Auckland': 'NZ', 'Pacific/Fiji': 'FJ',\n // Africa\n 'Africa/Cairo': 'EG', 'Africa/Lagos': 'NG', 'Africa/Johannesburg': 'ZA',\n 'Africa/Nairobi': 'KE', 'Africa/Casablanca': 'MA', 'Africa/Algiers': 'DZ',\n 'Africa/Accra': 'GH', 'Africa/Tunis': 'TN',\n};\n\nexport async function initGeoIP(dbPath?: string): Promise<void> {\n try {\n const maxmind = await import('maxmind');\n const path = dbPath || await findGeoLiteDB();\n if (path) {\n reader = await maxmind.open(path);\n }\n } catch {\n // GeoIP not available - will return empty geo info\n }\n}\n\nexport function resolveGeo(ip: string, timezone?: string): GeoInfo {\n if (!ip && !timezone) return {};\n\n // Try MaxMind lookup first\n if (reader && ip) {\n try {\n const cleanIp = ip.replace(/^::ffff:/, '');\n const result = reader.get(cleanIp);\n if (result?.country?.iso_code) {\n return {\n country: result.country.iso_code,\n city: result.city?.names?.en || undefined,\n region: result.subdivisions?.[0]?.names?.en || undefined,\n };\n }\n } catch {\n // fall through to timezone fallback\n }\n }\n\n // Timezone fallback for localhost/private IPs or when MaxMind fails\n if (timezone) {\n const country = TZ_COUNTRY[timezone];\n if (country) {\n return { country };\n }\n }\n\n return {};\n}\n\nasync function findGeoLiteDB(): Promise<string | null> {\n const { existsSync } = await import('fs');\n const { join } = await import('path');\n const { homedir } = await import('os');\n\n const candidates = [\n join(process.cwd(), 'GeoLite2-City.mmdb'),\n join(homedir(), '.litemetrics', 'GeoLite2-City.mmdb'),\n '/usr/share/GeoIP/GeoLite2-City.mmdb',\n '/var/lib/GeoIP/GeoLite2-City.mmdb',\n ];\n\n for (const p of candidates) {\n if (existsSync(p)) return p;\n }\n\n return null;\n}\n","import type { DeviceInfo } from '@litemetrics/core';\nimport { UAParser } from 'ua-parser-js';\n\nconst parser = new UAParser();\n\nexport function parseUserAgent(ua: string): DeviceInfo {\n parser.setUA(ua);\n const result = parser.getResult();\n\n return {\n type: resolveDeviceType(result.device?.type),\n browser: result.browser?.name || 'Unknown',\n os: result.os?.name || 'Unknown',\n };\n}\n\nfunction resolveDeviceType(type?: string): string {\n if (type === 'mobile') return 'mobile';\n if (type === 'tablet') return 'tablet';\n return 'desktop';\n}\n","const BOT_PATTERNS = [\n // Headless browsers\n /HeadlessChrome/i,\n /PhantomJS/i,\n /Selenium/i,\n /Puppeteer/i,\n /Playwright/i,\n\n // Common bots\n /bot\\b/i,\n /spider/i,\n /crawl/i,\n /slurp/i,\n /mediapartners/i,\n /facebookexternalhit/i,\n /Twitterbot/i,\n /LinkedInBot/i,\n /WhatsApp/i,\n /Discordbot/i,\n /TelegramBot/i,\n /Applebot/i,\n /Baiduspider/i,\n /YandexBot/i,\n /DuckDuckBot/i,\n /Sogou/i,\n /Exabot/i,\n /ia_archiver/i,\n\n // HTTP libraries & API tools\n /PostmanRuntime/i,\n /axios/i,\n /node-fetch/i,\n /python-requests/i,\n /Go-http-client/i,\n /Java\\//i,\n /libwww-perl/i,\n /wget/i,\n /curl/i,\n /httpie/i,\n\n // Monitoring / uptime\n /UptimeRobot/i,\n /Pingdom/i,\n /StatusCake/i,\n /Site24x7/i,\n /NewRelic/i,\n /Datadog/i,\n\n // Preview/embed\n /Slackbot/i,\n /Embedly/i,\n /Quora Link Preview/i,\n /redditbot/i,\n /Pinterestbot/i,\n];\n\nexport function isBot(ua: string): boolean {\n if (!ua || ua.length === 0) return true;\n return BOT_PATTERNS.some((re) => re.test(ua));\n}\n","import type {\n CollectorConfig,\n DBAdapter,\n EnrichedEvent,\n ClientEvent,\n CollectPayload,\n QueryParams,\n QueryResult,\n TimeSeriesParams,\n RetentionParams,\n EventListParams,\n EventListResult,\n UserListParams,\n UserListResult,\n UserDetail,\n Site,\n CreateSiteRequest,\n UpdateSiteRequest,\n} from '@litemetrics/core';\nimport { ClickHouseAdapter } from './adapters/clickhouse';\nimport { MongoDBAdapter } from './adapters/mongodb';\nimport { initGeoIP, resolveGeo } from './geoip';\nimport { parseUserAgent } from './useragent';\nimport { isBot } from './botfilter';\n\nexport interface Collector {\n handler(): (req: any, res: any) => void | Promise<void>;\n queryHandler(): (req: any, res: any) => void | Promise<void>;\n eventsHandler(): (req: any, res: any) => void | Promise<void>;\n usersHandler(): (req: any, res: any) => void | Promise<void>;\n sitesHandler(): (req: any, res: any) => void | Promise<void>;\n query(params: QueryParams): Promise<QueryResult>;\n listEvents(params: EventListParams): Promise<EventListResult>;\n listUsers(params: UserListParams): Promise<UserListResult>;\n getUserDetail(siteId: string, identifier: string): Promise<UserDetail | null>;\n getUserEvents(siteId: string, identifier: string, params: EventListParams): Promise<EventListResult>;\n track(siteId: string, name: string, properties?: Record<string, unknown>, options?: { userId?: string; ip?: string }): Promise<void>;\n identify(siteId: string, userId: string, traits?: Record<string, unknown>, options?: { ip?: string }): Promise<void>;\n createSite(data: CreateSiteRequest): Promise<Site>;\n listSites(): Promise<Site[]>;\n getSite(siteId: string): Promise<Site | null>;\n updateSite(siteId: string, data: UpdateSiteRequest): Promise<Site | null>;\n deleteSite(siteId: string): Promise<boolean>;\n regenerateSecret(siteId: string): Promise<Site | null>;\n close(): Promise<void>;\n}\n\nexport async function createCollector(config: CollectorConfig): Promise<Collector> {\n const db = createAdapter(config.db);\n await db.init();\n\n if (config.geoip) {\n const geoipConfig = typeof config.geoip === 'object' ? config.geoip : {};\n await initGeoIP(geoipConfig.dbPath);\n }\n\n // ─── Auth helpers ──────────────────────────────────────\n\n function isAdmin(req: any): boolean {\n if (!config.adminSecret) return false;\n return req.headers?.['x-litemetrics-admin-secret'] === config.adminSecret;\n }\n\n async function isAuthorizedForSite(req: any, siteId: string): Promise<boolean> {\n // Admin can access everything\n if (isAdmin(req)) return true;\n // Check site secret\n const secret = req.headers?.['x-litemetrics-secret'];\n if (!secret) return false;\n const site = await db.getSiteBySecret(secret);\n return site !== null && site.siteId === siteId;\n }\n\n // ─── CORS helper ──────────────────────────────────────\n\n function setCors(req: any, res: any, methods: string, extraHeaders?: string): boolean {\n if (!config.cors) return false;\n const origin = req.headers?.origin;\n const allowed = !config.cors.origins || config.cors.origins.length === 0 || config.cors.origins.includes(origin);\n if (allowed) {\n res.setHeader?.('Access-Control-Allow-Origin', origin || '*');\n res.setHeader?.('Access-Control-Allow-Methods', methods);\n res.setHeader?.('Access-Control-Allow-Credentials', 'true');\n const headers = ['Content-Type', extraHeaders].filter(Boolean).join(', ');\n res.setHeader?.('Access-Control-Allow-Headers', headers);\n }\n if (req.method === 'OPTIONS') {\n res.writeHead?.(204);\n res.end?.();\n return true;\n }\n return false;\n }\n\n // ─── Event helpers ────────────────────────────────────\n\n function enrichEvents(events: ClientEvent[], ip: string, userAgent: string): EnrichedEvent[] {\n const device = parseUserAgent(userAgent);\n return events.map((event) => {\n const geo = resolveGeo(ip, event.timezone);\n return { ...event, ip, geo, device };\n });\n }\n\n // ─── Identity resolution ────────────────────────────────\n // In-memory cache: siteId:visitorId → userId (5 min TTL)\n const identityCache = new Map<string, { userId: string; expires: number }>();\n const IDENTITY_CACHE_TTL = 5 * 60 * 1000;\n\n function getCachedUserId(siteId: string, visitorId: string): string | undefined {\n const key = `${siteId}:${visitorId}`;\n const entry = identityCache.get(key);\n if (!entry) return undefined;\n if (Date.now() > entry.expires) {\n identityCache.delete(key);\n return undefined;\n }\n return entry.userId;\n }\n\n function setCachedUserId(siteId: string, visitorId: string, userId: string): void {\n const key = `${siteId}:${visitorId}`;\n identityCache.set(key, { userId, expires: Date.now() + IDENTITY_CACHE_TTL });\n // Evict old entries if cache grows too large\n if (identityCache.size > 10000) {\n const now = Date.now();\n for (const [k, v] of identityCache) {\n if (now > v.expires) identityCache.delete(k);\n }\n }\n }\n\n async function processIdentity(events: EnrichedEvent[]): Promise<void> {\n for (const event of events) {\n // Skip sentinel visitorId used by server-side programmatic calls\n if (!event.visitorId || event.visitorId === 'server') continue;\n\n if (event.type === 'identify' && event.userId) {\n // Identify event → upsert identity map and cache\n await db.upsertIdentity(event.siteId, event.visitorId, event.userId);\n setCachedUserId(event.siteId, event.visitorId, event.userId);\n } else if (!event.userId) {\n // Non-identify event without userId → try to resolve from cache or DB\n const cached = getCachedUserId(event.siteId, event.visitorId);\n if (cached) {\n event.userId = cached;\n } else {\n const resolved = await db.getUserIdForVisitor(event.siteId, event.visitorId);\n if (resolved) {\n event.userId = resolved;\n setCachedUserId(event.siteId, event.visitorId, resolved);\n }\n }\n } else if (event.userId) {\n // Event already has userId → persist and cache the mapping\n setCachedUserId(event.siteId, event.visitorId, event.userId);\n await db.upsertIdentity(event.siteId, event.visitorId, event.userId);\n }\n }\n }\n\n function extractIp(req: any): string {\n if (config.trustProxy ?? true) {\n const forwarded = req.headers?.['x-forwarded-for'];\n if (forwarded) {\n const first = typeof forwarded === 'string' ? forwarded.split(',')[0] : forwarded[0];\n return first.trim();\n }\n if (req.headers?.['x-real-ip']) return req.headers['x-real-ip'];\n }\n return req.ip || req.socket?.remoteAddress || req.connection?.remoteAddress || '';\n }\n\n // ─── Handlers ─────────────────────────────────────────\n\n function handler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n // Collect endpoint is public — always allow cross-origin\n res.setHeader?.('Access-Control-Allow-Origin', '*');\n res.setHeader?.('Access-Control-Allow-Methods', 'POST, OPTIONS');\n res.setHeader?.('Access-Control-Allow-Headers', 'Content-Type');\n if (req.method === 'OPTIONS') {\n res.writeHead?.(204);\n res.end?.();\n return;\n }\n\n if (req.method !== 'POST') {\n sendJson(res, 405, { ok: false, error: 'Method not allowed' });\n return;\n }\n\n try {\n const body = await parseBody(req);\n const payload = body as CollectPayload;\n\n if (!payload?.events || !Array.isArray(payload.events) || payload.events.length === 0) {\n sendJson(res, 400, { ok: false, error: 'No events provided' });\n return;\n }\n if (payload.events.length > 100) {\n sendJson(res, 400, { ok: false, error: 'Too many events (max 100)' });\n return;\n }\n\n const userAgent = req.headers?.['user-agent'] || '';\n\n // Bot check - silent drop\n if (isBot(userAgent)) {\n sendJson(res, 200, { ok: true });\n return;\n }\n\n const ip = extractIp(req);\n const enriched = enrichEvents(payload.events, ip, userAgent);\n\n // Hostname filtering: check site's allowedOrigins\n const siteId = enriched[0]?.siteId;\n if (siteId) {\n const site = await db.getSite(siteId);\n if (site?.allowedOrigins && site.allowedOrigins.length > 0) {\n const allowed = new Set(site.allowedOrigins.map((h) => h.toLowerCase()));\n const filtered = enriched.filter((event) => {\n if (!event.url) return true; // non-pageview events pass through\n try {\n const hostname = new URL(event.url).hostname.toLowerCase();\n return allowed.has(hostname);\n } catch {\n return true; // malformed URLs pass through\n }\n });\n if (filtered.length === 0) {\n sendJson(res, 200, { ok: true });\n return;\n }\n await processIdentity(filtered);\n await db.insertEvents(filtered);\n sendJson(res, 200, { ok: true });\n return;\n }\n }\n\n await processIdentity(enriched);\n await db.insertEvents(enriched);\n sendJson(res, 200, { ok: true });\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n function queryHandler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n if (setCors(req, res, 'GET, OPTIONS', 'X-Litemetrics-Secret, X-Litemetrics-Admin-Secret')) return;\n\n try {\n const params = extractQueryParams(req);\n\n if (!params.siteId) {\n sendJson(res, 400, { ok: false, error: 'siteId is required' });\n return;\n }\n if (!params.metric) {\n sendJson(res, 400, { ok: false, error: 'metric is required' });\n return;\n }\n\n // Auth check\n const authorized = await isAuthorizedForSite(req, params.siteId);\n if (!authorized) {\n sendJson(res, 401, { ok: false, error: 'Invalid or missing secret key' });\n return;\n }\n\n // Time series query\n if (params.metric === 'timeseries' as any) {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n const tsParams: TimeSeriesParams = {\n siteId: params.siteId,\n metric: (q.tsMetric as TimeSeriesParams['metric']) || 'pageviews',\n period: params.period,\n dateFrom: params.dateFrom,\n dateTo: params.dateTo,\n granularity: q.granularity as TimeSeriesParams['granularity'],\n filters: q.filters ? JSON.parse(q.filters as string) : undefined,\n };\n if (tsParams.metric === 'conversions') {\n const site = await db.getSite(params.siteId);\n tsParams.conversionEvents = site?.conversionEvents ?? [];\n }\n const result = await db.queryTimeSeries(tsParams);\n sendJson(res, 200, result);\n return;\n }\n\n // Retention query\n if (params.metric === 'retention' as any) {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n const retentionParams: RetentionParams = {\n siteId: params.siteId,\n period: params.period,\n weeks: q.weeks ? parseInt(q.weeks as string, 10) : undefined,\n };\n const result = await db.queryRetention(retentionParams);\n sendJson(res, 200, result);\n return;\n }\n\n const isConversionMetric = params.metric === 'conversions' || params.metric === 'top_conversions';\n let result: QueryResult;\n if (isConversionMetric) {\n const site = await db.getSite(params.siteId);\n const conversionEvents = site?.conversionEvents ?? [];\n result = await db.query({ ...params, conversionEvents });\n } else {\n result = await db.query(params);\n }\n sendJson(res, 200, result);\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n function sitesHandler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n if (setCors(req, res, 'GET, POST, PUT, DELETE, OPTIONS', 'X-Litemetrics-Admin-Secret')) return;\n\n // Admin auth required for all site management\n if (!isAdmin(req)) {\n sendJson(res, 401, { ok: false, error: 'Unauthorized - invalid or missing admin secret' });\n return;\n }\n\n try {\n const method = req.method;\n // Extract path - works with Express params or raw URL\n const url = new URL(req.url || '/', 'http://localhost');\n const pathSegments = url.pathname.split('/').filter(Boolean);\n // Find the segment after 'sites' in the path\n const sitesIdx = pathSegments.indexOf('sites');\n const siteId = sitesIdx >= 0 ? pathSegments[sitesIdx + 1] : undefined;\n const action = sitesIdx >= 0 ? pathSegments[sitesIdx + 2] : undefined;\n\n // POST /api/sites/:siteId/regenerate\n if (method === 'POST' && siteId && action === 'regenerate') {\n const site = await db.regenerateSecret(siteId);\n if (!site) { sendJson(res, 404, { ok: false, error: 'Site not found' }); return; }\n sendJson(res, 200, { site });\n return;\n }\n\n // GET /api/sites - list\n if (method === 'GET' && !siteId) {\n const sites = await db.listSites();\n sendJson(res, 200, { sites, total: sites.length });\n return;\n }\n\n // GET /api/sites/:siteId - get one\n if (method === 'GET' && siteId) {\n const site = await db.getSite(siteId);\n if (!site) { sendJson(res, 404, { ok: false, error: 'Site not found' }); return; }\n sendJson(res, 200, { site });\n return;\n }\n\n // POST /api/sites - create\n if (method === 'POST' && !siteId) {\n const body = await parseBody(req) as CreateSiteRequest;\n if (!body.name || typeof body.name !== 'string' || !body.name.trim()) {\n sendJson(res, 400, { ok: false, error: 'Site name is required' });\n return;\n }\n const site = await db.createSite(body);\n sendJson(res, 201, { site });\n return;\n }\n\n // PUT /api/sites/:siteId - update\n if (method === 'PUT' && siteId) {\n const body = await parseBody(req) as UpdateSiteRequest;\n const site = await db.updateSite(siteId, body);\n if (!site) { sendJson(res, 404, { ok: false, error: 'Site not found' }); return; }\n sendJson(res, 200, { site });\n return;\n }\n\n // DELETE /api/sites/:siteId - delete\n if (method === 'DELETE' && siteId) {\n const deleted = await db.deleteSite(siteId);\n if (!deleted) { sendJson(res, 404, { ok: false, error: 'Site not found' }); return; }\n sendJson(res, 200, { ok: true });\n return;\n }\n\n sendJson(res, 404, { ok: false, error: 'Not found' });\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n // ─── Events handler ────────────────────────────────────\n\n function eventsHandler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n if (setCors(req, res, 'GET, OPTIONS', 'X-Litemetrics-Secret, X-Litemetrics-Admin-Secret')) return;\n\n if (req.method !== 'GET') {\n sendJson(res, 405, { ok: false, error: 'Method not allowed' });\n return;\n }\n\n try {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n\n if (!q.siteId) {\n sendJson(res, 400, { ok: false, error: 'siteId is required' });\n return;\n }\n\n const authorized = await isAuthorizedForSite(req, q.siteId as string);\n if (!authorized) {\n sendJson(res, 401, { ok: false, error: 'Invalid or missing secret key' });\n return;\n }\n\n const eventNames = typeof q.eventNames === 'string'\n ? q.eventNames.split(',').map((s: string) => s.trim()).filter(Boolean)\n : undefined;\n\n const params: EventListParams = {\n siteId: q.siteId as string,\n type: q.type as EventListParams['type'],\n eventName: q.eventName as string | undefined,\n eventNames,\n eventSource: q.eventSource as EventListParams['eventSource'],\n visitorId: q.visitorId as string | undefined,\n userId: q.userId as string | undefined,\n period: q.period as EventListParams['period'],\n dateFrom: q.dateFrom as string | undefined,\n dateTo: q.dateTo as string | undefined,\n limit: q.limit ? parseInt(q.limit as string, 10) : undefined,\n offset: q.offset ? parseInt(q.offset as string, 10) : undefined,\n };\n\n const result = await db.listEvents(params);\n sendJson(res, 200, result);\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n // ─── Users handler ────────────────────────────────────\n\n function usersHandler(): (req: any, res: any) => void | Promise<void> {\n return async (req: any, res: any) => {\n if (setCors(req, res, 'GET, OPTIONS', 'X-Litemetrics-Secret, X-Litemetrics-Admin-Secret')) return;\n\n if (req.method !== 'GET') {\n sendJson(res, 405, { ok: false, error: 'Method not allowed' });\n return;\n }\n\n try {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n\n if (!q.siteId) {\n sendJson(res, 400, { ok: false, error: 'siteId is required' });\n return;\n }\n\n const authorized = await isAuthorizedForSite(req, q.siteId as string);\n if (!authorized) {\n sendJson(res, 401, { ok: false, error: 'Invalid or missing secret key' });\n return;\n }\n\n // Extract path to check for /api/users/:visitorId or /api/users/:visitorId/events\n const url = new URL(req.url || '/', 'http://localhost');\n const pathSegments = url.pathname.split('/').filter(Boolean);\n const usersIdx = pathSegments.indexOf('users');\n const visitorId = usersIdx >= 0 ? pathSegments[usersIdx + 1] : undefined;\n const action = usersIdx >= 0 ? pathSegments[usersIdx + 2] : undefined;\n\n // GET /api/users/:visitorId/events\n if (visitorId && action === 'events') {\n const eventNames = typeof q.eventNames === 'string'\n ? q.eventNames.split(',').map((s: string) => s.trim()).filter(Boolean)\n : undefined;\n\n const params: EventListParams = {\n siteId: q.siteId as string,\n type: q.type as EventListParams['type'],\n eventName: q.eventName as string | undefined,\n eventNames,\n eventSource: q.eventSource as EventListParams['eventSource'],\n period: q.period as EventListParams['period'],\n dateFrom: q.dateFrom as string | undefined,\n dateTo: q.dateTo as string | undefined,\n limit: q.limit ? parseInt(q.limit as string, 10) : undefined,\n offset: q.offset ? parseInt(q.offset as string, 10) : undefined,\n };\n const result = await db.getUserEvents(q.siteId as string, decodeURIComponent(visitorId), params);\n sendJson(res, 200, result);\n return;\n }\n\n // GET /api/users/:visitorId\n if (visitorId) {\n const user = await db.getUserDetail(q.siteId as string, decodeURIComponent(visitorId));\n if (!user) {\n sendJson(res, 404, { ok: false, error: 'User not found' });\n return;\n }\n sendJson(res, 200, { user });\n return;\n }\n\n // GET /api/users - list\n const params: UserListParams = {\n siteId: q.siteId as string,\n search: q.search as string | undefined,\n limit: q.limit ? parseInt(q.limit as string, 10) : undefined,\n offset: q.offset ? parseInt(q.offset as string, 10) : undefined,\n };\n const result = await db.listUsers(params);\n sendJson(res, 200, result);\n } catch (err) {\n sendJson(res, 500, { ok: false, error: err instanceof Error ? err.message : 'Internal error' });\n }\n };\n }\n\n // ─── Return collector ─────────────────────────────────\n\n return {\n handler,\n queryHandler,\n eventsHandler,\n usersHandler,\n sitesHandler,\n\n async query(params: QueryParams): Promise<QueryResult> {\n return db.query(params);\n },\n\n async listEvents(params: EventListParams): Promise<EventListResult> {\n return db.listEvents(params);\n },\n\n async listUsers(params: UserListParams): Promise<UserListResult> {\n return db.listUsers(params);\n },\n\n async getUserDetail(siteId: string, identifier: string): Promise<UserDetail | null> {\n return db.getUserDetail(siteId, identifier);\n },\n\n async getUserEvents(siteId: string, identifier: string, params: EventListParams): Promise<EventListResult> {\n return db.getUserEvents(siteId, identifier, params);\n },\n\n async track(siteId, name, properties, options) {\n const event: EnrichedEvent = {\n type: 'event', siteId, timestamp: Date.now(), sessionId: 'server', visitorId: 'server',\n name, properties, userId: options?.userId, ip: options?.ip,\n geo: options?.ip ? resolveGeo(options.ip) : undefined,\n };\n await db.insertEvents([event]);\n },\n\n async identify(siteId, userId, traits, options) {\n const event: EnrichedEvent = {\n type: 'identify', siteId, timestamp: Date.now(), sessionId: 'server', visitorId: 'server',\n userId, traits, ip: options?.ip,\n geo: options?.ip ? resolveGeo(options.ip) : undefined,\n };\n await db.insertEvents([event]);\n },\n\n // Programmatic site management\n createSite: (data) => db.createSite(data),\n listSites: () => db.listSites(),\n getSite: (siteId) => db.getSite(siteId),\n updateSite: (siteId, data) => db.updateSite(siteId, data),\n deleteSite: (siteId) => db.deleteSite(siteId),\n regenerateSecret: (siteId) => db.regenerateSecret(siteId),\n\n async close() {\n await db.close();\n },\n };\n}\n\nfunction createAdapter(config: CollectorConfig['db']): DBAdapter {\n const adapter = config.adapter ?? 'clickhouse';\n switch (adapter) {\n case 'clickhouse':\n return new ClickHouseAdapter(config.url);\n case 'mongodb':\n return new MongoDBAdapter(config.url);\n default:\n throw new Error(`Unknown DB adapter: ${adapter}. Supported: clickhouse, mongodb`);\n }\n}\n\nasync function parseBody(req: any): Promise<unknown> {\n // Already parsed by middleware (e.g. express.json())\n if (req.body && typeof req.body === 'object') return req.body;\n // Raw string body (e.g. express.text() or text/plain content-type)\n if (typeof req.body === 'string') return JSON.parse(req.body);\n // Manual stream parsing\n return new Promise((resolve, reject) => {\n let data = '';\n req.on('data', (chunk: Buffer) => { data += chunk.toString(); });\n req.on('end', () => {\n try { resolve(JSON.parse(data)); } catch { reject(new Error('Invalid JSON')); }\n });\n req.on('error', reject);\n });\n}\n\nfunction extractQueryParams(req: any): QueryParams {\n const q = req.query ?? Object.fromEntries(new URL(req.url, 'http://localhost').searchParams);\n return {\n siteId: q.siteId as string,\n metric: q.metric as QueryParams['metric'],\n period: q.period as QueryParams['period'],\n dateFrom: q.dateFrom as string | undefined,\n dateTo: q.dateTo as string | undefined,\n limit: q.limit ? parseInt(q.limit as string, 10) : undefined,\n filters: q.filters ? JSON.parse(q.filters as string) : undefined,\n compare: q.compare === 'true' || q.compare === '1',\n };\n}\n\nfunction sendJson(res: any, status: number, body: unknown): void {\n if (typeof res.status === 'function' && typeof res.json === 'function') {\n res.status(status).json(body);\n return;\n }\n res.writeHead(status, { 'Content-Type': 'application/json' });\n res.end(JSON.stringify(body));\n}\n"],"mappings":"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","names":["result","data","result","params"]}
|