diff --git a/README.md b/README.md
index 80c3af4..143da28 100644
--- a/README.md
+++ b/README.md
@@ -35,12 +35,12 @@ Server logs have the data, but turning them into analytics is a pipeline project
## What you get
```ts
-import { trackDocView, posthogAnalytics } from '@apideck/agent-analytics'
+import { trackVisit, posthogAnalytics } from '@apideck/agent-analytics'
const analytics = posthogAnalytics({ apiKey: process.env.POSTHOG_KEY! })
export function middleware(req: NextRequest) {
- void trackDocView(req, { analytics }) // ← that's the whole thing
+ void trackVisit(req, { analytics }) // ← that's the whole thing
return NextResponse.next()
}
```
@@ -121,11 +121,11 @@ Ships with **PostHog**, **webhook**, and **custom** adapters. BYO analytics.
```ts
// middleware.ts
import {
- trackDocView
+ trackVisit
} from '@apideck/agent-analytics'
export function middleware(req) {
- void trackDocView(req, {
+ void trackVisit(req, {
analytics,
source: 'page-view'
})
@@ -366,14 +366,14 @@ Full middleware example: [`README.md → Markdown mirror helpers`](./README.md#m
Will this slow down my middleware?
-No. `trackDocView` returns a promise you don't await, and the underlying `fetch` uses `keepalive: true` — the browser / runtime guarantees the request completes after your response returns. Your critical path is: `req.headers.get('user-agent')` + a regex test + a `void fetch(...)`. Sub-millisecond.
+No. `trackVisit` returns a promise you don't await, and the underlying `fetch` uses `keepalive: true` — the browser / runtime guarantees the request completes after your response returns. Your critical path is: `req.headers.get('user-agent')` + a regex test + a `void fetch(...)`. Sub-millisecond.
What if my analytics backend is down?
-The adapter call is wrapped in try/catch — `trackDocView` never throws, even if PostHog / your webhook / your custom callback crashes. You lose the event, not the response.
+The adapter call is wrapped in try/catch — `trackVisit` never throws, even if PostHog / your webhook / your custom callback crashes. You lose the event, not the response.
diff --git a/package.json b/package.json
index 2ab7319..29ed055 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@apideck/agent-analytics",
- "version": "0.2.0",
+ "version": "0.3.0",
"description": "Track AI agent and bot traffic to your Next.js / Vercel app — PostHog, webhooks, or any custom analytics backend. Detects Claude, ChatGPT, Perplexity, Google-Extended, and more.",
"keywords": [
"ai",
diff --git a/src/bots.ts b/src/bots.ts
index e4d700b..1ff14d7 100644
--- a/src/bots.ts
+++ b/src/bots.ts
@@ -150,7 +150,7 @@ export interface AgentClassification {
/**
* One-stop classification of a user-agent. Combines {@link isAiBot},
* {@link isHttpClient}, and {@link parseBotName} into a single structured
- * result. Used internally by `trackDocView` to populate event properties;
+ * result. Used internally by `trackVisit` to populate event properties;
* useful in consumer code when you need all signals at once.
*/
export function classifyAgent(userAgent: string | null | undefined): AgentClassification {
diff --git a/src/index.ts b/src/index.ts
index 950709b..7462af7 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,4 +1,4 @@
-export { trackDocView } from './track.js'
+export { trackVisit } from './track.js'
export {
AI_BOT_PATTERN,
HTTP_CLIENT_PATTERN,
@@ -16,5 +16,5 @@ export { customAnalytics } from './adapters/custom.js'
export type {
AnalyticsAdapter,
CaptureEvent,
- TrackDocViewOptions
+ TrackVisitOptions
} from './types.js'
diff --git a/src/track.ts b/src/track.ts
index 6a30e4f..962d01f 100644
--- a/src/track.ts
+++ b/src/track.ts
@@ -1,6 +1,6 @@
import { classifyAgent, isAiBot } from './bots.js'
import { hashId } from './hash.js'
-import type { TrackDocViewOptions } from './types.js'
+import type { TrackVisitOptions } from './types.js'
/**
* Capture an event describing the incoming request. Fire-and-forget: awaits
@@ -10,9 +10,9 @@ import type { TrackDocViewOptions } from './types.js'
* When `onlyBots` is true (the default), skips capture unless the UA matches
* {@link AI_BOT_PATTERN}. Set `onlyBots: false` to track every visit.
*/
-export async function trackDocView(
+export async function trackVisit(
req: Request,
- opts: TrackDocViewOptions
+ opts: TrackVisitOptions
): Promise {
const userAgent = req.headers.get('user-agent') || ''
diff --git a/src/types.ts b/src/types.ts
index 261bf8c..f4a9028 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -9,7 +9,7 @@ export interface AnalyticsAdapter {
capture(event: CaptureEvent): Promise | void
}
-export interface TrackDocViewOptions {
+export interface TrackVisitOptions {
analytics: AnalyticsAdapter
/**
* Label describing how the request arrived (e.g. `'page-view'`, `'md-suffix'`,
diff --git a/test/track.test.ts b/test/track.test.ts
index 0d2b201..77dc262 100644
--- a/test/track.test.ts
+++ b/test/track.test.ts
@@ -1,6 +1,6 @@
import { describe, expect, it, vi } from 'vitest'
import { customAnalytics } from '../src/adapters/custom.js'
-import { trackDocView } from '../src/track.js'
+import { trackVisit } from '../src/track.js'
import type { CaptureEvent } from '../src/types.js'
function makeRequest(
@@ -10,14 +10,14 @@ function makeRequest(
return new Request(url, { headers })
}
-describe('trackDocView', () => {
+describe('trackVisit', () => {
it('captures when the UA is a known AI bot', async () => {
const captured: CaptureEvent[] = []
const analytics = customAnalytics((e) => {
captured.push(e)
})
- await trackDocView(
+ await trackVisit(
makeRequest('https://example.com/docs/intro', {
'user-agent': 'ClaudeBot/1.0',
'x-forwarded-for': '1.2.3.4',
@@ -46,7 +46,7 @@ describe('trackDocView', () => {
it('sets bot_name to Browser for human traffic when onlyBots is false', async () => {
const spy = vi.fn()
- await trackDocView(
+ await trackVisit(
makeRequest('https://example.com/page', {
'user-agent': 'Mozilla/5.0 (Macintosh) Chrome/120'
}),
@@ -61,7 +61,7 @@ describe('trackDocView', () => {
it('sets coding_agent_hint and ua_category for HTTP-library UAs (onlyBots: false)', async () => {
const spy = vi.fn()
- await trackDocView(
+ await trackVisit(
makeRequest('https://example.com/docs/intro', { 'user-agent': 'curl/8.4.0' }),
{ analytics: customAnalytics(spy), onlyBots: false }
)
@@ -76,7 +76,7 @@ describe('trackDocView', () => {
it('skips capture when UA is not a bot and onlyBots is on (default)', async () => {
const spy = vi.fn()
- await trackDocView(
+ await trackVisit(
makeRequest('https://example.com/page', {
'user-agent': 'Mozilla/5.0 (Macintosh) Chrome/120'
}),
@@ -87,7 +87,7 @@ describe('trackDocView', () => {
it('captures every request when onlyBots is false', async () => {
const spy = vi.fn()
- await trackDocView(
+ await trackVisit(
makeRequest('https://example.com/page', {
'user-agent': 'Mozilla/5.0 (Macintosh) Chrome/120'
}),
@@ -100,7 +100,7 @@ describe('trackDocView', () => {
it('honours a custom event name', async () => {
const spy = vi.fn()
- await trackDocView(
+ await trackVisit(
makeRequest('https://example.com/page', { 'user-agent': 'ClaudeBot/1.0' }),
{ analytics: customAnalytics(spy), eventName: 'agent_fetch' }
)
@@ -113,7 +113,7 @@ describe('trackDocView', () => {
throw new Error('downstream offline')
})
await expect(
- trackDocView(
+ trackVisit(
makeRequest('https://example.com/page', { 'user-agent': 'ClaudeBot' }),
{ analytics }
)
@@ -122,7 +122,7 @@ describe('trackDocView', () => {
it('uses the first x-forwarded-for value when multiple are present', async () => {
const spy = vi.fn()
- await trackDocView(
+ await trackVisit(
makeRequest('https://example.com/page', {
'user-agent': 'ClaudeBot',
'x-forwarded-for': '203.0.113.1, 10.0.0.1'
@@ -133,7 +133,7 @@ describe('trackDocView', () => {
const b = (
await (async () => {
const spy2 = vi.fn()
- await trackDocView(
+ await trackVisit(
makeRequest('https://example.com/page', {
'user-agent': 'ClaudeBot',
'x-forwarded-for': '203.0.113.1, 10.0.0.2'