@apideck/agent-analytics 0.11.0 → 0.12.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.
- package/README.md +56 -0
- package/dist/adapters/posthog.cjs +2 -30
- package/dist/adapters/posthog.cjs.map +1 -1
- package/dist/adapters/posthog.d.cts +7 -1
- package/dist/adapters/posthog.d.ts +7 -1
- package/dist/adapters/posthog.js +2 -28
- package/dist/adapters/posthog.js.map +1 -1
- package/dist/adapters/webhook.cjs +1 -23
- package/dist/adapters/webhook.cjs.map +1 -1
- package/dist/adapters/webhook.d.cts +3 -1
- package/dist/adapters/webhook.d.ts +3 -1
- package/dist/adapters/webhook.js +1 -21
- package/dist/adapters/webhook.js.map +1 -1
- package/dist/index.cjs +2 -900
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -100
- package/dist/index.d.ts +37 -100
- package/dist/index.js +2 -875
- package/dist/index.js.map +1 -1
- package/dist/markdown.cjs +2 -67
- package/dist/markdown.cjs.map +1 -1
- package/dist/markdown.js +2 -63
- package/dist/markdown.js.map +1 -1
- package/dist/{types-DKqlfVz6.d.cts → types-sQoQK-ox.d.cts} +31 -2
- package/dist/{types-DKqlfVz6.d.ts → types-sQoQK-ox.d.ts} +31 -2
- package/dist/verify.cjs +3 -0
- package/dist/verify.cjs.map +1 -0
- package/dist/verify.d.cts +50 -0
- package/dist/verify.d.ts +50 -0
- package/dist/verify.js +3 -0
- package/dist/verify.js.map +1 -0
- package/package.json +7 -2
package/dist/index.js
CHANGED
|
@@ -1,876 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
var HTTP_CLIENT_PATTERN = /axios\/|curl\/|(?:^|[\s(])got(?:\/|[\s(])|\bcolly\b|Electron\/|node-fetch\/|python-requests\/|Go-http-client\/|okhttp\/|aiohttp\/|Deno\//i;
|
|
4
|
-
function isAiBot(userAgent) {
|
|
5
|
-
if (!userAgent) return false;
|
|
6
|
-
return AI_BOT_PATTERN.test(userAgent);
|
|
7
|
-
}
|
|
8
|
-
function isHttpClient(userAgent) {
|
|
9
|
-
if (!userAgent) return false;
|
|
10
|
-
return HTTP_CLIENT_PATTERN.test(userAgent);
|
|
11
|
-
}
|
|
12
|
-
function parseBotName(userAgent) {
|
|
13
|
-
if (!userAgent || typeof userAgent !== "string") return "Other";
|
|
14
|
-
const s = userAgent.toLowerCase();
|
|
15
|
-
if (s.includes("chatgpt-user") || s.includes("gptbot") || s.includes("oai-searchbot") || s.includes("openai"))
|
|
16
|
-
return "ChatGPT";
|
|
17
|
-
if (s.includes("claudebot") || s.includes("claude-user") || s.includes("claude-searchbot") || s.includes("claude-web") || s.includes("anthropic"))
|
|
18
|
-
return "Claude";
|
|
19
|
-
if (s.includes("perplexitybot") || s.includes("perplexity-user")) return "Perplexity";
|
|
20
|
-
if (s.includes("ccbot")) return "Common Crawl";
|
|
21
|
-
if (s.includes("google-extended") || s.includes("googlebot") || s.includes("google-cloudvertexbot") || s.includes("google-agent") || s.includes("googleagent-mariner") || s.includes("gemini-deep-research"))
|
|
22
|
-
return "Google";
|
|
23
|
-
if (s.includes("applebot")) return "Apple";
|
|
24
|
-
if (s.includes("bingbot")) return "Bing";
|
|
25
|
-
if (s.includes("bytespider")) return "Bytespider";
|
|
26
|
-
if (s.includes("amazonbot") || s.includes("amzn-searchbot") || s.includes("novaact")) return "Amazon";
|
|
27
|
-
if (s.includes("meta-externalagent") || s.includes("meta-externalfetcher") || s.includes("meta-webindexer") || s.includes("facebookbot"))
|
|
28
|
-
return "Meta";
|
|
29
|
-
if (s.includes("mistralai-user")) return "Mistral";
|
|
30
|
-
if (s.includes("duckassistbot")) return "DuckDuckGo";
|
|
31
|
-
if (s.includes("youbot")) return "You.com";
|
|
32
|
-
if (s.includes("diffbot")) return "Diffbot";
|
|
33
|
-
if (s.includes("ai2bot")) return "AI2";
|
|
34
|
-
if (s.includes("cohere")) return "Cohere";
|
|
35
|
-
if (s.includes("cursor")) return "Cursor";
|
|
36
|
-
if (s.includes("windsurf")) return "Windsurf";
|
|
37
|
-
if (s.includes("deepseek")) return "DeepSeek";
|
|
38
|
-
if (s.includes("pangubot")) return "Huawei";
|
|
39
|
-
if (s.includes("webzio") || s.includes("omgili")) return "Webz.io";
|
|
40
|
-
if (s.includes("timpibot")) return "Timpi";
|
|
41
|
-
if (s.includes("grok") || s.includes("xai-")) return "xAI";
|
|
42
|
-
if (s.includes("manus-user")) return "Manus";
|
|
43
|
-
if (s.includes("quillbot")) return "QuillBot";
|
|
44
|
-
if (s.includes("azureai-searchbot")) return "Microsoft";
|
|
45
|
-
if (s.includes("mycentralaiscraperbot")) return "MyCentralAI";
|
|
46
|
-
if (s.includes("petalbot")) return "PetalBot";
|
|
47
|
-
if (s.includes("ahrefsbot")) return "Ahrefs";
|
|
48
|
-
if (s.includes("semrushbot")) return "Semrush";
|
|
49
|
-
if (s.includes("mj12bot")) return "Majestic";
|
|
50
|
-
if (s.includes("dotbot")) return "Moz";
|
|
51
|
-
if (s.includes("rogerbot")) return "Moz";
|
|
52
|
-
if (s.includes("screaming frog")) return "Screaming Frog";
|
|
53
|
-
if (s.includes("sitebulb")) return "Sitebulb";
|
|
54
|
-
if (s.includes("linkfluence")) return "Linkfluence";
|
|
55
|
-
if (s.includes("dataforseo")) return "DataForSEO";
|
|
56
|
-
if (s.includes("serpstatbot")) return "Serpstat";
|
|
57
|
-
if (s.includes("uptimerobot")) return "UptimeRobot";
|
|
58
|
-
if (s.includes("pingdom")) return "Pingdom";
|
|
59
|
-
if (s.includes("statuscake")) return "StatusCake";
|
|
60
|
-
if (s.includes("newrelicpinger")) return "New Relic";
|
|
61
|
-
if (s.includes("datadogagent") || s.includes("datadog")) return "Datadog";
|
|
62
|
-
if (s.includes("slackbot")) return "Slack";
|
|
63
|
-
if (s.includes("twitterbot")) return "Twitter";
|
|
64
|
-
if (s.includes("linkedinbot")) return "LinkedIn";
|
|
65
|
-
if (s.includes("discordbot")) return "Discord";
|
|
66
|
-
if (s.includes("telegrambot")) return "Telegram";
|
|
67
|
-
if (s.includes("whatsapp")) return "WhatsApp";
|
|
68
|
-
if (s.includes("linkupbot")) return "Linkup";
|
|
69
|
-
if (s.includes("sogou")) return "Sogou";
|
|
70
|
-
if (s.includes("yandexbot")) return "Yandex";
|
|
71
|
-
if (s.includes("baiduspider")) return "Baidu";
|
|
72
|
-
if (s.includes("facebookexternalhit")) return "Facebook";
|
|
73
|
-
if (s.includes("com.apple.webkit")) return "Apple URL Preview";
|
|
74
|
-
if (s.includes("ohdear")) return "Oh Dear";
|
|
75
|
-
if (s.includes("scrapy")) return "Scrapy";
|
|
76
|
-
if (s.includes("headlesschrome")) return "Headless Chrome";
|
|
77
|
-
if (s.includes("phantomjs")) return "PhantomJS";
|
|
78
|
-
if (s.includes("wget")) return "wget";
|
|
79
|
-
if (s.includes("httpie")) return "HTTPie";
|
|
80
|
-
if (s.includes("guzzlehttp")) return "Guzzle";
|
|
81
|
-
if (s.includes("electron/")) return "Electron";
|
|
82
|
-
if (/curl\//.test(s)) return "curl";
|
|
83
|
-
if (/axios\//.test(s)) return "axios";
|
|
84
|
-
if (/(?:^|[\s(])got(?:\/|[\s(])/.test(s)) return "got";
|
|
85
|
-
if (/\bcolly\b/.test(s)) return "colly";
|
|
86
|
-
if (/node-fetch\//.test(s)) return "node-fetch";
|
|
87
|
-
if (/python-requests\//.test(s)) return "python-requests";
|
|
88
|
-
if (/go-http-client\//.test(s)) return "Go http client";
|
|
89
|
-
if (/okhttp\//.test(s)) return "OkHttp";
|
|
90
|
-
if (/aiohttp\//.test(s)) return "aiohttp";
|
|
91
|
-
if (/deno\//.test(s)) return "Deno";
|
|
92
|
-
if (s.includes("mozilla") || s.includes("chrome") || s.includes("safari") || s.includes("firefox"))
|
|
93
|
-
return "Browser";
|
|
94
|
-
return "Other";
|
|
95
|
-
}
|
|
96
|
-
function firstUserAgentProduct(userAgent) {
|
|
97
|
-
if (!userAgent || typeof userAgent !== "string") return "Other";
|
|
98
|
-
const compatibleMatch = userAgent.match(/compatible;\s*([^/;\s]+)(?:\/[^\s;]*)?/i);
|
|
99
|
-
if (compatibleMatch && compatibleMatch[1]) return compatibleMatch[1].trim();
|
|
100
|
-
const first = userAgent.trim().split("/")[0]?.trim().split(/\s+/)[0]?.trim();
|
|
101
|
-
return first || "Other";
|
|
102
|
-
}
|
|
103
|
-
function detectHeadless(req) {
|
|
104
|
-
const signals = [];
|
|
105
|
-
const ua = (req.headers.get("user-agent") || "").toLowerCase();
|
|
106
|
-
const isBrowserUA = ua.includes("mozilla") || ua.includes("chrome") || ua.includes("safari") || ua.includes("firefox");
|
|
107
|
-
if (!isBrowserUA) return { score: 0, signals: [], likely: false };
|
|
108
|
-
if (!req.headers.get("accept-language")) {
|
|
109
|
-
signals.push("missing-accept-language");
|
|
110
|
-
}
|
|
111
|
-
if (!req.headers.get("sec-fetch-mode")) {
|
|
112
|
-
signals.push("missing-sec-fetch-mode");
|
|
113
|
-
}
|
|
114
|
-
const secChUa = req.headers.get("sec-ch-ua");
|
|
115
|
-
if (!secChUa) {
|
|
116
|
-
signals.push("missing-sec-ch-ua");
|
|
117
|
-
} else if (secChUa.toLowerCase().includes("headlesschrome")) {
|
|
118
|
-
signals.push("headless-chrome-hint");
|
|
119
|
-
}
|
|
120
|
-
const accept = req.headers.get("accept") || "";
|
|
121
|
-
if (!accept || accept === "*/*") {
|
|
122
|
-
signals.push("missing-or-bare-accept");
|
|
123
|
-
}
|
|
124
|
-
if ((req.headers.get("connection") || "").toLowerCase() === "close") {
|
|
125
|
-
signals.push("connection-close");
|
|
126
|
-
}
|
|
127
|
-
const score = signals.length;
|
|
128
|
-
return { score, signals, likely: score >= 2 };
|
|
129
|
-
}
|
|
130
|
-
function classifyAgent(userAgent) {
|
|
131
|
-
const label = parseBotName(userAgent);
|
|
132
|
-
const aiBot = isAiBot(userAgent);
|
|
133
|
-
const httpClient = isHttpClient(userAgent);
|
|
134
|
-
let kind;
|
|
135
|
-
if (aiBot) kind = "declared-crawler";
|
|
136
|
-
else if (httpClient) kind = "coding-agent-hint";
|
|
137
|
-
else if (label === "Browser") kind = "browser";
|
|
138
|
-
else kind = "other";
|
|
139
|
-
return { kind, label, isAiBot: aiBot, codingAgentHint: httpClient };
|
|
140
|
-
}
|
|
141
|
-
function classifyRequest(req) {
|
|
142
|
-
const userAgent = req.headers.get("user-agent") || "";
|
|
143
|
-
const base = classifyAgent(userAgent);
|
|
144
|
-
const headless = detectHeadless(req);
|
|
145
|
-
let kind = base.kind;
|
|
146
|
-
if (kind === "browser" && headless.likely) {
|
|
147
|
-
kind = "headless-likely";
|
|
148
|
-
}
|
|
149
|
-
return { ...base, kind, headless };
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// src/bot-ranges.ts
|
|
153
|
-
var BOT_RANGES_CAPTURED_AT = "2026-08-02T00:00:00Z";
|
|
154
|
-
var BOT_IP_RANGES = {
|
|
155
|
-
ChatGPT: [
|
|
156
|
-
"104.208.184.192/28",
|
|
157
|
-
"104.208.184.208/28",
|
|
158
|
-
"104.210.139.192/28",
|
|
159
|
-
"104.210.139.224/28",
|
|
160
|
-
"104.210.140.128/28",
|
|
161
|
-
"128.85.198.32/28",
|
|
162
|
-
"13.65.138.112/28",
|
|
163
|
-
"13.65.138.96/28",
|
|
164
|
-
"13.67.72.16/28",
|
|
165
|
-
"13.70.107.160/28",
|
|
166
|
-
"13.71.2.208/28",
|
|
167
|
-
"13.76.115.224/28",
|
|
168
|
-
"13.76.115.240/28",
|
|
169
|
-
"13.76.116.80/28",
|
|
170
|
-
"13.76.32.208/28",
|
|
171
|
-
"13.83.167.128/28",
|
|
172
|
-
"13.83.237.176/28",
|
|
173
|
-
"132.196.82.48/28",
|
|
174
|
-
"132.196.86.0/24",
|
|
175
|
-
"134.149.233.80/28",
|
|
176
|
-
"135.116.136.160/28",
|
|
177
|
-
"135.13.64.240/28",
|
|
178
|
-
"135.220.73.208/28",
|
|
179
|
-
"135.220.73.240/28",
|
|
180
|
-
"135.234.64.0/24",
|
|
181
|
-
"135.237.131.208/28",
|
|
182
|
-
"135.237.133.48/28",
|
|
183
|
-
"137.135.191.176/28",
|
|
184
|
-
"138.91.30.48/28",
|
|
185
|
-
"138.91.46.96/28",
|
|
186
|
-
"145.132.1.32/28",
|
|
187
|
-
"145.132.136.96/28",
|
|
188
|
-
"145.133.0.176/28",
|
|
189
|
-
"158.158.5.32/28",
|
|
190
|
-
"168.63.252.240/28",
|
|
191
|
-
"172.162.248.64/28",
|
|
192
|
-
"172.170.1.80/28",
|
|
193
|
-
"172.170.225.0/28",
|
|
194
|
-
"172.170.241.80/28",
|
|
195
|
-
"172.170.8.208/28",
|
|
196
|
-
"172.171.4.176/28",
|
|
197
|
-
"172.175.152.224/28",
|
|
198
|
-
"172.178.140.144/28",
|
|
199
|
-
"172.178.141.112/28",
|
|
200
|
-
"172.178.141.128/28",
|
|
201
|
-
"172.182.193.224/28",
|
|
202
|
-
"172.182.193.80/28",
|
|
203
|
-
"172.182.194.144/28",
|
|
204
|
-
"172.182.194.32/28",
|
|
205
|
-
"172.182.195.48/28",
|
|
206
|
-
"172.182.202.0/25",
|
|
207
|
-
"172.182.204.0/24",
|
|
208
|
-
"172.182.207.0/25",
|
|
209
|
-
"172.182.209.208/28",
|
|
210
|
-
"172.182.211.192/28",
|
|
211
|
-
"172.182.213.192/28",
|
|
212
|
-
"172.182.214.0/24",
|
|
213
|
-
"172.182.215.0/24",
|
|
214
|
-
"172.182.224.0/28",
|
|
215
|
-
"172.183.143.224/28",
|
|
216
|
-
"172.183.222.128/28",
|
|
217
|
-
"172.192.112.208/28",
|
|
218
|
-
"172.192.88.192/28",
|
|
219
|
-
"172.192.97.32/28",
|
|
220
|
-
"172.197.160.192/28",
|
|
221
|
-
"172.197.161.208/28",
|
|
222
|
-
"172.197.170.80/28",
|
|
223
|
-
"172.197.203.16/28",
|
|
224
|
-
"172.199.137.80/28",
|
|
225
|
-
"172.202.102.112/28",
|
|
226
|
-
"172.203.190.128/28",
|
|
227
|
-
"172.204.27.16/28",
|
|
228
|
-
"172.204.28.224/28",
|
|
229
|
-
"172.204.96.32/28",
|
|
230
|
-
"172.204.96.48/28",
|
|
231
|
-
"172.204.96.80/28",
|
|
232
|
-
"172.205.189.192/28",
|
|
233
|
-
"172.207.1.32/28",
|
|
234
|
-
"172.208.128.32/28",
|
|
235
|
-
"172.208.128.48/28",
|
|
236
|
-
"172.212.159.64/28",
|
|
237
|
-
"172.212.172.160/28",
|
|
238
|
-
"172.213.21.16/28",
|
|
239
|
-
"172.215.215.32/28",
|
|
240
|
-
"172.215.218.96/28",
|
|
241
|
-
"191.233.1.112/28",
|
|
242
|
-
"191.233.1.128/28",
|
|
243
|
-
"191.233.194.32/28",
|
|
244
|
-
"191.233.196.112/28",
|
|
245
|
-
"191.233.199.160/28",
|
|
246
|
-
"191.233.2.0/28",
|
|
247
|
-
"191.235.66.16/28",
|
|
248
|
-
"191.235.99.80/28",
|
|
249
|
-
"191.237.249.64/28",
|
|
250
|
-
"191.239.245.16/28",
|
|
251
|
-
"20.102.212.144/28",
|
|
252
|
-
"20.113.211.112/28",
|
|
253
|
-
"20.113.225.112/28",
|
|
254
|
-
"20.125.112.224/28",
|
|
255
|
-
"20.125.144.144/28",
|
|
256
|
-
"20.125.66.80/28",
|
|
257
|
-
"20.14.99.96/28",
|
|
258
|
-
"20.161.75.208/28",
|
|
259
|
-
"20.168.18.32/28",
|
|
260
|
-
"20.168.7.192/28",
|
|
261
|
-
"20.168.7.240/28",
|
|
262
|
-
"20.169.6.224/28",
|
|
263
|
-
"20.169.7.48/28",
|
|
264
|
-
"20.169.72.112/28",
|
|
265
|
-
"20.169.73.176/28",
|
|
266
|
-
"20.169.73.32/28",
|
|
267
|
-
"20.169.73.64/28",
|
|
268
|
-
"20.169.77.0/25",
|
|
269
|
-
"20.169.78.112/28",
|
|
270
|
-
"20.169.78.128/28",
|
|
271
|
-
"20.169.78.144/28",
|
|
272
|
-
"20.169.78.160/28",
|
|
273
|
-
"20.169.78.176/28",
|
|
274
|
-
"20.169.78.192/28",
|
|
275
|
-
"20.169.78.208/28",
|
|
276
|
-
"20.169.78.48/28",
|
|
277
|
-
"20.169.78.64/28",
|
|
278
|
-
"20.169.78.80/28",
|
|
279
|
-
"20.169.78.96/28",
|
|
280
|
-
"20.169.86.224/28",
|
|
281
|
-
"20.169.86.240/28",
|
|
282
|
-
"20.169.87.112/28",
|
|
283
|
-
"20.17.108.96/28",
|
|
284
|
-
"20.170.184.16/28",
|
|
285
|
-
"20.170.184.32/28",
|
|
286
|
-
"20.170.184.48/28",
|
|
287
|
-
"20.170.184.64/28",
|
|
288
|
-
"20.170.184.80/28",
|
|
289
|
-
"20.171.123.64/28",
|
|
290
|
-
"20.171.206.0/24",
|
|
291
|
-
"20.171.207.0/24",
|
|
292
|
-
"20.171.53.224/28",
|
|
293
|
-
"20.172.29.32/28",
|
|
294
|
-
"20.193.233.240/28",
|
|
295
|
-
"20.193.50.32/28",
|
|
296
|
-
"20.194.0.208/28",
|
|
297
|
-
"20.194.1.0/28",
|
|
298
|
-
"20.198.67.96/28",
|
|
299
|
-
"20.199.211.160/28",
|
|
300
|
-
"20.199.242.0/28",
|
|
301
|
-
"20.200.212.240/28",
|
|
302
|
-
"20.204.24.240/28",
|
|
303
|
-
"20.210.154.128/28",
|
|
304
|
-
"20.210.174.208/28",
|
|
305
|
-
"20.210.211.192/28",
|
|
306
|
-
"20.215.187.208/28",
|
|
307
|
-
"20.215.188.192/28",
|
|
308
|
-
"20.215.214.16/28",
|
|
309
|
-
"20.215.219.128/28",
|
|
310
|
-
"20.215.219.160/28",
|
|
311
|
-
"20.215.219.208/28",
|
|
312
|
-
"20.215.220.112/28",
|
|
313
|
-
"20.215.220.128/28",
|
|
314
|
-
"20.215.220.144/28",
|
|
315
|
-
"20.215.220.160/28",
|
|
316
|
-
"20.215.220.176/28",
|
|
317
|
-
"20.215.220.192/28",
|
|
318
|
-
"20.215.220.208/28",
|
|
319
|
-
"20.215.220.64/28",
|
|
320
|
-
"20.215.220.80/28",
|
|
321
|
-
"20.215.220.96/28",
|
|
322
|
-
"20.218.30.240/28",
|
|
323
|
-
"20.219.71.192/28",
|
|
324
|
-
"20.222.36.192/28",
|
|
325
|
-
"20.226.32.80/28",
|
|
326
|
-
"20.227.140.32/28",
|
|
327
|
-
"20.228.106.176/28",
|
|
328
|
-
"20.235.75.208/28",
|
|
329
|
-
"20.235.87.224/28",
|
|
330
|
-
"20.249.63.208/28",
|
|
331
|
-
"20.25.151.224/28",
|
|
332
|
-
"20.250.136.64/28",
|
|
333
|
-
"20.250.136.80/28",
|
|
334
|
-
"20.250.6.128/28",
|
|
335
|
-
"20.27.94.128/28",
|
|
336
|
-
"20.42.10.176/28",
|
|
337
|
-
"20.45.178.144/28",
|
|
338
|
-
"20.48.120.208/28",
|
|
339
|
-
"20.52.125.160/28",
|
|
340
|
-
"20.55.129.0/28",
|
|
341
|
-
"20.55.229.144/28",
|
|
342
|
-
"20.57.199.192/28",
|
|
343
|
-
"20.63.180.96/28",
|
|
344
|
-
"20.63.221.64/28",
|
|
345
|
-
"20.79.59.112/28",
|
|
346
|
-
"20.81.183.64/28",
|
|
347
|
-
"20.83.243.176/28",
|
|
348
|
-
"20.97.189.96/28",
|
|
349
|
-
"23.102.140.144/28",
|
|
350
|
-
"23.102.141.32/28",
|
|
351
|
-
"23.98.142.176/28",
|
|
352
|
-
"23.98.179.16/28",
|
|
353
|
-
"23.98.186.176/28",
|
|
354
|
-
"23.98.186.192/28",
|
|
355
|
-
"23.98.186.64/28",
|
|
356
|
-
"23.98.186.96/28",
|
|
357
|
-
"4.151.119.48/28",
|
|
358
|
-
"4.151.241.240/28",
|
|
359
|
-
"4.151.71.176/28",
|
|
360
|
-
"4.189.118.208/28",
|
|
361
|
-
"4.189.119.48/28",
|
|
362
|
-
"4.196.118.112/28",
|
|
363
|
-
"4.197.115.112/28",
|
|
364
|
-
"4.197.19.176/28",
|
|
365
|
-
"4.197.22.112/28",
|
|
366
|
-
"4.197.64.0/28",
|
|
367
|
-
"4.197.64.16/28",
|
|
368
|
-
"4.197.64.48/28",
|
|
369
|
-
"4.197.64.64/28",
|
|
370
|
-
"4.198.72.16/28",
|
|
371
|
-
"4.198.96.112/28",
|
|
372
|
-
"4.201.232.64/28",
|
|
373
|
-
"4.201.232.80/28",
|
|
374
|
-
"4.203.96.80/28",
|
|
375
|
-
"4.205.128.176/28",
|
|
376
|
-
"4.218.24.64/28",
|
|
377
|
-
"4.226.200.16/28",
|
|
378
|
-
"4.226.226.32/28",
|
|
379
|
-
"4.227.36.0/25",
|
|
380
|
-
"40.116.73.208/28",
|
|
381
|
-
"40.122.235.112/28",
|
|
382
|
-
"40.67.175.0/25",
|
|
383
|
-
"40.67.183.160/28",
|
|
384
|
-
"40.67.183.176/28",
|
|
385
|
-
"40.78.161.48/28",
|
|
386
|
-
"40.81.134.128/28",
|
|
387
|
-
"40.81.134.144/28",
|
|
388
|
-
"40.81.234.144/28",
|
|
389
|
-
"40.81.67.96/28",
|
|
390
|
-
"40.84.181.32/28",
|
|
391
|
-
"40.84.221.208/28",
|
|
392
|
-
"40.84.221.224/28",
|
|
393
|
-
"40.90.214.16/28",
|
|
394
|
-
"48.193.44.32/28",
|
|
395
|
-
"48.221.184.112/28",
|
|
396
|
-
"48.221.184.80/28",
|
|
397
|
-
"48.221.184.96/28",
|
|
398
|
-
"48.221.40.176/28",
|
|
399
|
-
"51.107.70.192/28",
|
|
400
|
-
"51.116.2.80/28",
|
|
401
|
-
"51.116.221.96/28",
|
|
402
|
-
"51.56.40.80/28",
|
|
403
|
-
"51.57.0.96/28",
|
|
404
|
-
"51.59.24.64/28",
|
|
405
|
-
"51.59.24.80/28",
|
|
406
|
-
"51.59.40.80/28",
|
|
407
|
-
"51.59.40.96/28",
|
|
408
|
-
"51.59.48.80/28",
|
|
409
|
-
"51.59.48.96/28",
|
|
410
|
-
"51.8.102.0/24",
|
|
411
|
-
"51.8.155.112/28",
|
|
412
|
-
"51.8.155.48/28",
|
|
413
|
-
"51.8.155.64/28",
|
|
414
|
-
"51.8.187.224/28",
|
|
415
|
-
"52.148.129.32/28",
|
|
416
|
-
"52.153.130.64/28",
|
|
417
|
-
"52.154.22.48/28",
|
|
418
|
-
"52.156.77.144/28",
|
|
419
|
-
"52.159.227.32/28",
|
|
420
|
-
"52.159.249.96/28",
|
|
421
|
-
"52.161.49.224/28",
|
|
422
|
-
"52.161.49.96/28",
|
|
423
|
-
"52.165.212.16/28",
|
|
424
|
-
"52.165.212.32/28",
|
|
425
|
-
"52.165.212.48/28",
|
|
426
|
-
"52.172.129.160/28",
|
|
427
|
-
"52.172.251.112/28",
|
|
428
|
-
"52.173.219.112/28",
|
|
429
|
-
"52.173.219.96/28",
|
|
430
|
-
"52.173.221.16/28",
|
|
431
|
-
"52.173.221.176/28",
|
|
432
|
-
"52.173.221.208/28",
|
|
433
|
-
"52.173.234.16/28",
|
|
434
|
-
"52.173.234.80/28",
|
|
435
|
-
"52.173.235.80/28",
|
|
436
|
-
"52.183.217.240/28",
|
|
437
|
-
"52.187.246.128/28",
|
|
438
|
-
"52.190.137.144/28",
|
|
439
|
-
"52.190.137.16/28",
|
|
440
|
-
"52.190.139.48/28",
|
|
441
|
-
"52.190.142.64/28",
|
|
442
|
-
"52.190.190.16/28",
|
|
443
|
-
"52.225.75.208/28",
|
|
444
|
-
"52.230.152.0/24",
|
|
445
|
-
"52.230.163.32/28",
|
|
446
|
-
"52.230.164.176/28",
|
|
447
|
-
"52.231.30.48/28",
|
|
448
|
-
"52.231.34.176/28",
|
|
449
|
-
"52.231.39.144/28",
|
|
450
|
-
"52.231.39.192/28",
|
|
451
|
-
"52.231.49.48/28",
|
|
452
|
-
"52.231.50.64/28",
|
|
453
|
-
"52.236.94.144/28",
|
|
454
|
-
"52.241.146.208/28",
|
|
455
|
-
"52.242.132.224/28",
|
|
456
|
-
"52.242.132.240/28",
|
|
457
|
-
"52.242.245.208/28",
|
|
458
|
-
"52.252.113.240/28",
|
|
459
|
-
"52.255.109.112/28",
|
|
460
|
-
"52.255.109.128/28",
|
|
461
|
-
"52.255.109.144/28",
|
|
462
|
-
"52.255.109.80/28",
|
|
463
|
-
"52.255.109.96/28",
|
|
464
|
-
"52.255.111.0/28",
|
|
465
|
-
"52.255.111.112/28",
|
|
466
|
-
"52.255.111.32/28",
|
|
467
|
-
"52.255.111.48/28",
|
|
468
|
-
"52.255.111.80/28",
|
|
469
|
-
"57.154.174.112/28",
|
|
470
|
-
"57.154.175.0/28",
|
|
471
|
-
"57.154.187.32/28",
|
|
472
|
-
"68.154.28.96/28",
|
|
473
|
-
"68.218.30.112/28",
|
|
474
|
-
"68.220.57.64/28",
|
|
475
|
-
"68.221.67.192/28",
|
|
476
|
-
"68.221.67.224/28",
|
|
477
|
-
"68.221.67.240/28",
|
|
478
|
-
"70.153.139.208/28",
|
|
479
|
-
"70.153.189.192/28",
|
|
480
|
-
"70.153.190.16/28",
|
|
481
|
-
"70.153.76.16/28",
|
|
482
|
-
"70.153.87.224/28",
|
|
483
|
-
"70.156.144.64/28",
|
|
484
|
-
"70.156.152.80/28",
|
|
485
|
-
"70.156.152.96/28",
|
|
486
|
-
"74.161.200.96/28",
|
|
487
|
-
"74.224.217.64/28",
|
|
488
|
-
"74.226.253.160/28",
|
|
489
|
-
"74.249.86.176/28",
|
|
490
|
-
"74.7.175.128/25",
|
|
491
|
-
"74.7.227.0/25",
|
|
492
|
-
"74.7.227.128/25",
|
|
493
|
-
"74.7.228.0/25",
|
|
494
|
-
"74.7.228.128/25",
|
|
495
|
-
"74.7.229.0/25",
|
|
496
|
-
"74.7.229.128/25",
|
|
497
|
-
"74.7.230.0/25",
|
|
498
|
-
"74.7.241.0/25",
|
|
499
|
-
"74.7.241.128/25",
|
|
500
|
-
"74.7.242.0/25",
|
|
501
|
-
"74.7.242.128/25",
|
|
502
|
-
"74.7.243.0/25",
|
|
503
|
-
"74.7.243.128/25",
|
|
504
|
-
"74.7.244.0/25",
|
|
505
|
-
"74.7.35.112/28",
|
|
506
|
-
"74.7.35.48/28",
|
|
507
|
-
"74.7.36.64/28",
|
|
508
|
-
"74.7.36.80/28",
|
|
509
|
-
"74.7.36.96/28",
|
|
510
|
-
"85.211.241.128/28",
|
|
511
|
-
"9.129.0.0/17",
|
|
512
|
-
"9.160.128.16/28",
|
|
513
|
-
"9.160.128.32/28",
|
|
514
|
-
"9.160.128.64/28",
|
|
515
|
-
"9.160.163.128/28",
|
|
516
|
-
"9.160.164.128/28",
|
|
517
|
-
"9.160.34.144/28",
|
|
518
|
-
"9.160.36.16/28",
|
|
519
|
-
"9.160.96.16/28",
|
|
520
|
-
"9.163.101.48/28",
|
|
521
|
-
"9.205.25.128/28",
|
|
522
|
-
"9.205.8.48/28",
|
|
523
|
-
"9.223.181.208/28",
|
|
524
|
-
"9.234.96.192/28",
|
|
525
|
-
"9.234.97.128/28",
|
|
526
|
-
"9.234.97.96/28",
|
|
527
|
-
"9.235.40.32/28"
|
|
528
|
-
],
|
|
529
|
-
Claude: [
|
|
530
|
-
"136.107.176.208/32",
|
|
531
|
-
"216.73.216.0/22",
|
|
532
|
-
"34.11.34.31/32",
|
|
533
|
-
"34.150.241.79/32",
|
|
534
|
-
"34.162.191.81/32",
|
|
535
|
-
"34.162.230.222/32",
|
|
536
|
-
"34.162.244.71/32",
|
|
537
|
-
"34.182.140.95/32",
|
|
538
|
-
"34.182.161.143/32",
|
|
539
|
-
"34.182.218.27/32",
|
|
540
|
-
"34.182.220.85/32",
|
|
541
|
-
"34.182.222.37/32",
|
|
542
|
-
"34.182.225.167/32",
|
|
543
|
-
"34.182.226.151/32",
|
|
544
|
-
"34.182.226.221/32",
|
|
545
|
-
"34.186.108.163/32",
|
|
546
|
-
"34.85.172.162/32",
|
|
547
|
-
"35.221.29.174/32",
|
|
548
|
-
"35.245.175.129/32",
|
|
549
|
-
"35.245.89.239/32"
|
|
550
|
-
],
|
|
551
|
-
Perplexity: [
|
|
552
|
-
"107.20.236.150/32",
|
|
553
|
-
"18.210.92.235/32",
|
|
554
|
-
"18.97.1.228/30",
|
|
555
|
-
"18.97.9.96/29",
|
|
556
|
-
"3.211.124.183/32",
|
|
557
|
-
"3.222.232.239/32",
|
|
558
|
-
"3.224.62.45/32",
|
|
559
|
-
"3.231.139.107/32"
|
|
560
|
-
],
|
|
561
|
-
Apple: [
|
|
562
|
-
"17.22.237.0/24",
|
|
563
|
-
"17.22.245.0/24",
|
|
564
|
-
"17.22.253.0/24",
|
|
565
|
-
"17.241.193.160/27",
|
|
566
|
-
"17.241.200.160/27",
|
|
567
|
-
"17.241.208.160/27",
|
|
568
|
-
"17.241.219.0/24",
|
|
569
|
-
"17.241.227.0/24",
|
|
570
|
-
"17.241.75.0/24",
|
|
571
|
-
"17.246.15.0/24",
|
|
572
|
-
"17.246.19.0/24",
|
|
573
|
-
"17.246.23.0/24"
|
|
574
|
-
]
|
|
575
|
-
};
|
|
576
|
-
var VERIFIABLE_VENDORS = Object.keys(BOT_IP_RANGES);
|
|
577
|
-
|
|
578
|
-
// src/cidr.ts
|
|
579
|
-
function ipv4ToInt(ip) {
|
|
580
|
-
const parts = ip.split(".");
|
|
581
|
-
if (parts.length !== 4) return null;
|
|
582
|
-
let out = 0;
|
|
583
|
-
for (const part of parts) {
|
|
584
|
-
if (!/^\d{1,3}$/.test(part)) return null;
|
|
585
|
-
if (part.length > 1 && part[0] === "0") return null;
|
|
586
|
-
const n = Number(part);
|
|
587
|
-
if (n > 255) return null;
|
|
588
|
-
out = out << 8 | n;
|
|
589
|
-
}
|
|
590
|
-
return out >>> 0;
|
|
591
|
-
}
|
|
592
|
-
function ipv6ToBigInt(ip) {
|
|
593
|
-
let text = ip;
|
|
594
|
-
const zone = text.indexOf("%");
|
|
595
|
-
if (zone !== -1) text = text.slice(0, zone);
|
|
596
|
-
if (!text || text.indexOf(":") === -1) return null;
|
|
597
|
-
let tail = null;
|
|
598
|
-
const lastColon = text.lastIndexOf(":");
|
|
599
|
-
const maybeV4 = text.slice(lastColon + 1);
|
|
600
|
-
if (maybeV4.indexOf(".") !== -1) {
|
|
601
|
-
tail = ipv4ToInt(maybeV4);
|
|
602
|
-
if (tail === null) return null;
|
|
603
|
-
text = text.slice(0, lastColon + 1) + "0:0";
|
|
604
|
-
}
|
|
605
|
-
const halves = text.split("::");
|
|
606
|
-
if (halves.length > 2) return null;
|
|
607
|
-
const head = halves[0] ? halves[0].split(":") : [];
|
|
608
|
-
const rest = halves.length === 2 ? halves[1] ? halves[1].split(":") : [] : null;
|
|
609
|
-
let groups;
|
|
610
|
-
if (rest === null) {
|
|
611
|
-
groups = head;
|
|
612
|
-
if (groups.length !== 8) return null;
|
|
613
|
-
} else {
|
|
614
|
-
const fill = 8 - head.length - rest.length;
|
|
615
|
-
if (fill < 0) return null;
|
|
616
|
-
groups = [...head, ...Array(fill).fill("0"), ...rest];
|
|
617
|
-
}
|
|
618
|
-
let out = 0n;
|
|
619
|
-
for (const g of groups) {
|
|
620
|
-
if (!/^[0-9a-fA-F]{1,4}$/.test(g)) return null;
|
|
621
|
-
out = out << 16n | BigInt(parseInt(g, 16));
|
|
622
|
-
}
|
|
623
|
-
if (tail !== null) out = out >> 32n << 32n | BigInt(tail >>> 0);
|
|
624
|
-
return out;
|
|
625
|
-
}
|
|
626
|
-
function compileRanges(cidrs) {
|
|
627
|
-
const v4 = [];
|
|
628
|
-
const v6 = [];
|
|
629
|
-
for (const cidr of cidrs) {
|
|
630
|
-
const slash = cidr.lastIndexOf("/");
|
|
631
|
-
if (slash === -1) continue;
|
|
632
|
-
const addr = cidr.slice(0, slash);
|
|
633
|
-
const bits = Number(cidr.slice(slash + 1));
|
|
634
|
-
if (!Number.isInteger(bits) || bits < 0) continue;
|
|
635
|
-
if (addr.indexOf(":") !== -1) {
|
|
636
|
-
if (bits > 128) continue;
|
|
637
|
-
const net = ipv6ToBigInt(addr);
|
|
638
|
-
if (net === null) continue;
|
|
639
|
-
v6.push({ net: bits === 0 ? 0n : net >> BigInt(128 - bits) << BigInt(128 - bits), bits });
|
|
640
|
-
} else {
|
|
641
|
-
if (bits > 32) continue;
|
|
642
|
-
const net = ipv4ToInt(addr);
|
|
643
|
-
if (net === null) continue;
|
|
644
|
-
const mask = bits === 0 ? 0 : 4294967295 << 32 - bits >>> 0;
|
|
645
|
-
v4.push({ net: (net & mask) >>> 0, mask });
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
return { v4, v6 };
|
|
649
|
-
}
|
|
650
|
-
function ipInRanges(ip, ranges) {
|
|
651
|
-
if (!ip) return false;
|
|
652
|
-
const trimmed = ip.trim();
|
|
653
|
-
if (!trimmed) return false;
|
|
654
|
-
if (trimmed.indexOf(":") !== -1) {
|
|
655
|
-
const value2 = ipv6ToBigInt(trimmed);
|
|
656
|
-
if (value2 === null) return false;
|
|
657
|
-
const V4_MAPPED_PREFIX = 0xffffn << 32n;
|
|
658
|
-
if (value2 >> 32n === V4_MAPPED_PREFIX >> 32n) {
|
|
659
|
-
const low = Number(value2 & 0xffffffffn) >>> 0;
|
|
660
|
-
if (matchV4(low, ranges.v4)) return true;
|
|
661
|
-
}
|
|
662
|
-
for (const r of ranges.v6) {
|
|
663
|
-
if (r.bits === 0) return true;
|
|
664
|
-
if (value2 >> BigInt(128 - r.bits) << BigInt(128 - r.bits) === r.net) return true;
|
|
665
|
-
}
|
|
666
|
-
return false;
|
|
667
|
-
}
|
|
668
|
-
const value = ipv4ToInt(trimmed);
|
|
669
|
-
if (value === null) return false;
|
|
670
|
-
return matchV4(value, ranges.v4);
|
|
671
|
-
}
|
|
672
|
-
function matchV4(value, list) {
|
|
673
|
-
for (const r of list) {
|
|
674
|
-
if ((value & r.mask) >>> 0 === r.net) return true;
|
|
675
|
-
}
|
|
676
|
-
return false;
|
|
677
|
-
}
|
|
678
|
-
function ipInCidr(ip, cidr) {
|
|
679
|
-
return ipInRanges(ip, compileRanges([cidr]));
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
// src/verify.ts
|
|
683
|
-
var CLIENT_SIDE_AGENT_PATTERN = /claude-code|perplexity-user|cursor|windsurf|cline|aider/i;
|
|
684
|
-
var SERVER_SIDE_CRAWLER_PATTERN = /ClaudeBot|Claude-SearchBot|GPTBot|OAI-SearchBot|ChatGPT-User|PerplexityBot|Applebot/i;
|
|
685
|
-
var COMPILED = {};
|
|
686
|
-
for (const vendor of VERIFIABLE_VENDORS) {
|
|
687
|
-
COMPILED[vendor] = compileRanges(BOT_IP_RANGES[vendor] ?? []);
|
|
688
|
-
}
|
|
689
|
-
function verifiableVendors() {
|
|
690
|
-
return VERIFIABLE_VENDORS;
|
|
691
|
-
}
|
|
692
|
-
function verifyBotIdentity(userAgent, ip) {
|
|
693
|
-
const ua = userAgent ?? "";
|
|
694
|
-
const claimed = parseBotName(userAgent);
|
|
695
|
-
const ranges = COMPILED[claimed];
|
|
696
|
-
if (!ranges) {
|
|
697
|
-
const isKnownCrawler = claimed !== "Other" && claimed !== "Browser";
|
|
698
|
-
return {
|
|
699
|
-
verdict: isKnownCrawler ? "unverifiable" : "not-claimed",
|
|
700
|
-
...isKnownCrawler ? { reason: "no-published-ranges" } : {},
|
|
701
|
-
claimed: isKnownCrawler ? claimed : null,
|
|
702
|
-
verified: null
|
|
703
|
-
};
|
|
704
|
-
}
|
|
705
|
-
if (CLIENT_SIDE_AGENT_PATTERN.test(ua) || !SERVER_SIDE_CRAWLER_PATTERN.test(ua)) {
|
|
706
|
-
return {
|
|
707
|
-
verdict: "unverifiable",
|
|
708
|
-
reason: "client-side-agent",
|
|
709
|
-
claimed,
|
|
710
|
-
verified: null
|
|
711
|
-
};
|
|
712
|
-
}
|
|
713
|
-
const trimmed = (ip ?? "").trim();
|
|
714
|
-
if (!trimmed) {
|
|
715
|
-
return { verdict: "unverifiable", reason: "no-client-ip", claimed, verified: null };
|
|
716
|
-
}
|
|
717
|
-
const inRange = ipInRanges(trimmed, ranges);
|
|
718
|
-
return {
|
|
719
|
-
verdict: inRange ? "verified" : "spoofed",
|
|
720
|
-
claimed,
|
|
721
|
-
verified: inRange
|
|
722
|
-
};
|
|
723
|
-
}
|
|
724
|
-
function clientIpFromRequest(req) {
|
|
725
|
-
const forwarded = req.headers.get("x-forwarded-for") || "";
|
|
726
|
-
const first = forwarded.split(",")[0]?.trim();
|
|
727
|
-
if (first) return first;
|
|
728
|
-
return (req.headers.get("cf-connecting-ip") || req.headers.get("x-real-ip") || "").trim();
|
|
729
|
-
}
|
|
730
|
-
function verifyRequest(req) {
|
|
731
|
-
return verifyBotIdentity(req.headers.get("user-agent"), clientIpFromRequest(req));
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
// src/hash.ts
|
|
735
|
-
function hashId(input) {
|
|
736
|
-
let h = 5381;
|
|
737
|
-
for (let i = 0; i < input.length; i++) {
|
|
738
|
-
h = (h << 5) + h + input.charCodeAt(i) & 4294967295;
|
|
739
|
-
}
|
|
740
|
-
return "anon_" + (h >>> 0).toString(16);
|
|
741
|
-
}
|
|
742
|
-
|
|
743
|
-
// src/track.ts
|
|
744
|
-
async function trackVisit(req, opts) {
|
|
745
|
-
const userAgent = req.headers.get("user-agent") || "";
|
|
746
|
-
const onlyBots = opts.onlyBots ?? false;
|
|
747
|
-
const skipBrowsers = opts.skipBrowsers ?? false;
|
|
748
|
-
if (onlyBots && !isAiBot(userAgent)) return;
|
|
749
|
-
if (skipBrowsers && !isAiBot(userAgent) && !isHttpClient(userAgent)) {
|
|
750
|
-
if (!detectHeadless(req).likely) return;
|
|
751
|
-
}
|
|
752
|
-
let pathname = "/";
|
|
753
|
-
let originFromUrl = "";
|
|
754
|
-
try {
|
|
755
|
-
const url = new URL(req.url);
|
|
756
|
-
pathname = url.pathname;
|
|
757
|
-
originFromUrl = url.origin;
|
|
758
|
-
} catch {
|
|
759
|
-
pathname = req.url || "/";
|
|
760
|
-
}
|
|
761
|
-
const origin = opts.origin ?? originFromUrl;
|
|
762
|
-
const forwardedFor = req.headers.get("x-forwarded-for") || "";
|
|
763
|
-
const ip = forwardedFor.split(",")[0]?.trim() ?? "";
|
|
764
|
-
const referer = req.headers.get("referer");
|
|
765
|
-
const country = opts.captureCountry ? req.headers.get("x-vercel-ip-country") || req.headers.get("cf-ipcountry") || req.headers.get("x-country-code") || null : null;
|
|
766
|
-
const geo = opts.captureGeo ? extractGeo(req) : null;
|
|
767
|
-
const classification = classifyRequest(req);
|
|
768
|
-
const verification = opts.verifyIdentity ? verifyBotIdentity(userAgent, ip) : null;
|
|
769
|
-
const event = {
|
|
770
|
-
event: opts.eventName ?? "agent_visit",
|
|
771
|
-
distinctId: hashId(`${ip}:${userAgent}`),
|
|
772
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
773
|
-
properties: {
|
|
774
|
-
$process_person_profile: false,
|
|
775
|
-
$current_url: origin ? `${origin}${pathname}` : pathname,
|
|
776
|
-
path: pathname,
|
|
777
|
-
method: req.method,
|
|
778
|
-
...opts.captureCountry ? { country_code: country } : {},
|
|
779
|
-
...geo ?? {},
|
|
780
|
-
...opts.captureIp ? { client_ip: ip || null } : {},
|
|
781
|
-
user_agent: userAgent,
|
|
782
|
-
is_ai_bot: classification.isAiBot,
|
|
783
|
-
bot_name: classification.label,
|
|
784
|
-
ua_category: classification.kind,
|
|
785
|
-
coding_agent_hint: classification.codingAgentHint,
|
|
786
|
-
headless_score: classification.headless?.score ?? 0,
|
|
787
|
-
headless_likely: classification.headless?.likely ?? false,
|
|
788
|
-
...verification ? {
|
|
789
|
-
bot_verified: verification.verified,
|
|
790
|
-
bot_verification: verification.verdict,
|
|
791
|
-
...verification.reason ? { bot_verification_reason: verification.reason } : {}
|
|
792
|
-
} : {},
|
|
793
|
-
referer,
|
|
794
|
-
source: opts.source ?? null,
|
|
795
|
-
...opts.properties
|
|
796
|
-
}
|
|
797
|
-
};
|
|
798
|
-
try {
|
|
799
|
-
await opts.analytics.capture(event);
|
|
800
|
-
} catch {
|
|
801
|
-
}
|
|
802
|
-
}
|
|
803
|
-
function extractGeo(req) {
|
|
804
|
-
const decode = (v) => {
|
|
805
|
-
if (!v) return "";
|
|
806
|
-
try {
|
|
807
|
-
return decodeURIComponent(v);
|
|
808
|
-
} catch {
|
|
809
|
-
return v;
|
|
810
|
-
}
|
|
811
|
-
};
|
|
812
|
-
const fields = [
|
|
813
|
-
["region", decode(req.headers.get("x-vercel-ip-country-region"))],
|
|
814
|
-
["city", decode(req.headers.get("x-vercel-ip-city"))],
|
|
815
|
-
["latitude", req.headers.get("x-vercel-ip-latitude") ?? ""],
|
|
816
|
-
["longitude", req.headers.get("x-vercel-ip-longitude") ?? ""],
|
|
817
|
-
["timezone", req.headers.get("x-vercel-ip-timezone") ?? ""]
|
|
818
|
-
];
|
|
819
|
-
const out = {};
|
|
820
|
-
for (const [k, v] of fields) if (v) out[k] = v;
|
|
821
|
-
return out;
|
|
822
|
-
}
|
|
823
|
-
|
|
824
|
-
// src/adapters/posthog.ts
|
|
825
|
-
function posthogAnalytics(config) {
|
|
826
|
-
const hostRaw = config.host ?? "https://us.i.posthog.com";
|
|
827
|
-
const base = (/^https?:\/\//.test(hostRaw) ? hostRaw : `https://${hostRaw}`).replace(/\/$/, "");
|
|
828
|
-
const path = (config.path ?? "/i/v0/e/").replace(/^(?!\/)/, "/");
|
|
829
|
-
const endpoint = `${base}${path}`;
|
|
830
|
-
const fetchImpl = config.fetchImpl ?? fetch;
|
|
831
|
-
return {
|
|
832
|
-
async capture(event) {
|
|
833
|
-
const payload = {
|
|
834
|
-
api_key: config.apiKey,
|
|
835
|
-
event: event.event,
|
|
836
|
-
distinct_id: event.distinctId,
|
|
837
|
-
timestamp: event.timestamp,
|
|
838
|
-
properties: event.properties
|
|
839
|
-
};
|
|
840
|
-
await fetchImpl(endpoint, {
|
|
841
|
-
method: "POST",
|
|
842
|
-
headers: { "Content-Type": "application/json" },
|
|
843
|
-
body: JSON.stringify(payload),
|
|
844
|
-
keepalive: true
|
|
845
|
-
});
|
|
846
|
-
}
|
|
847
|
-
};
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
// src/adapters/webhook.ts
|
|
851
|
-
function webhookAnalytics(config) {
|
|
852
|
-
const fetchImpl = config.fetchImpl ?? fetch;
|
|
853
|
-
const transform = config.transform ?? ((e) => e);
|
|
854
|
-
return {
|
|
855
|
-
async capture(event) {
|
|
856
|
-
await fetchImpl(config.url, {
|
|
857
|
-
method: "POST",
|
|
858
|
-
headers: {
|
|
859
|
-
"Content-Type": "application/json",
|
|
860
|
-
...config.headers ?? {}
|
|
861
|
-
},
|
|
862
|
-
body: JSON.stringify(transform(event)),
|
|
863
|
-
keepalive: true
|
|
864
|
-
});
|
|
865
|
-
}
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
// src/adapters/custom.ts
|
|
870
|
-
function customAnalytics(capture) {
|
|
871
|
-
return { capture };
|
|
872
|
-
}
|
|
873
|
-
|
|
874
|
-
export { AI_BOT_PATTERN, BOT_IP_RANGES, BOT_RANGES_CAPTURED_AT, HTTP_CLIENT_PATTERN, VERIFIABLE_VENDORS, classifyAgent, classifyRequest, clientIpFromRequest, compileRanges, customAnalytics, detectHeadless, firstUserAgentProduct, hashId, ipInCidr, ipInRanges, isAiBot, isHttpClient, parseBotName, posthogAnalytics, trackVisit, verifiableVendors, verifyBotIdentity, verifyRequest, webhookAnalytics };
|
|
875
|
-
//# sourceMappingURL=index.js.map
|
|
1
|
+
var C=/ClaudeBot|Claude-User|Claude-SearchBot|Claude-Web|Anthropic|GPTBot|ChatGPT-User|OAI-SearchBot|PerplexityBot|Perplexity-User|Google-Extended|Google-CloudVertexBot|Google-Agent|GoogleAgent-Mariner|Gemini-Deep-Research|Applebot|cohere|Bytespider|CCBot|Amazonbot|Amzn-SearchBot|NovaAct|AzureAI-SearchBot|Meta-ExternalAgent|meta-externalfetcher|meta-webindexer|FacebookBot|DuckAssistBot|MistralAI-User|YouBot|AI2Bot|Diffbot|DeepSeek|PanguBot|Webzio-Extended|omgili|Timpibot|Grok|Manus-User|quillbot|MyCentralAIScraperBot|Cursor|Windsurf/i,T=/axios\/|curl\/|(?:^|[\s(])got(?:\/|[\s(])|\bcolly\b|Electron\/|node-fetch\/|python-requests\/|Go-http-client\/|okhttp\/|aiohttp\/|Deno\//i;function d(t){return t?C.test(t):false}function g(t){return t?T.test(t):false}function S(t){if(!t||typeof t!="string")return "Other";let e=t.toLowerCase();return e.includes("chatgpt-user")||e.includes("gptbot")||e.includes("oai-searchbot")||e.includes("openai")?"ChatGPT":e.includes("claudebot")||e.includes("claude-user")||e.includes("claude-searchbot")||e.includes("claude-web")||e.includes("anthropic")?"Claude":e.includes("perplexitybot")||e.includes("perplexity-user")?"Perplexity":e.includes("ccbot")?"Common Crawl":e.includes("google-extended")||e.includes("googlebot")||e.includes("google-cloudvertexbot")||e.includes("google-agent")||e.includes("googleagent-mariner")||e.includes("gemini-deep-research")?"Google":e.includes("applebot")?"Apple":e.includes("bingbot")?"Bing":e.includes("bytespider")?"Bytespider":e.includes("amazonbot")||e.includes("amzn-searchbot")||e.includes("novaact")?"Amazon":e.includes("meta-externalagent")||e.includes("meta-externalfetcher")||e.includes("meta-webindexer")||e.includes("facebookbot")?"Meta":e.includes("mistralai-user")?"Mistral":e.includes("duckassistbot")?"DuckDuckGo":e.includes("youbot")?"You.com":e.includes("diffbot")?"Diffbot":e.includes("ai2bot")?"AI2":e.includes("cohere")?"Cohere":e.includes("cursor")?"Cursor":e.includes("windsurf")?"Windsurf":e.includes("deepseek")?"DeepSeek":e.includes("pangubot")?"Huawei":e.includes("webzio")||e.includes("omgili")?"Webz.io":e.includes("timpibot")?"Timpi":e.includes("grok")||e.includes("xai-")?"xAI":e.includes("manus-user")?"Manus":e.includes("quillbot")?"QuillBot":e.includes("azureai-searchbot")?"Microsoft":e.includes("mycentralaiscraperbot")?"MyCentralAI":e.includes("petalbot")?"PetalBot":e.includes("ahrefsbot")?"Ahrefs":e.includes("semrushbot")?"Semrush":e.includes("mj12bot")?"Majestic":e.includes("dotbot")||e.includes("rogerbot")?"Moz":e.includes("screaming frog")?"Screaming Frog":e.includes("sitebulb")?"Sitebulb":e.includes("linkfluence")?"Linkfluence":e.includes("dataforseo")?"DataForSEO":e.includes("serpstatbot")?"Serpstat":e.includes("uptimerobot")?"UptimeRobot":e.includes("pingdom")?"Pingdom":e.includes("statuscake")?"StatusCake":e.includes("newrelicpinger")?"New Relic":e.includes("datadogagent")||e.includes("datadog")?"Datadog":e.includes("slackbot")?"Slack":e.includes("twitterbot")?"Twitter":e.includes("linkedinbot")?"LinkedIn":e.includes("discordbot")?"Discord":e.includes("telegrambot")?"Telegram":e.includes("whatsapp")?"WhatsApp":e.includes("linkupbot")?"Linkup":e.includes("sogou")?"Sogou":e.includes("yandexbot")?"Yandex":e.includes("baiduspider")?"Baidu":e.includes("facebookexternalhit")?"Facebook":e.includes("com.apple.webkit")?"Apple URL Preview":e.includes("ohdear")?"Oh Dear":e.includes("scrapy")?"Scrapy":e.includes("headlesschrome")?"Headless Chrome":e.includes("phantomjs")?"PhantomJS":e.includes("wget")?"wget":e.includes("httpie")?"HTTPie":e.includes("guzzlehttp")?"Guzzle":e.includes("electron/")?"Electron":/curl\//.test(e)?"curl":/axios\//.test(e)?"axios":/(?:^|[\s(])got(?:\/|[\s(])/.test(e)?"got":/\bcolly\b/.test(e)?"colly":/node-fetch\//.test(e)?"node-fetch":/python-requests\//.test(e)?"python-requests":/go-http-client\//.test(e)?"Go http client":/okhttp\//.test(e)?"OkHttp":/aiohttp\//.test(e)?"aiohttp":/deno\//.test(e)?"Deno":e.includes("mozilla")||e.includes("chrome")||e.includes("safari")||e.includes("firefox")?"Browser":"Other"}function M(t){if(!t||typeof t!="string")return "Other";let e=t.match(/compatible;\s*([^/;\s]+)(?:\/[^\s;]*)?/i);return e&&e[1]?e[1].trim():t.trim().split("/")[0]?.trim().split(/\s+/)[0]?.trim()||"Other"}function h(t){let e=[],n=(t.headers.get("user-agent")||"").toLowerCase();if(!(n.includes("mozilla")||n.includes("chrome")||n.includes("safari")||n.includes("firefox")))return {score:0,signals:[],likely:false};t.headers.get("accept-language")||e.push("missing-accept-language"),t.headers.get("sec-fetch-mode")||e.push("missing-sec-fetch-mode");let r=t.headers.get("sec-ch-ua");r?r.toLowerCase().includes("headlesschrome")&&e.push("headless-chrome-hint"):e.push("missing-sec-ch-ua");let i=t.headers.get("accept")||"";(!i||i==="*/*")&&e.push("missing-or-bare-accept"),(t.headers.get("connection")||"").toLowerCase()==="close"&&e.push("connection-close");let o=e.length;return {score:o,signals:e,likely:o>=2}}function v(t){let e=S(t),n=d(t),s=g(t),r;return n?r="declared-crawler":s?r="coding-agent-hint":e==="Browser"?r="browser":r="other",{kind:r,label:e,isAiBot:n,codingAgentHint:s}}function m(t){let e=t.headers.get("user-agent")||"",n=v(e),s=h(t),r=n.kind,i=n.label;return r==="browser"&&s.likely&&(r="headless-likely",i="Headless"),{...n,kind:r,label:i,headless:s}}var f=class extends Error{constructor(e){super(e),this.name="HashSecretError";}};function y(){let t=globalThis.crypto;if(!t?.subtle)throw new f("Web Crypto is unavailable. agent-analytics requires Node >= 20, or any runtime exposing globalThis.crypto.subtle (Vercel Edge, Cloudflare Workers, Deno, browsers).");return t.subtle}var B=new Map;function z(t){let e=B.get(t);return e||(e=y().importKey("raw",new TextEncoder().encode(t),{name:"HMAC",hash:"SHA-256"},false,["sign"]),B.set(t,e)),e}async function b(t,e){if(typeof e!="string"||e.length===0)throw new f("hashId requires a non-empty secret");let n=await y().sign("HMAC",await z(e),new TextEncoder().encode(t)),s=new Uint8Array(n,0,8),r="";for(let i of s)r+=i.toString(16).padStart(2,"0");return "anon_"+r}function A(){let t=new Uint8Array(32);y(),globalThis.crypto.getRandomValues(t);let e="";for(let n of t)e+=n.toString(16).padStart(2,"0");return e}var x,E=false;function N(t){if(t)return t;let e=typeof process<"u"?process.env?.AGENT_ANALYTICS_ID_SECRET:void 0;return e||(x||(x=A(),E||(E=true,console.warn("[agent-analytics] No idSecret or AGENT_ANALYTICS_ID_SECRET set. Using a per-instance random secret: distinctIds will not correlate across instances or deploys."))),x)}async function G(t,e){let n=t.headers.get("user-agent")||"",s=e.onlyBots??false,r=e.skipBrowsers??false;if(!(s&&!d(n))&&!(r&&!d(n)&&!g(n)&&!h(t).likely))try{let i="/",o="";try{let w=new URL(t.url);i=w.pathname,o=w.origin;}catch{i=t.url||"/";}let p=e.origin??o,k=(t.headers.get("x-forwarded-for")||"").split(",")[0]?.trim()??"",_=t.headers.get("referer"),I=e.captureCountry&&(t.headers.get("x-vercel-ip-country")||t.headers.get("cf-ipcountry")||t.headers.get("x-country-code"))||null,P=e.captureGeo?O(t):null,a=m(t),l=e.verify?e.verify(t):null,H=a.kind==="headless-likely"||a.kind==="browser",R=await b(`${k}:${n}`,N(e.idSecret)),D={event:e.eventName??"agent_visit",distinctId:R,timestamp:new Date().toISOString(),properties:{...e.properties,$process_person_profile:!1,$current_url:p?`${p}${i}`:i,path:i,method:t.method,...e.captureCountry?{country_code:I}:{},...P??{},...e.captureIp?{client_ip:k||null}:{},user_agent:n,is_ai_bot:a.isAiBot,bot_name:a.label,ua_category:a.kind,coding_agent_hint:a.codingAgentHint,...H?{headless_score:a.headless?.score??0,headless_likely:a.headless?.likely??!1}:{},...l?{bot_verified:l.verified,bot_verification:l.verdict,...l.reason?{bot_verification_reason:l.reason}:{}}:{},referer:_,source:e.source??null}};await e.analytics.capture(D);}catch(i){e.onError?.(i instanceof Error?i:new Error(String(i)));}}function O(t){let e=r=>{if(!r)return "";try{return decodeURIComponent(r)}catch{return r}},n=[["region",e(t.headers.get("x-vercel-ip-country-region"))],["city",e(t.headers.get("x-vercel-ip-city"))],["latitude",t.headers.get("x-vercel-ip-latitude")??""],["longitude",t.headers.get("x-vercel-ip-longitude")??""],["timezone",t.headers.get("x-vercel-ip-timezone")??""]],s={};for(let[r,i]of n)i&&(s[r]=i);return s}var c=class extends Error{status;body;constructor(e,n,s){super(e),this.name="CaptureTransportError",this.status=n,this.body=s;}};function U(t){let e=t.host??"https://us.i.posthog.com",n=(/^https?:\/\//.test(e)?e:`https://${e}`).replace(/\/$/,""),s=(t.path??"/i/v0/e/").replace(/^(?!\/)/,"/"),r=`${n}${s}`,i=t.fetchImpl??fetch;return {async capture(o){let p={api_key:t.apiKey,event:o.event,distinct_id:o.distinctId,timestamp:o.timestamp,properties:o.properties},u=await i(r,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(p),keepalive:true,signal:AbortSignal.timeout(t.timeoutMs??3e3)});if(!u.ok)throw new c(`PostHog capture failed: ${u.status} ${u.statusText}`,u.status,await u.text().catch(()=>{}))}}}function L(t){let e=t.fetchImpl??fetch,n=t.transform??(s=>s);return {async capture(s){let r=await e(t.url,{method:"POST",headers:{"Content-Type":"application/json",...t.headers??{}},body:JSON.stringify(n(s)),keepalive:true,signal:AbortSignal.timeout(t.timeoutMs??3e3)});if(!r.ok)throw new c(`Webhook capture failed: ${r.status} ${r.statusText}`,r.status,await r.text().catch(()=>{}))}}}function $(t){return {capture:t}}
|
|
2
|
+
export{C as AI_BOT_PATTERN,c as CaptureTransportError,T as HTTP_CLIENT_PATTERN,f as HashSecretError,v as classifyAgent,m as classifyRequest,$ as customAnalytics,h as detectHeadless,M as firstUserAgentProduct,b as hashId,d as isAiBot,g as isHttpClient,S as parseBotName,U as posthogAnalytics,A as randomSecret,G as trackVisit,L as webhookAnalytics};//# sourceMappingURL=index.js.map
|
|
876
3
|
//# sourceMappingURL=index.js.map
|