@traceten/ai-crawl 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.
- package/CHANGELOG.md +17 -0
- package/LICENSE +21 -0
- package/README.md +260 -0
- package/dist/adapters/cloudflare-pages.d.ts +42 -0
- package/dist/adapters/cloudflare-pages.d.ts.map +1 -0
- package/dist/adapters/cloudflare-pages.js +46 -0
- package/dist/adapters/cloudflare-pages.js.map +1 -0
- package/dist/adapters/cloudflare-workers.d.ts +41 -0
- package/dist/adapters/cloudflare-workers.d.ts.map +1 -0
- package/dist/adapters/cloudflare-workers.js +49 -0
- package/dist/adapters/cloudflare-workers.js.map +1 -0
- package/dist/adapters/express.d.ts +49 -0
- package/dist/adapters/express.d.ts.map +1 -0
- package/dist/adapters/express.js +91 -0
- package/dist/adapters/express.js.map +1 -0
- package/dist/adapters/hono.d.ts +48 -0
- package/dist/adapters/hono.d.ts.map +1 -0
- package/dist/adapters/hono.js +64 -0
- package/dist/adapters/hono.js.map +1 -0
- package/dist/adapters/next.d.ts +41 -0
- package/dist/adapters/next.d.ts.map +1 -0
- package/dist/adapters/next.js +70 -0
- package/dist/adapters/next.js.map +1 -0
- package/dist/config.d.ts +21 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +99 -0
- package/dist/config.js.map +1 -0
- package/dist/crawlers.d.ts +68 -0
- package/dist/crawlers.d.ts.map +1 -0
- package/dist/crawlers.js +248 -0
- package/dist/crawlers.js.map +1 -0
- package/dist/filter.d.ts +33 -0
- package/dist/filter.d.ts.map +1 -0
- package/dist/filter.js +169 -0
- package/dist/filter.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/ip.d.ts +35 -0
- package/dist/ip.d.ts.map +1 -0
- package/dist/ip.js +109 -0
- package/dist/ip.js.map +1 -0
- package/dist/matcher.d.ts +44 -0
- package/dist/matcher.d.ts.map +1 -0
- package/dist/matcher.js +111 -0
- package/dist/matcher.js.map +1 -0
- package/dist/report.d.ts +43 -0
- package/dist/report.d.ts.map +1 -0
- package/dist/report.js +116 -0
- package/dist/report.js.map +1 -0
- package/dist/track.d.ts +30 -0
- package/dist/track.d.ts.map +1 -0
- package/dist/track.js +96 -0
- package/dist/track.js.map +1 -0
- package/dist/types.d.ts +184 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +11 -0
- package/dist/types.js.map +1 -0
- package/package.json +87 -0
- package/src/adapters/cloudflare-pages.ts +64 -0
- package/src/adapters/cloudflare-workers.ts +70 -0
- package/src/adapters/express.ts +113 -0
- package/src/adapters/hono.ts +89 -0
- package/src/adapters/next.ts +87 -0
- package/src/config.ts +127 -0
- package/src/crawlers.ts +269 -0
- package/src/filter.ts +178 -0
- package/src/index.ts +46 -0
- package/src/ip.ts +112 -0
- package/src/matcher.ts +119 -0
- package/src/report.ts +149 -0
- package/src/track.ts +117 -0
- package/src/types.ts +190 -0
package/dist/crawlers.js
ADDED
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local crawler token data — a deliberately-loose COST FILTER, not the source
|
|
3
|
+
* of truth. Traceten's server-side crawler registry decides provider,
|
|
4
|
+
* category, verification and confidence; this list only decides "worth
|
|
5
|
+
* reporting". Keep it small and keep every token anchored against current
|
|
6
|
+
* vendor documentation.
|
|
7
|
+
*
|
|
8
|
+
* Recent reconciliation notes, kept because they explain non-obvious entries:
|
|
9
|
+
* - Deleted `GoogleAgent` (a hyphenation typo of the real `Google-Agent`,
|
|
10
|
+
* which stays), `FacebookBot`, Microsoft `Copilot` and `AliyunBot` — no
|
|
11
|
+
* vendor documents any of them.
|
|
12
|
+
* - Added `Google-GeminiNotebook`, Google's 2026-07-16 rename of
|
|
13
|
+
* `Google-NotebookLM`; the old token is honoured only until August 2026.
|
|
14
|
+
* BOTH are listed on purpose — stale traffic still carries the old one.
|
|
15
|
+
* - Added `MistralAI-Training`, documented on Mistral's crawler page.
|
|
16
|
+
* - Recategorised `GoogleOther` (training → ai_crawler; Google states it
|
|
17
|
+
* "doesn't affect any specific product") and `Bytespider` (training →
|
|
18
|
+
* search_index; ByteDance documents only a Toutiao Search index crawler).
|
|
19
|
+
*
|
|
20
|
+
* DELIBERATE EXCLUSIONS: `Google-Extended` and `Applebot-Extended` are
|
|
21
|
+
* robots.txt control tokens, not crawlers — they never appear as a live
|
|
22
|
+
* user agent, so listing them here would be dead code that can never fire.
|
|
23
|
+
*
|
|
24
|
+
* ⚠️ THE EXCLUSION ONLY ACTUALLY HOLDS FOR `Google-Extended`. Nothing matches
|
|
25
|
+
* it, so it is never reported. `Applebot-Extended` IS reported, by two
|
|
26
|
+
* independent paths:
|
|
27
|
+
* 1. Apple's UA strings carry `+http://www.apple.com/go/applebot`, whose
|
|
28
|
+
* `applebot` substring is a TIER-1 exact token match (anchored between
|
|
29
|
+
* `/` and `)`), so the report lands as agent `Applebot`, training.
|
|
30
|
+
* 2. The bare string `Applebot-Extended` hits the tier-2 alias
|
|
31
|
+
* `{ alias: "applebot" }` below, because prefix anchoring accepts a
|
|
32
|
+
* trailing `-`. That yields provider `apple`, agent null, `ai_crawler`.
|
|
33
|
+
* Neither is a bug — attributing an Apple fetch to Apple is right, and this
|
|
34
|
+
* file is a cost filter, not the classifier. Do NOT write, in code comments
|
|
35
|
+
* or in customer copy, that these tokens are "never reported" — only
|
|
36
|
+
* `Google-Extended` qualifies.
|
|
37
|
+
*
|
|
38
|
+
* All tokens are stored lowercase; matching lowercases the UA once.
|
|
39
|
+
*/
|
|
40
|
+
/** Tier 1 — exact agent tokens. First match wins. */
|
|
41
|
+
export const AGENT_TOKENS = [
|
|
42
|
+
// ── answer_fetch · user-triggered live fetches ─────────────────────────
|
|
43
|
+
{ token: "chatgpt-user", agent: "ChatGPT-User", provider: "openai", category: "answer_fetch" },
|
|
44
|
+
{ token: "claude-user", agent: "Claude-User", provider: "anthropic", category: "answer_fetch" },
|
|
45
|
+
{
|
|
46
|
+
token: "perplexity-user",
|
|
47
|
+
agent: "Perplexity-User",
|
|
48
|
+
provider: "perplexity",
|
|
49
|
+
category: "answer_fetch",
|
|
50
|
+
},
|
|
51
|
+
{ token: "google-agent", agent: "Google-Agent", provider: "google", category: "answer_fetch" },
|
|
52
|
+
{
|
|
53
|
+
token: "google-gemininotebook",
|
|
54
|
+
agent: "Google-GeminiNotebook",
|
|
55
|
+
provider: "google",
|
|
56
|
+
category: "answer_fetch",
|
|
57
|
+
},
|
|
58
|
+
// Superseded by Google-GeminiNotebook on 2026-07-16, honoured until August
|
|
59
|
+
// 2026. Kept: traffic already in flight still carries it.
|
|
60
|
+
{
|
|
61
|
+
token: "google-notebooklm",
|
|
62
|
+
agent: "Google-NotebookLM",
|
|
63
|
+
provider: "google",
|
|
64
|
+
category: "answer_fetch",
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
token: "google-read-aloud",
|
|
68
|
+
agent: "Google-Read-Aloud",
|
|
69
|
+
provider: "google",
|
|
70
|
+
category: "answer_fetch",
|
|
71
|
+
},
|
|
72
|
+
{
|
|
73
|
+
token: "mistralai-user",
|
|
74
|
+
agent: "MistralAI-User",
|
|
75
|
+
provider: "mistral",
|
|
76
|
+
category: "answer_fetch",
|
|
77
|
+
},
|
|
78
|
+
{ token: "amzn-user", agent: "Amzn-User", provider: "amazon", category: "answer_fetch" },
|
|
79
|
+
{
|
|
80
|
+
token: "duckassistbot",
|
|
81
|
+
agent: "DuckAssistBot",
|
|
82
|
+
provider: "duckduckgo",
|
|
83
|
+
category: "answer_fetch",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
token: "meta-externalfetcher",
|
|
87
|
+
agent: "meta-externalfetcher",
|
|
88
|
+
provider: "meta",
|
|
89
|
+
category: "answer_fetch",
|
|
90
|
+
},
|
|
91
|
+
{ token: "kimi-user", agent: "Kimi-User", provider: "moonshot", category: "answer_fetch" },
|
|
92
|
+
{ token: "xai-searchbot", agent: "xAI-SearchBot", provider: "xai", category: "answer_fetch" },
|
|
93
|
+
{ token: "grok-deepsearch", agent: "Grok-DeepSearch", provider: "xai", category: "answer_fetch" },
|
|
94
|
+
{ token: "qwen-user", agent: "Qwen-User", provider: "alibaba", category: "answer_fetch" },
|
|
95
|
+
// ── search_index · AI search index crawlers ────────────────────────────
|
|
96
|
+
{ token: "oai-searchbot", agent: "OAI-SearchBot", provider: "openai", category: "search_index" },
|
|
97
|
+
{
|
|
98
|
+
token: "claude-searchbot",
|
|
99
|
+
agent: "Claude-SearchBot",
|
|
100
|
+
provider: "anthropic",
|
|
101
|
+
category: "search_index",
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
token: "perplexitybot",
|
|
105
|
+
agent: "PerplexityBot",
|
|
106
|
+
provider: "perplexity",
|
|
107
|
+
category: "search_index",
|
|
108
|
+
},
|
|
109
|
+
{ token: "googlebot", agent: "Googlebot", provider: "google", category: "search_index" },
|
|
110
|
+
{
|
|
111
|
+
token: "google-inspectiontool",
|
|
112
|
+
agent: "Google-InspectionTool",
|
|
113
|
+
provider: "google",
|
|
114
|
+
category: "search_index",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
token: "mistralai-index",
|
|
118
|
+
agent: "MistralAI-Index",
|
|
119
|
+
provider: "mistral",
|
|
120
|
+
category: "search_index",
|
|
121
|
+
},
|
|
122
|
+
{ token: "bingbot", agent: "Bingbot", provider: "microsoft", category: "search_index" },
|
|
123
|
+
{ token: "msnbot", agent: "msnbot", provider: "microsoft", category: "search_index" },
|
|
124
|
+
{
|
|
125
|
+
token: "amzn-searchbot",
|
|
126
|
+
agent: "Amzn-SearchBot",
|
|
127
|
+
provider: "amazon",
|
|
128
|
+
category: "search_index",
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
token: "meta-webindexer",
|
|
132
|
+
agent: "meta-webindexer",
|
|
133
|
+
provider: "meta",
|
|
134
|
+
category: "search_index",
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
token: "kimi-searchbot",
|
|
138
|
+
agent: "Kimi-SearchBot",
|
|
139
|
+
provider: "moonshot",
|
|
140
|
+
category: "search_index",
|
|
141
|
+
},
|
|
142
|
+
{ token: "tiktokspider", agent: "TikTokSpider", provider: "bytedance", category: "search_index" },
|
|
143
|
+
{ token: "bytespider", agent: "Bytespider", provider: "bytedance", category: "search_index" },
|
|
144
|
+
{ token: "baiduspider", agent: "Baiduspider", provider: "baidu", category: "search_index" },
|
|
145
|
+
{ token: "youbot", agent: "YouBot", provider: "youcom", category: "search_index" },
|
|
146
|
+
// ── training · corpus crawlers ─────────────────────────────────────────
|
|
147
|
+
{ token: "gptbot", agent: "GPTBot", provider: "openai", category: "training" },
|
|
148
|
+
{ token: "claudebot", agent: "ClaudeBot", provider: "anthropic", category: "training" },
|
|
149
|
+
{
|
|
150
|
+
token: "mistralai-training",
|
|
151
|
+
agent: "MistralAI-Training",
|
|
152
|
+
provider: "mistral",
|
|
153
|
+
category: "training",
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
token: "google-cloudvertexbot",
|
|
157
|
+
agent: "Google-CloudVertexBot",
|
|
158
|
+
provider: "google",
|
|
159
|
+
category: "training",
|
|
160
|
+
},
|
|
161
|
+
{ token: "applebot", agent: "Applebot", provider: "apple", category: "training" },
|
|
162
|
+
{ token: "amazonbot", agent: "Amazonbot", provider: "amazon", category: "training" },
|
|
163
|
+
{
|
|
164
|
+
token: "meta-externalagent",
|
|
165
|
+
agent: "meta-externalagent",
|
|
166
|
+
provider: "meta",
|
|
167
|
+
category: "training",
|
|
168
|
+
},
|
|
169
|
+
{ token: "kimibot", agent: "KimiBot", provider: "moonshot", category: "training" },
|
|
170
|
+
{ token: "ccbot", agent: "CCBot", provider: "commoncrawl", category: "training" },
|
|
171
|
+
{ token: "erniebot", agent: "ERNIEBot", provider: "baidu", category: "training" },
|
|
172
|
+
{ token: "qwenbot", agent: "QwenBot", provider: "alibaba", category: "training" },
|
|
173
|
+
{ token: "chatglm-spider", agent: "ChatGLM-Spider", provider: "zhipu", category: "training" },
|
|
174
|
+
{ token: "deepseekbot", agent: "DeepSeekBot", provider: "deepseek", category: "training" },
|
|
175
|
+
{ token: "cohere-ai", agent: "cohere-ai", provider: "cohere", category: "training" },
|
|
176
|
+
{
|
|
177
|
+
token: "cohere-training-data-crawler",
|
|
178
|
+
agent: "cohere-training-data-crawler",
|
|
179
|
+
provider: "cohere",
|
|
180
|
+
category: "training",
|
|
181
|
+
},
|
|
182
|
+
{ token: "ai2bot", agent: "AI2Bot", provider: "allenai", category: "training" },
|
|
183
|
+
// ── ai_crawler · AI-adjacent, uncategorised ────────────────────────────
|
|
184
|
+
{ token: "googleother", agent: "GoogleOther", provider: "google", category: "ai_crawler" },
|
|
185
|
+
{
|
|
186
|
+
token: "meta-externalads",
|
|
187
|
+
agent: "meta-externalads",
|
|
188
|
+
provider: "meta",
|
|
189
|
+
category: "ai_crawler",
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
token: "facebookexternalhit",
|
|
193
|
+
agent: "facebookexternalhit",
|
|
194
|
+
provider: "meta",
|
|
195
|
+
category: "ai_crawler",
|
|
196
|
+
},
|
|
197
|
+
{ token: "oai-adsbot", agent: "OAI-AdsBot", provider: "openai", category: "ai_crawler" },
|
|
198
|
+
{ token: "grokbot", agent: "GrokBot", provider: "xai", category: "ai_crawler" },
|
|
199
|
+
{ token: "xai-bot", agent: "xAI-Bot", provider: "xai", category: "ai_crawler" },
|
|
200
|
+
{ token: "xai-grok", agent: "xAI-Grok", provider: "xai", category: "ai_crawler" },
|
|
201
|
+
{ token: "xai-web-crawler", agent: "xAI-Web-Crawler", provider: "xai", category: "ai_crawler" },
|
|
202
|
+
{ token: "grok", agent: "Grok", provider: "xai", category: "ai_crawler" },
|
|
203
|
+
{ token: "doubaobot", agent: "Doubaobot", provider: "bytedance", category: "ai_crawler" },
|
|
204
|
+
{ token: "yiyanbot", agent: "YiyanBot", provider: "baidu", category: "ai_crawler" },
|
|
205
|
+
{ token: "tongyibot", agent: "TongyiBot", provider: "alibaba", category: "ai_crawler" },
|
|
206
|
+
];
|
|
207
|
+
/**
|
|
208
|
+
* Tier 2 — coarse provider aliases, so a NEW agent from a known vendor
|
|
209
|
+
* (e.g. a hypothetical `ChatGPT-Reader/1.0`) is still captured without a
|
|
210
|
+
* package release. Left-boundary-anchored prefix match: the alias must start
|
|
211
|
+
* at the beginning of a token (start of string or after a non-token
|
|
212
|
+
* character), which is what defeats `not-really-GPTBot`-style spoofs while
|
|
213
|
+
* still catching `Claude-NewAgent/1.0`.
|
|
214
|
+
*
|
|
215
|
+
* Tier-2 matches are reported with category `ai_crawler` locally (the server
|
|
216
|
+
* assigns the real category) and are gated by `disableOtherCrawlers`.
|
|
217
|
+
*/
|
|
218
|
+
export const PROVIDER_ALIASES = [
|
|
219
|
+
{ alias: "gptbot", provider: "openai" },
|
|
220
|
+
{ alias: "chatgpt", provider: "openai" },
|
|
221
|
+
{ alias: "oai-", provider: "openai" },
|
|
222
|
+
{ alias: "claudebot", provider: "anthropic" },
|
|
223
|
+
{ alias: "claude-", provider: "anthropic" },
|
|
224
|
+
{ alias: "anthropic", provider: "anthropic" },
|
|
225
|
+
{ alias: "perplexity", provider: "perplexity" },
|
|
226
|
+
{ alias: "mistralai", provider: "mistral" },
|
|
227
|
+
{ alias: "duckassist", provider: "duckduckgo" },
|
|
228
|
+
{ alias: "kimi", provider: "moonshot" },
|
|
229
|
+
{ alias: "grok", provider: "xai" },
|
|
230
|
+
{ alias: "xai-", provider: "xai" },
|
|
231
|
+
{ alias: "qwen", provider: "alibaba" },
|
|
232
|
+
{ alias: "tongyi", provider: "alibaba" },
|
|
233
|
+
{ alias: "bytespider", provider: "bytedance" },
|
|
234
|
+
{ alias: "doubao", provider: "bytedance" },
|
|
235
|
+
{ alias: "baiduspider", provider: "baidu" },
|
|
236
|
+
{ alias: "erniebot", provider: "baidu" },
|
|
237
|
+
{ alias: "chatglm", provider: "zhipu" },
|
|
238
|
+
{ alias: "deepseek", provider: "deepseek" },
|
|
239
|
+
{ alias: "cohere", provider: "cohere" },
|
|
240
|
+
{ alias: "ai2bot", provider: "allenai" },
|
|
241
|
+
{ alias: "ccbot", provider: "commoncrawl" },
|
|
242
|
+
{ alias: "youbot", provider: "youcom" },
|
|
243
|
+
{ alias: "meta-", provider: "meta" },
|
|
244
|
+
{ alias: "applebot", provider: "apple" },
|
|
245
|
+
{ alias: "amazonbot", provider: "amazon" },
|
|
246
|
+
{ alias: "amzn-", provider: "amazon" },
|
|
247
|
+
];
|
|
248
|
+
//# sourceMappingURL=crawlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"crawlers.js","sourceRoot":"","sources":["../src/crawlers.ts"],"names":[],"mappings":"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"}
|
package/dist/filter.d.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local pre-filter — everything here runs BEFORE any network call, so the
|
|
3
|
+
* overwhelming majority of requests (assets, API calls, browser subresource
|
|
4
|
+
* fetches) cost nothing.
|
|
5
|
+
*
|
|
6
|
+
* Order of checks:
|
|
7
|
+
* 1. Method gate (GET/HEAD by default)
|
|
8
|
+
* 2. sec-fetch-dest deny-list (browser subresource fetches; crawlers
|
|
9
|
+
* don't send the header, so absence passes)
|
|
10
|
+
* 3. Crawler-facing path re-allow — /robots.txt, /llms.txt,
|
|
11
|
+
* /llms-full.txt, *sitemap*.xml skip BOTH deny-lists below. A GPTBot
|
|
12
|
+
* hit on /llms.txt is one of the highest-signal events available and a
|
|
13
|
+
* naive `.txt` rule would eat it.
|
|
14
|
+
* 4. Path-prefix deny-list (extendable, never replaceable)
|
|
15
|
+
* 5. Static-extension deny-list (extendable, never replaceable)
|
|
16
|
+
*/
|
|
17
|
+
import type { ResolvedAiCrawlConfig } from "./types.js";
|
|
18
|
+
/** Built-in path-prefix deny-list (plain prefix match on the pathname). */
|
|
19
|
+
export declare const DEFAULT_DENY_PATH_PREFIXES: readonly string[];
|
|
20
|
+
/** Built-in static-extension deny-list (leading dot, lowercase). */
|
|
21
|
+
export declare const DEFAULT_DENY_EXTENSIONS: readonly string[];
|
|
22
|
+
/** `sec-fetch-dest` values that identify browser subresource fetches. */
|
|
23
|
+
export declare const DENY_SEC_FETCH_DESTS: readonly string[];
|
|
24
|
+
/** True for /robots.txt, /llms.txt, /llms-full.txt and *sitemap*.xml paths. */
|
|
25
|
+
export declare function isCrawlerFacingPath(pathname: string): boolean;
|
|
26
|
+
/** Extract a lowercase pathname from an absolute URL or a bare path. Never throws. */
|
|
27
|
+
export declare function extractPathname(url: string): string;
|
|
28
|
+
/**
|
|
29
|
+
* The full local pre-filter. Returns `true` when the request should proceed
|
|
30
|
+
* to UA matching. Never throws.
|
|
31
|
+
*/
|
|
32
|
+
export declare function passesPreFilter(cfg: ResolvedAiCrawlConfig, method: string | null | undefined, url: string | null | undefined, secFetchDest: string | null | undefined): boolean;
|
|
33
|
+
//# sourceMappingURL=filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAExD,2EAA2E;AAC3E,eAAO,MAAM,0BAA0B,EAAE,SAAS,MAAM,EAmBvD,CAAC;AAEF,oEAAoE;AACpE,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAsCpD,CAAC;AAEF,yEAAyE;AACzE,eAAO,MAAM,oBAAoB,EAAE,SAAS,MAAM,EAYjD,CAAC;AAQF,+EAA+E;AAC/E,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAG7D;AAED,sFAAsF;AACtF,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAkBnD;AAYD;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,qBAAqB,EAC1B,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EACjC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAC9B,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GACtC,OAAO,CA4BT"}
|
package/dist/filter.js
ADDED
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local pre-filter — everything here runs BEFORE any network call, so the
|
|
3
|
+
* overwhelming majority of requests (assets, API calls, browser subresource
|
|
4
|
+
* fetches) cost nothing.
|
|
5
|
+
*
|
|
6
|
+
* Order of checks:
|
|
7
|
+
* 1. Method gate (GET/HEAD by default)
|
|
8
|
+
* 2. sec-fetch-dest deny-list (browser subresource fetches; crawlers
|
|
9
|
+
* don't send the header, so absence passes)
|
|
10
|
+
* 3. Crawler-facing path re-allow — /robots.txt, /llms.txt,
|
|
11
|
+
* /llms-full.txt, *sitemap*.xml skip BOTH deny-lists below. A GPTBot
|
|
12
|
+
* hit on /llms.txt is one of the highest-signal events available and a
|
|
13
|
+
* naive `.txt` rule would eat it.
|
|
14
|
+
* 4. Path-prefix deny-list (extendable, never replaceable)
|
|
15
|
+
* 5. Static-extension deny-list (extendable, never replaceable)
|
|
16
|
+
*/
|
|
17
|
+
/** Built-in path-prefix deny-list (plain prefix match on the pathname). */
|
|
18
|
+
export const DEFAULT_DENY_PATH_PREFIXES = [
|
|
19
|
+
"/api",
|
|
20
|
+
"/_next",
|
|
21
|
+
"/_nuxt",
|
|
22
|
+
"/_astro",
|
|
23
|
+
"/static",
|
|
24
|
+
"/assets",
|
|
25
|
+
"/public",
|
|
26
|
+
"/images",
|
|
27
|
+
"/img",
|
|
28
|
+
"/fonts",
|
|
29
|
+
"/favicon",
|
|
30
|
+
"/build",
|
|
31
|
+
"/dist",
|
|
32
|
+
"/admin",
|
|
33
|
+
"/webhook",
|
|
34
|
+
"/webhooks",
|
|
35
|
+
"/cdn-cgi",
|
|
36
|
+
"/.well-known",
|
|
37
|
+
];
|
|
38
|
+
/** Built-in static-extension deny-list (leading dot, lowercase). */
|
|
39
|
+
export const DEFAULT_DENY_EXTENSIONS = [
|
|
40
|
+
".js",
|
|
41
|
+
".mjs",
|
|
42
|
+
".cjs",
|
|
43
|
+
".css",
|
|
44
|
+
".map",
|
|
45
|
+
".json",
|
|
46
|
+
".xml",
|
|
47
|
+
".txt",
|
|
48
|
+
".ico",
|
|
49
|
+
".png",
|
|
50
|
+
".jpg",
|
|
51
|
+
".jpeg",
|
|
52
|
+
".gif",
|
|
53
|
+
".webp",
|
|
54
|
+
".avif",
|
|
55
|
+
".svg",
|
|
56
|
+
".bmp",
|
|
57
|
+
".tiff",
|
|
58
|
+
".woff",
|
|
59
|
+
".woff2",
|
|
60
|
+
".ttf",
|
|
61
|
+
".otf",
|
|
62
|
+
".eot",
|
|
63
|
+
".mp4",
|
|
64
|
+
".webm",
|
|
65
|
+
".avi",
|
|
66
|
+
".mov",
|
|
67
|
+
".mp3",
|
|
68
|
+
".wav",
|
|
69
|
+
".ogg",
|
|
70
|
+
".flac",
|
|
71
|
+
".zip",
|
|
72
|
+
".gz",
|
|
73
|
+
".tar",
|
|
74
|
+
".rar",
|
|
75
|
+
".7z",
|
|
76
|
+
".wasm",
|
|
77
|
+
];
|
|
78
|
+
/** `sec-fetch-dest` values that identify browser subresource fetches. */
|
|
79
|
+
export const DENY_SEC_FETCH_DESTS = [
|
|
80
|
+
"audio",
|
|
81
|
+
"embed",
|
|
82
|
+
"font",
|
|
83
|
+
"image",
|
|
84
|
+
"manifest",
|
|
85
|
+
"object",
|
|
86
|
+
"script",
|
|
87
|
+
"style",
|
|
88
|
+
"track",
|
|
89
|
+
"video",
|
|
90
|
+
"worker",
|
|
91
|
+
];
|
|
92
|
+
/**
|
|
93
|
+
* Crawler-facing paths re-allowed PAST both deny-lists. Exact matches plus
|
|
94
|
+
* the `*sitemap*.xml` pattern.
|
|
95
|
+
*/
|
|
96
|
+
const CRAWLER_PATH_EXACT = ["/robots.txt", "/llms.txt", "/llms-full.txt"];
|
|
97
|
+
/** True for /robots.txt, /llms.txt, /llms-full.txt and *sitemap*.xml paths. */
|
|
98
|
+
export function isCrawlerFacingPath(pathname) {
|
|
99
|
+
if (CRAWLER_PATH_EXACT.includes(pathname))
|
|
100
|
+
return true;
|
|
101
|
+
return pathname.endsWith(".xml") && pathname.includes("sitemap");
|
|
102
|
+
}
|
|
103
|
+
/** Extract a lowercase pathname from an absolute URL or a bare path. Never throws. */
|
|
104
|
+
export function extractPathname(url) {
|
|
105
|
+
try {
|
|
106
|
+
let path = url;
|
|
107
|
+
// Strip scheme://host for absolute URLs without allocating a URL object.
|
|
108
|
+
const schemeIdx = path.indexOf("://");
|
|
109
|
+
if (schemeIdx !== -1) {
|
|
110
|
+
const pathStart = path.indexOf("/", schemeIdx + 3);
|
|
111
|
+
path = pathStart === -1 ? "/" : path.slice(pathStart);
|
|
112
|
+
}
|
|
113
|
+
const q = path.indexOf("?");
|
|
114
|
+
if (q !== -1)
|
|
115
|
+
path = path.slice(0, q);
|
|
116
|
+
const h = path.indexOf("#");
|
|
117
|
+
if (h !== -1)
|
|
118
|
+
path = path.slice(0, h);
|
|
119
|
+
if (path === "")
|
|
120
|
+
path = "/";
|
|
121
|
+
return path.toLowerCase();
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return "/";
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/** Last-segment extension (with dot, lowercase), or `null`. */
|
|
128
|
+
function pathExtension(pathname) {
|
|
129
|
+
const lastSlash = pathname.lastIndexOf("/");
|
|
130
|
+
const lastDot = pathname.lastIndexOf(".");
|
|
131
|
+
if (lastDot === -1 || lastDot < lastSlash || lastDot === pathname.length - 1) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
return pathname.slice(lastDot);
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* The full local pre-filter. Returns `true` when the request should proceed
|
|
138
|
+
* to UA matching. Never throws.
|
|
139
|
+
*/
|
|
140
|
+
export function passesPreFilter(cfg, method, url, secFetchDest) {
|
|
141
|
+
try {
|
|
142
|
+
if (!method || !url)
|
|
143
|
+
return false;
|
|
144
|
+
if (!cfg.allowedMethods.includes(method.toUpperCase()))
|
|
145
|
+
return false;
|
|
146
|
+
if (secFetchDest && DENY_SEC_FETCH_DESTS.includes(secFetchDest.toLowerCase())) {
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
const pathname = extractPathname(url);
|
|
150
|
+
// Crawler-facing paths skip both deny-lists.
|
|
151
|
+
if (isCrawlerFacingPath(pathname))
|
|
152
|
+
return true;
|
|
153
|
+
// Segment-boundary prefix match: `/api` denies `/api` and `/api/users`
|
|
154
|
+
// but NOT `/apidocs-overview`; `/public` does not eat `/publications`.
|
|
155
|
+
// A plain startsWith here silently unreports real crawls on real paths.
|
|
156
|
+
for (const prefix of cfg.denyPathPrefixes) {
|
|
157
|
+
if (pathname === prefix || pathname.startsWith(prefix + "/"))
|
|
158
|
+
return false;
|
|
159
|
+
}
|
|
160
|
+
const ext = pathExtension(pathname);
|
|
161
|
+
if (ext !== null && cfg.denyExtensions.includes(ext))
|
|
162
|
+
return false;
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../src/filter.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traceten/ai-crawl — server-side AI crawler tracking.
|
|
3
|
+
*
|
|
4
|
+
* AI crawlers (GPTBot, ClaudeBot, PerplexityBot, …) never execute
|
|
5
|
+
* JavaScript, so the browser snippet cannot see them. The observation point
|
|
6
|
+
* has to be the customer's server — this package is that observation point.
|
|
7
|
+
* It pre-filters locally, matches user agents with anchored tokens, and
|
|
8
|
+
* reports plausible AI crawls to `POST /v1/ai-crawls`. Classification,
|
|
9
|
+
* verification and confidence are decided server-side.
|
|
10
|
+
*
|
|
11
|
+
* Adapters: `@traceten/ai-crawl/next`, `/cloudflare-pages`,
|
|
12
|
+
* `/cloudflare-workers`, `/express`, `/hono`.
|
|
13
|
+
*/
|
|
14
|
+
export { AUTH_TOKEN_PREFIX, DEFAULT_ALLOWED_METHODS, DEFAULT_ENDPOINT, defineAiCrawlConfig, isResolvedConfig, } from "./config.js";
|
|
15
|
+
export { DEFAULT_DENY_EXTENSIONS, DEFAULT_DENY_PATH_PREFIXES, DENY_SEC_FETCH_DESTS, isCrawlerFacingPath, passesPreFilter, } from "./filter.js";
|
|
16
|
+
export { matchCrawler } from "./matcher.js";
|
|
17
|
+
export { resolveCrawlerIp } from "./ip.js";
|
|
18
|
+
export { REPORT_TIMEOUT_MS, buildPayload, sendReport, type SendOptions } from "./report.js";
|
|
19
|
+
export { evaluateRequest, factsFromFetchRequest, trackFacts, type FetchLikeRequest, } from "./track.js";
|
|
20
|
+
export type { AiCrawlConfig, AiCrawlDeliveryError, AiCrawlWirePayload, CrawlerCategory, CrawlerMatch, RequestFacts, ResolvedAiCrawlConfig, } from "./types.js";
|
|
21
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,WAAW,EAAE,MAAM,aAAa,CAAC;AAC5F,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,UAAU,EACV,KAAK,gBAAgB,GACtB,MAAM,YAAY,CAAC;AACpB,YAAY,EACV,aAAa,EACb,oBAAoB,EACpB,kBAAkB,EAClB,eAAe,EACf,YAAY,EACZ,YAAY,EACZ,qBAAqB,GACtB,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @traceten/ai-crawl — server-side AI crawler tracking.
|
|
3
|
+
*
|
|
4
|
+
* AI crawlers (GPTBot, ClaudeBot, PerplexityBot, …) never execute
|
|
5
|
+
* JavaScript, so the browser snippet cannot see them. The observation point
|
|
6
|
+
* has to be the customer's server — this package is that observation point.
|
|
7
|
+
* It pre-filters locally, matches user agents with anchored tokens, and
|
|
8
|
+
* reports plausible AI crawls to `POST /v1/ai-crawls`. Classification,
|
|
9
|
+
* verification and confidence are decided server-side.
|
|
10
|
+
*
|
|
11
|
+
* Adapters: `@traceten/ai-crawl/next`, `/cloudflare-pages`,
|
|
12
|
+
* `/cloudflare-workers`, `/express`, `/hono`.
|
|
13
|
+
*/
|
|
14
|
+
export { AUTH_TOKEN_PREFIX, DEFAULT_ALLOWED_METHODS, DEFAULT_ENDPOINT, defineAiCrawlConfig, isResolvedConfig, } from "./config.js";
|
|
15
|
+
export { DEFAULT_DENY_EXTENSIONS, DEFAULT_DENY_PATH_PREFIXES, DENY_SEC_FETCH_DESTS, isCrawlerFacingPath, passesPreFilter, } from "./filter.js";
|
|
16
|
+
export { matchCrawler } from "./matcher.js";
|
|
17
|
+
export { resolveCrawlerIp } from "./ip.js";
|
|
18
|
+
export { REPORT_TIMEOUT_MS, buildPayload, sendReport } from "./report.js";
|
|
19
|
+
export { evaluateRequest, factsFromFetchRequest, trackFacts, } from "./track.js";
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EACL,iBAAiB,EACjB,uBAAuB,EACvB,gBAAgB,EAChB,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,uBAAuB,EACvB,0BAA0B,EAC1B,oBAAoB,EACpB,mBAAmB,EACnB,eAAe,GAChB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,YAAY,EAAE,MAAM,cAAc,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAC3C,OAAO,EAAE,iBAAiB,EAAE,YAAY,EAAE,UAAU,EAAoB,MAAM,aAAa,CAAC;AAC5F,OAAO,EACL,eAAe,EACf,qBAAqB,EACrB,UAAU,GAEX,MAAM,YAAY,CAAC"}
|
package/dist/ip.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Crawler IP resolution.
|
|
3
|
+
*
|
|
4
|
+
* The crawler's TCP connection terminates at the CUSTOMER's server, so the
|
|
5
|
+
* crawler IP is only observable here — Traceten's edge sees the customer's
|
|
6
|
+
* origin IP, not the crawler's. This module
|
|
7
|
+
* derives the best-available value; the payload OMITS the field rather than
|
|
8
|
+
* send a wrong one.
|
|
9
|
+
*
|
|
10
|
+
* Resolution order:
|
|
11
|
+
* 1. `cf-connecting-ip` — ONLY when `trustCfConnectingIp` is set. The
|
|
12
|
+
* Cloudflare adapters set it automatically (the platform strips and
|
|
13
|
+
* rewrites the header there); anywhere else it is client-forgeable,
|
|
14
|
+
* and a forged vendor-range IP paired with a vendor UA is exactly the
|
|
15
|
+
* spoof the server-side verification exists to catch.
|
|
16
|
+
* 2. `x-forwarded-for` — ONLY when `trustProxy` is true, selecting the
|
|
17
|
+
* entry `proxyDepth` hops from the right (each trusted proxy appends
|
|
18
|
+
* one entry; anything further left is attacker-controllable)
|
|
19
|
+
* 3. Socket remote address, where the runtime exposes one
|
|
20
|
+
*/
|
|
21
|
+
import type { RequestFacts, ResolvedAiCrawlConfig } from "./types.js";
|
|
22
|
+
/** Normalise a candidate; returns `undefined` when it is not a plausible IP. */
|
|
23
|
+
export declare function normalizeIp(raw: string | null | undefined): string | undefined;
|
|
24
|
+
/**
|
|
25
|
+
* Select the client entry from an `x-forwarded-for` value given the number
|
|
26
|
+
* of trusted proxies that append to it. Returns `undefined` when the chain
|
|
27
|
+
* is shorter than `proxyDepth` (a wrong value is worse than none).
|
|
28
|
+
*/
|
|
29
|
+
export declare function ipFromForwardedFor(headerValue: string, proxyDepth: number): string | undefined;
|
|
30
|
+
/**
|
|
31
|
+
* Resolve the crawler IP from a request. Returns `undefined` when no
|
|
32
|
+
* trustworthy value is derivable. Never throws.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveCrawlerIp(cfg: ResolvedAiCrawlConfig, facts: RequestFacts): string | undefined;
|
|
35
|
+
//# sourceMappingURL=ip.d.ts.map
|
package/dist/ip.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ip.d.ts","sourceRoot":"","sources":["../src/ip.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAQtE,gFAAgF;AAChF,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,SAAS,CAqC9E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,WAAW,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAS9F;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,GAAG,EAAE,qBAAqB,EAC1B,KAAK,EAAE,YAAY,GAClB,MAAM,GAAG,SAAS,CAmBpB"}
|