Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions src/analytics.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,11 @@ describe("ingestion", () => {

test("track(Request) infers dimensions and writes the event", async () => {
const req = new Request("https://upstash.com/blog", {
headers: { "user-agent": "PerplexityBot/1.0", referer: "https://www.perplexity.ai/" },
headers: {
"user-agent": "PerplexityBot/1.0",
referer: "https://www.perplexity.ai/",
accept: "Text/Markdown, text/html;q=0.8",
},
});

expect(await analytics.track(req)).toBe(1);
Expand All @@ -146,6 +150,7 @@ describe("ingestion", () => {
);
expect(tracked).toBeDefined();
expect(String(tracked!.provider)).toBe("perplexity");
expect(String(tracked!.accept)).toBe("text/markdown, text/html;q=0.8");
});
});

Expand Down Expand Up @@ -235,7 +240,7 @@ describe("querying without an index", () => {

describe("getIndex schema reconciliation", () => {
// The schema getIndex() should converge to, regardless of what existed before.
const EXPECTED_FIELDS = ["count", "hour", "path", "provider"];
const EXPECTED_FIELDS = ["accept", "count", "hour", "path", "provider"];

/** A bare index handle for inspecting the server-side schema via describe(). */
function indexHandle(name: string) {
Expand All @@ -244,6 +249,7 @@ describe("getIndex schema reconciliation", () => {
schema: {
count: { type: "U64" as const, fast: true as const },
hour: { type: "U64" as const, fast: true as const },
accept: { type: "KEYWORD" as const },
provider: { type: "KEYWORD" as const },
path: { type: "KEYWORD" as const },
},
Expand Down Expand Up @@ -299,6 +305,7 @@ describe("getIndex schema reconciliation", () => {
schema: {
count: { type: "U64", fast: true },
hour: { type: "U64", fast: true },
accept: { type: "KEYWORD" },
provider: { type: "KEYWORD" },
path: { type: "KEYWORD" },
sourceUrl: { type: "KEYWORD" },
Expand Down
1 change: 1 addition & 0 deletions src/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ export class IndexNotFoundError extends Error {
const EVENT_SCHEMA = {
count: { type: "U64" as const, fast: true as const },
hour: { type: "U64" as const, fast: true as const },
accept: { type: "KEYWORD" as const },
provider: { type: "KEYWORD" as const },
path: { type: "KEYWORD" as const },
};
Expand Down
28 changes: 28 additions & 0 deletions src/request.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { describe, expect, test } from "bun:test";

import { eventFromRequest } from "./request.ts";

describe("eventFromRequest", () => {
test("captures the normalized Accept header", () => {
const event = eventFromRequest(
new Request("https://upstash.com/blog#section", {
headers: {
"user-agent": "ClaudeBot/1.0",
accept: " Text/Markdown, text/html;q=0.8 ",
},
}),
);

expect(event).toEqual({
provider: "claude",
path: "https://upstash.com/blog",
accept: "text/markdown, text/html;q=0.8",
});
});

test("omits Accept when the header is missing", () => {
const event = eventFromRequest(new Request("https://upstash.com/docs"));

expect(event.accept).toBeUndefined();
});
});
5 changes: 5 additions & 0 deletions src/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ export function eventFromRequest(req: Request): TrackedEvent {
return {
provider: detectProvider(request),
path: normalizeUrl(request.nextUrl?.href ?? request.url),
accept: normalizeHeader(request.headers.get("accept")),
};
}

Expand All @@ -29,6 +30,10 @@ function normalizeUrl(url: string): string {
}
}

function normalizeHeader(value: string | null): string | undefined {
return value?.trim().toLowerCase() || undefined;
}

/** Infer the AI agent from the user-agent and referrer headers. */
export function detectProvider(req: Request): Provider {
const userAgent = req.headers.get("user-agent")?.toLowerCase() ?? "";
Expand Down
2 changes: 2 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export type TrackedEvent = {
provider: Provider;
/** The path on our site that was cited. */
path: string;
/** The request's Accept header, when present. */
accept?: string;
};

/** The dimensions that can be grouped/filtered on. */
Expand Down