@litemetrics/node 0.1.0

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.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/adapters/clickhouse.ts","../src/adapters/utils.ts","../src/adapters/mongodb.ts","../src/geoip.ts","../src/useragent.ts","../src/collector.ts"],"sourcesContent":["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\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: new Date(e.timestamp).toISOString(),\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: dateRange.from,\n to: 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: dateRange.from,\n to: 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: startDate.toISOString(),\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 = dateRange.from;\n queryParams.to = 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().toISOString();\n const site: Site = {\n siteId: generateSiteId(),\n secretKey: generateSecretKey(),\n name: data.name,\n domain: data.domain,\n allowedOrigins: data.allowedOrigins,\n createdAt: now,\n updatedAt: now,\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: now,\n updated_at: now,\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().toISOString();\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: String(current.created_at),\n updated_at: now,\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: now,\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 now = new Date().toISOString();\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: String(current.created_at),\n updated_at: now,\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().toISOString();\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: String(current.created_at),\n updated_at: now,\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: now,\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\nfunction isPrivateIp(ip: string): boolean {\n return ip === '::1' || ip === '127.0.0.1' || ip === 'localhost'\n || ip.startsWith('10.') || ip.startsWith('192.168.')\n || /^172\\.(1[6-9]|2\\d|3[01])\\./.test(ip);\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","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';\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 ip = extractIp(req);\n const userAgent = req.headers?.['user-agent'] || '';\n const enriched = enrichEvents(payload.events, ip, userAgent);\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"]}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@litemetrics/node",
3
+ "version": "0.1.0",
4
+ "description": "Node.js analytics collector with DB adapters and query API",
5
+ "license": "MIT",
6
+ "author": "Metehan Kurucu",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/metehankurucu/litemetrics",
10
+ "directory": "packages/node"
11
+ },
12
+ "keywords": ["analytics", "tracking", "litemetrics", "node", "collector", "mongodb"],
13
+ "type": "module",
14
+ "main": "./dist/index.cjs",
15
+ "module": "./dist/index.js",
16
+ "types": "./dist/index.d.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "import": "./dist/index.js",
21
+ "require": "./dist/index.cjs"
22
+ }
23
+ },
24
+ "files": ["dist"],
25
+ "publishConfig": {
26
+ "access": "public"
27
+ },
28
+ "scripts": {
29
+ "build": "tsup",
30
+ "dev": "tsup --watch",
31
+ "typecheck": "tsc --noEmit",
32
+ "clean": "rm -rf dist"
33
+ },
34
+ "dependencies": {
35
+ "@clickhouse/client": "^1.17.0",
36
+ "@litemetrics/core": "0.1.0",
37
+ "maxmind": "^4.3",
38
+ "mongodb": "^6",
39
+ "ua-parser-js": "^2"
40
+ },
41
+ "devDependencies": {
42
+ "@types/ua-parser-js": "^0.7",
43
+ "tsup": "^8",
44
+ "typescript": "^5.7"
45
+ }
46
+ }