@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.
Files changed (74) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/LICENSE +21 -0
  3. package/README.md +260 -0
  4. package/dist/adapters/cloudflare-pages.d.ts +42 -0
  5. package/dist/adapters/cloudflare-pages.d.ts.map +1 -0
  6. package/dist/adapters/cloudflare-pages.js +46 -0
  7. package/dist/adapters/cloudflare-pages.js.map +1 -0
  8. package/dist/adapters/cloudflare-workers.d.ts +41 -0
  9. package/dist/adapters/cloudflare-workers.d.ts.map +1 -0
  10. package/dist/adapters/cloudflare-workers.js +49 -0
  11. package/dist/adapters/cloudflare-workers.js.map +1 -0
  12. package/dist/adapters/express.d.ts +49 -0
  13. package/dist/adapters/express.d.ts.map +1 -0
  14. package/dist/adapters/express.js +91 -0
  15. package/dist/adapters/express.js.map +1 -0
  16. package/dist/adapters/hono.d.ts +48 -0
  17. package/dist/adapters/hono.d.ts.map +1 -0
  18. package/dist/adapters/hono.js +64 -0
  19. package/dist/adapters/hono.js.map +1 -0
  20. package/dist/adapters/next.d.ts +41 -0
  21. package/dist/adapters/next.d.ts.map +1 -0
  22. package/dist/adapters/next.js +70 -0
  23. package/dist/adapters/next.js.map +1 -0
  24. package/dist/config.d.ts +21 -0
  25. package/dist/config.d.ts.map +1 -0
  26. package/dist/config.js +99 -0
  27. package/dist/config.js.map +1 -0
  28. package/dist/crawlers.d.ts +68 -0
  29. package/dist/crawlers.d.ts.map +1 -0
  30. package/dist/crawlers.js +248 -0
  31. package/dist/crawlers.js.map +1 -0
  32. package/dist/filter.d.ts +33 -0
  33. package/dist/filter.d.ts.map +1 -0
  34. package/dist/filter.js +169 -0
  35. package/dist/filter.js.map +1 -0
  36. package/dist/index.d.ts +21 -0
  37. package/dist/index.d.ts.map +1 -0
  38. package/dist/index.js +20 -0
  39. package/dist/index.js.map +1 -0
  40. package/dist/ip.d.ts +35 -0
  41. package/dist/ip.d.ts.map +1 -0
  42. package/dist/ip.js +109 -0
  43. package/dist/ip.js.map +1 -0
  44. package/dist/matcher.d.ts +44 -0
  45. package/dist/matcher.d.ts.map +1 -0
  46. package/dist/matcher.js +111 -0
  47. package/dist/matcher.js.map +1 -0
  48. package/dist/report.d.ts +43 -0
  49. package/dist/report.d.ts.map +1 -0
  50. package/dist/report.js +116 -0
  51. package/dist/report.js.map +1 -0
  52. package/dist/track.d.ts +30 -0
  53. package/dist/track.d.ts.map +1 -0
  54. package/dist/track.js +96 -0
  55. package/dist/track.js.map +1 -0
  56. package/dist/types.d.ts +184 -0
  57. package/dist/types.d.ts.map +1 -0
  58. package/dist/types.js +11 -0
  59. package/dist/types.js.map +1 -0
  60. package/package.json +87 -0
  61. package/src/adapters/cloudflare-pages.ts +64 -0
  62. package/src/adapters/cloudflare-workers.ts +70 -0
  63. package/src/adapters/express.ts +113 -0
  64. package/src/adapters/hono.ts +89 -0
  65. package/src/adapters/next.ts +87 -0
  66. package/src/config.ts +127 -0
  67. package/src/crawlers.ts +269 -0
  68. package/src/filter.ts +178 -0
  69. package/src/index.ts +46 -0
  70. package/src/ip.ts +112 -0
  71. package/src/matcher.ts +119 -0
  72. package/src/report.ts +149 -0
  73. package/src/track.ts +117 -0
  74. package/src/types.ts +190 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"express.js","sourceRoot":"","sources":["../../src/adapters/express.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,48 @@
1
+ /**
2
+ * Hono adapter + generic `Request`/`Response` helper.
3
+ *
4
+ * Hono (structural types — no dependency on hono):
5
+ * ```ts
6
+ * import { aiCrawlerTracking } from "@traceten/ai-crawl/hono";
7
+ * app.use("*", aiCrawlerTracking({ siteId: "…", authToken: "tt_bot_…" }));
8
+ * ```
9
+ *
10
+ * Any framework that exposes fetch-API `Request`/`Response`:
11
+ * ```ts
12
+ * import { defineAiCrawlConfig } from "@traceten/ai-crawl";
13
+ * import { trackAICrawlerFetch } from "@traceten/ai-crawl/hono";
14
+ * const config = defineAiCrawlConfig({ … });
15
+ * // after producing the response:
16
+ * trackAICrawlerFetch(request, response, config, ctx?.waitUntil?.bind(ctx));
17
+ * ```
18
+ */
19
+ import { type FetchLikeRequest } from "../track.js";
20
+ import type { AiCrawlConfig, ResolvedAiCrawlConfig } from "../types.js";
21
+ /**
22
+ * Generic helper for any `Request`/`Response` runtime. Never throws; pass a
23
+ * `waitUntil` when the runtime has one, otherwise delivery is
24
+ * fire-and-forget.
25
+ */
26
+ export declare function trackAICrawlerFetch(request: FetchLikeRequest, response: {
27
+ status: number;
28
+ } | undefined, config: ResolvedAiCrawlConfig, waitUntil?: ((promise: Promise<unknown>) => void) | undefined): void;
29
+ /** Structural subset of Hono's `Context`. */
30
+ export interface HonoLikeContext {
31
+ req: {
32
+ raw: FetchLikeRequest;
33
+ };
34
+ res: {
35
+ status: number;
36
+ };
37
+ /** Throws on runtimes without an execution context — accessed inside try. */
38
+ executionCtx?: {
39
+ waitUntil(promise: Promise<unknown>): void;
40
+ };
41
+ }
42
+ /**
43
+ * Hono middleware factory. THROWS at construction when the config is
44
+ * invalid; the middleware itself never throws its own errors (route errors
45
+ * propagate untouched).
46
+ */
47
+ export declare function aiCrawlerTracking(config: AiCrawlConfig | ResolvedAiCrawlConfig): (c: HonoLikeContext, next: () => Promise<void>) => Promise<void>;
48
+ //# sourceMappingURL=hono.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.d.ts","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAGH,OAAO,EAAqC,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAExE;;;;GAIG;AACH,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,gBAAgB,EACzB,QAAQ,EAAE;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,GAAG,SAAS,EACxC,MAAM,EAAE,qBAAqB,EAC7B,SAAS,CAAC,EAAE,CAAC,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC,GAAG,SAAS,GAC5D,IAAI,CAYN;AAED,6CAA6C;AAC7C,MAAM,WAAW,eAAe;IAC9B,GAAG,EAAE;QAAE,GAAG,EAAE,gBAAgB,CAAA;KAAE,CAAC;IAC/B,GAAG,EAAE;QAAE,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IACxB,6EAA6E;IAC7E,YAAY,CAAC,EAAE;QAAE,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;KAAE,CAAC;CAC/D;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,aAAa,GAAG,qBAAqB,GAC5C,CAAC,CAAC,EAAE,eAAe,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CA0BlE"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * Hono adapter + generic `Request`/`Response` helper.
3
+ *
4
+ * Hono (structural types — no dependency on hono):
5
+ * ```ts
6
+ * import { aiCrawlerTracking } from "@traceten/ai-crawl/hono";
7
+ * app.use("*", aiCrawlerTracking({ siteId: "…", authToken: "tt_bot_…" }));
8
+ * ```
9
+ *
10
+ * Any framework that exposes fetch-API `Request`/`Response`:
11
+ * ```ts
12
+ * import { defineAiCrawlConfig } from "@traceten/ai-crawl";
13
+ * import { trackAICrawlerFetch } from "@traceten/ai-crawl/hono";
14
+ * const config = defineAiCrawlConfig({ … });
15
+ * // after producing the response:
16
+ * trackAICrawlerFetch(request, response, config, ctx?.waitUntil?.bind(ctx));
17
+ * ```
18
+ */
19
+ import { defineAiCrawlConfig, isResolvedConfig } from "../config.js";
20
+ import { factsFromFetchRequest, trackFacts } from "../track.js";
21
+ /**
22
+ * Generic helper for any `Request`/`Response` runtime. Never throws; pass a
23
+ * `waitUntil` when the runtime has one, otherwise delivery is
24
+ * fire-and-forget.
25
+ */
26
+ export function trackAICrawlerFetch(request, response, config, waitUntil) {
27
+ try {
28
+ if (!isResolvedConfig(config))
29
+ return; // raw config: refuse silently, never throw per-request
30
+ trackFacts(config, factsFromFetchRequest(request), response !== undefined && typeof response.status === "number" ? response.status : undefined, waitUntil);
31
+ }
32
+ catch {
33
+ /* silent by contract */
34
+ }
35
+ }
36
+ /**
37
+ * Hono middleware factory. THROWS at construction when the config is
38
+ * invalid; the middleware itself never throws its own errors (route errors
39
+ * propagate untouched).
40
+ */
41
+ export function aiCrawlerTracking(config) {
42
+ const cfg = isResolvedConfig(config) ? config : defineAiCrawlConfig(config);
43
+ return async (c, next) => {
44
+ // Route errors propagate untouched — tracking must not change behaviour.
45
+ await next();
46
+ try {
47
+ let waitUntil;
48
+ try {
49
+ const ctx = c.executionCtx; // getter throws on Node's hono server
50
+ if (ctx !== undefined && typeof ctx.waitUntil === "function") {
51
+ waitUntil = (p) => ctx.waitUntil(p);
52
+ }
53
+ }
54
+ catch {
55
+ waitUntil = undefined;
56
+ }
57
+ trackFacts(cfg, factsFromFetchRequest(c.req.raw), typeof c.res?.status === "number" ? c.res.status : undefined, waitUntil);
58
+ }
59
+ catch {
60
+ /* silent by contract */
61
+ }
62
+ };
63
+ }
64
+ //# sourceMappingURL=hono.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hono.js","sourceRoot":"","sources":["../../src/adapters/hono.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAyB,MAAM,aAAa,CAAC;AAGvF;;;;GAIG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAyB,EACzB,QAAwC,EACxC,MAA6B,EAC7B,SAA6D;IAE7D,IAAI,CAAC;QACH,IAAI,CAAC,gBAAgB,CAAC,MAAM,CAAC;YAAE,OAAO,CAAC,uDAAuD;QAC9F,UAAU,CACR,MAAM,EACN,qBAAqB,CAAC,OAAO,CAAC,EAC9B,QAAQ,KAAK,SAAS,IAAI,OAAO,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC3F,SAAS,CACV,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;AACH,CAAC;AAUD;;;;GAIG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAA6C;IAE7C,MAAM,GAAG,GAAG,gBAAgB,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC;IAE5E,OAAO,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,EAAE;QACvB,yEAAyE;QACzE,MAAM,IAAI,EAAE,CAAC;QACb,IAAI,CAAC;YACH,IAAI,SAAsD,CAAC;YAC3D,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,CAAC,CAAC,YAAY,CAAC,CAAC,sCAAsC;gBAClE,IAAI,GAAG,KAAK,SAAS,IAAI,OAAO,GAAG,CAAC,SAAS,KAAK,UAAU,EAAE,CAAC;oBAC7D,SAAS,GAAG,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC;gBACtC,CAAC;YACH,CAAC;YAAC,MAAM,CAAC;gBACP,SAAS,GAAG,SAAS,CAAC;YACxB,CAAC;YACD,UAAU,CACR,GAAG,EACH,qBAAqB,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,EAChC,OAAO,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,EAC5D,SAAS,CACV,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,wBAAwB;QAC1B,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Next.js adapter — call from `proxy.ts` (Next ≥15.5; formerly
3
+ * `middleware.ts`).
4
+ *
5
+ * ```ts
6
+ * // proxy.ts
7
+ * import { defineAiCrawlConfig } from "@traceten/ai-crawl";
8
+ * import { trackAICrawlerRequest } from "@traceten/ai-crawl/next";
9
+ *
10
+ * const config = defineAiCrawlConfig({
11
+ * siteId: process.env.TRACETEN_SITE_ID!,
12
+ * authToken: process.env.TRACETEN_CRAWL_TOKEN!,
13
+ * });
14
+ *
15
+ * export function proxy(request: NextRequest, event: NextFetchEvent) {
16
+ * trackAICrawlerRequest(request, event, config);
17
+ * return NextResponse.next();
18
+ * }
19
+ * ```
20
+ *
21
+ * Validate the config at module scope with `defineAiCrawlConfig` so a
22
+ * missing token fails the deploy, not your visitors' requests. If a raw
23
+ * (unvalidated) config reaches this function it is validated lazily; an
24
+ * invalid one logs ONE loud error and no-ops — per-request code paths never
25
+ * throw.
26
+ *
27
+ * The middleware runs before the response exists, so no `status` is sent —
28
+ * the edge treats a crawl report without a status as "served".
29
+ */
30
+ import { type FetchLikeRequest } from "../track.js";
31
+ import type { AiCrawlConfig, ResolvedAiCrawlConfig } from "../types.js";
32
+ /** Structural subset of Next's `NextFetchEvent`. */
33
+ export interface WaitUntilEvent {
34
+ waitUntil(promise: Promise<unknown>): void;
35
+ }
36
+ /**
37
+ * Report the request if it looks like an AI crawler. Never throws, never
38
+ * blocks — delivery is scheduled via `event.waitUntil` when available.
39
+ */
40
+ export declare function trackAICrawlerRequest(request: FetchLikeRequest, event: WaitUntilEvent | undefined, config: AiCrawlConfig | ResolvedAiCrawlConfig): void;
41
+ //# sourceMappingURL=next.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.d.ts","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAGH,OAAO,EAAqC,KAAK,gBAAgB,EAAE,MAAM,aAAa,CAAC;AACvF,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAExE,oDAAoD;AACpD,MAAM,WAAW,cAAc;IAC7B,SAAS,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC;CAC5C;AA4BD;;;GAGG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,gBAAgB,EACzB,KAAK,EAAE,cAAc,GAAG,SAAS,EACjC,MAAM,EAAE,aAAa,GAAG,qBAAqB,GAC5C,IAAI,CAaN"}
@@ -0,0 +1,70 @@
1
+ /**
2
+ * Next.js adapter — call from `proxy.ts` (Next ≥15.5; formerly
3
+ * `middleware.ts`).
4
+ *
5
+ * ```ts
6
+ * // proxy.ts
7
+ * import { defineAiCrawlConfig } from "@traceten/ai-crawl";
8
+ * import { trackAICrawlerRequest } from "@traceten/ai-crawl/next";
9
+ *
10
+ * const config = defineAiCrawlConfig({
11
+ * siteId: process.env.TRACETEN_SITE_ID!,
12
+ * authToken: process.env.TRACETEN_CRAWL_TOKEN!,
13
+ * });
14
+ *
15
+ * export function proxy(request: NextRequest, event: NextFetchEvent) {
16
+ * trackAICrawlerRequest(request, event, config);
17
+ * return NextResponse.next();
18
+ * }
19
+ * ```
20
+ *
21
+ * Validate the config at module scope with `defineAiCrawlConfig` so a
22
+ * missing token fails the deploy, not your visitors' requests. If a raw
23
+ * (unvalidated) config reaches this function it is validated lazily; an
24
+ * invalid one logs ONE loud error and no-ops — per-request code paths never
25
+ * throw.
26
+ *
27
+ * The middleware runs before the response exists, so no `status` is sent —
28
+ * the edge treats a crawl report without a status as "served".
29
+ */
30
+ import { defineAiCrawlConfig, isResolvedConfig } from "../config.js";
31
+ import { factsFromFetchRequest, trackFacts } from "../track.js";
32
+ const resolvedCache = new WeakMap();
33
+ /** Resolve lazily, warning loudly ONCE per config object on invalid input. */
34
+ function resolveLazily(config) {
35
+ if (isResolvedConfig(config))
36
+ return config;
37
+ if (resolvedCache.has(config))
38
+ return resolvedCache.get(config) ?? null;
39
+ let resolved = null;
40
+ try {
41
+ resolved = defineAiCrawlConfig(config);
42
+ }
43
+ catch (err) {
44
+ try {
45
+ // eslint-disable-next-line no-console
46
+ console.error("[@traceten/ai-crawl] invalid config — crawler tracking is DISABLED:", err instanceof Error ? err.message : err);
47
+ }
48
+ catch {
49
+ /* ignore */
50
+ }
51
+ }
52
+ resolvedCache.set(config, resolved);
53
+ return resolved;
54
+ }
55
+ /**
56
+ * Report the request if it looks like an AI crawler. Never throws, never
57
+ * blocks — delivery is scheduled via `event.waitUntil` when available.
58
+ */
59
+ export function trackAICrawlerRequest(request, event, config) {
60
+ try {
61
+ const cfg = resolveLazily(config);
62
+ if (cfg === null)
63
+ return;
64
+ trackFacts(cfg, factsFromFetchRequest(request), undefined, event !== undefined ? (p) => event.waitUntil(p) : undefined);
65
+ }
66
+ catch {
67
+ /* silent by contract */
68
+ }
69
+ }
70
+ //# sourceMappingURL=next.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"next.js","sourceRoot":"","sources":["../../src/adapters/next.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,EAAE,mBAAmB,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrE,OAAO,EAAE,qBAAqB,EAAE,UAAU,EAAyB,MAAM,aAAa,CAAC;AAQvF,MAAM,aAAa,GAAG,IAAI,OAAO,EAA+C,CAAC;AAEjF,8EAA8E;AAC9E,SAAS,aAAa,CACpB,MAA6C;IAE7C,IAAI,gBAAgB,CAAC,MAAM,CAAC;QAAE,OAAO,MAAM,CAAC;IAC5C,IAAI,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC;IACxE,IAAI,QAAQ,GAAiC,IAAI,CAAC;IAClD,IAAI,CAAC;QACH,QAAQ,GAAG,mBAAmB,CAAC,MAAM,CAAC,CAAC;IACzC,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,IAAI,CAAC;YACH,sCAAsC;YACtC,OAAO,CAAC,KAAK,CACX,qEAAqE,EACrE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CACzC,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC;YACP,YAAY;QACd,CAAC;IACH,CAAC;IACD,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IACpC,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CACnC,OAAyB,EACzB,KAAiC,EACjC,MAA6C;IAE7C,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;QAClC,IAAI,GAAG,KAAK,IAAI;YAAE,OAAO;QACzB,UAAU,CACR,GAAG,EACH,qBAAqB,CAAC,OAAO,CAAC,EAC9B,SAAS,EACT,KAAK,KAAK,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAC5D,CAAC;IACJ,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;AACH,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Config validation + defaulting.
3
+ *
4
+ * This is the ONE place the package is allowed to throw: at construction,
5
+ * in the customer's module scope / server boot — never per-request. A
6
+ * missing `authToken` must fail loudly here rather than silently sending
7
+ * requests that the endpoint will 401.
8
+ */
9
+ import type { AiCrawlConfig, ResolvedAiCrawlConfig } from "./types.js";
10
+ export declare const DEFAULT_ENDPOINT = "https://ingest.traceten.com/v1/ai-crawls";
11
+ export declare const DEFAULT_ALLOWED_METHODS: readonly string[];
12
+ export declare const AUTH_TOKEN_PREFIX = "tt_bot_";
13
+ /**
14
+ * Validate a config and apply defaults. THROWS on invalid input — call it at
15
+ * module scope (or in your middleware factory), so a misconfiguration fails
16
+ * your deploy, not your visitors' requests.
17
+ */
18
+ export declare function defineAiCrawlConfig(config: AiCrawlConfig): ResolvedAiCrawlConfig;
19
+ /** True when the object already passed through {@link defineAiCrawlConfig}. */
20
+ export declare function isResolvedConfig(config: AiCrawlConfig | ResolvedAiCrawlConfig): config is ResolvedAiCrawlConfig;
21
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAEvE,eAAO,MAAM,gBAAgB,6CAA6C,CAAC;AAC3E,eAAO,MAAM,uBAAuB,EAAE,SAAS,MAAM,EAAoB,CAAC;AAC1E,eAAO,MAAM,iBAAiB,YAAY,CAAC;AAM3C;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,GAAG,qBAAqB,CA8FhF;AAED,+EAA+E;AAC/E,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,aAAa,GAAG,qBAAqB,GAC5C,MAAM,IAAI,qBAAqB,CAEjC"}
package/dist/config.js ADDED
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Config validation + defaulting.
3
+ *
4
+ * This is the ONE place the package is allowed to throw: at construction,
5
+ * in the customer's module scope / server boot — never per-request. A
6
+ * missing `authToken` must fail loudly here rather than silently sending
7
+ * requests that the endpoint will 401.
8
+ */
9
+ import { DEFAULT_DENY_EXTENSIONS, DEFAULT_DENY_PATH_PREFIXES } from "./filter.js";
10
+ export const DEFAULT_ENDPOINT = "https://ingest.traceten.com/v1/ai-crawls";
11
+ export const DEFAULT_ALLOWED_METHODS = ["GET", "HEAD"];
12
+ export const AUTH_TOKEN_PREFIX = "tt_bot_";
13
+ function fail(message) {
14
+ throw new Error(`[@traceten/ai-crawl] ${message}`);
15
+ }
16
+ /**
17
+ * Validate a config and apply defaults. THROWS on invalid input — call it at
18
+ * module scope (or in your middleware factory), so a misconfiguration fails
19
+ * your deploy, not your visitors' requests.
20
+ */
21
+ export function defineAiCrawlConfig(config) {
22
+ if (typeof config !== "object" || config === null) {
23
+ fail("config must be an object");
24
+ }
25
+ if (typeof config.siteId !== "string" || config.siteId.trim() === "") {
26
+ fail("siteId is required — use your site key (the ttid_… data-site value on the install page)");
27
+ }
28
+ if (typeof config.authToken !== "string" || config.authToken.trim() === "") {
29
+ fail("authToken is required. POST /v1/ai-crawls rejects unauthenticated reports, " +
30
+ "so without it every report would silently 401. Create a crawl token " +
31
+ "(tt_bot_...) in the Traceten dashboard and keep it server-side.");
32
+ }
33
+ if (!config.authToken.startsWith(AUTH_TOKEN_PREFIX)) {
34
+ fail(`authToken must start with "${AUTH_TOKEN_PREFIX}". It looks like you passed ` +
35
+ "something else (perhaps the public snippet key?) — the crawl token is a " +
36
+ "server-side secret created in the dashboard.");
37
+ }
38
+ const proxyDepth = config.proxyDepth ?? 1;
39
+ if (!Number.isInteger(proxyDepth) || proxyDepth < 1) {
40
+ fail("proxyDepth must be a positive integer (number of trusted proxies appending to x-forwarded-for)");
41
+ }
42
+ if (config.endpoint !== undefined) {
43
+ // https only: the request carries the tt_bot_ Bearer token and crawler
44
+ // IPs. A typo'd http:// endpoint would leak both in cleartext, silently.
45
+ // Plain http is allowed solely for local development loopback.
46
+ const isHttps = /^https:\/\//.test(config.endpoint);
47
+ const isLocalHttp = /^http:\/\/(localhost|127\.0\.0\.1|\[::1\])(:\d+)?(\/|$)/.test(config.endpoint);
48
+ if (!isHttps && !isLocalHttp) {
49
+ fail("endpoint must be an https:// URL (http:// is allowed only for localhost). " +
50
+ "Reports carry your tt_bot_ token and crawler IPs — never send them in cleartext.");
51
+ }
52
+ }
53
+ if (config.publicOrigin !== undefined && !/^https?:\/\/[^/]+$/.test(config.publicOrigin)) {
54
+ fail('publicOrigin must be an origin with no path, e.g. "https://example.com"');
55
+ }
56
+ const allowedMethods = (config.allowedMethods ?? DEFAULT_ALLOWED_METHODS).map((m) => String(m).toUpperCase());
57
+ if (allowedMethods.length === 0) {
58
+ fail("allowedMethods must not be empty");
59
+ }
60
+ // Extras EXTEND the built-in lists; they never replace them.
61
+ const denyPathPrefixes = [
62
+ ...DEFAULT_DENY_PATH_PREFIXES,
63
+ // Trailing slashes are stripped so the segment-boundary match in
64
+ // filter.ts (`=== prefix || startsWith(prefix + "/")`) works either way.
65
+ ...(config.extraDenyPathPrefixes ?? []).map((p) => String(p).toLowerCase().replace(/\/+$/, "")),
66
+ ];
67
+ const denyExtensions = [
68
+ ...DEFAULT_DENY_EXTENSIONS,
69
+ ...(config.extraDenyExtensions ?? []).map((e) => {
70
+ const ext = String(e).toLowerCase();
71
+ return ext.startsWith(".") ? ext : `.${ext}`;
72
+ }),
73
+ ];
74
+ const resolved = {
75
+ siteId: config.siteId.trim(),
76
+ authToken: config.authToken.trim(),
77
+ endpoint: config.endpoint ?? DEFAULT_ENDPOINT,
78
+ allowedMethods,
79
+ denyPathPrefixes,
80
+ denyExtensions,
81
+ disableAnswerFetch: config.disableAnswerFetch === true,
82
+ disableSearchCrawlers: config.disableSearchCrawlers === true,
83
+ disableTrainingCrawlers: config.disableTrainingCrawlers === true,
84
+ disableOtherCrawlers: config.disableOtherCrawlers === true,
85
+ trustProxy: config.trustProxy === true,
86
+ trustCfConnectingIp: config.trustCfConnectingIp === true,
87
+ proxyDepth,
88
+ publicOrigin: config.publicOrigin,
89
+ onError: typeof config.onError === "function" ? config.onError : undefined,
90
+ fetch: config.fetch,
91
+ __resolved: true,
92
+ };
93
+ return resolved;
94
+ }
95
+ /** True when the object already passed through {@link defineAiCrawlConfig}. */
96
+ export function isResolvedConfig(config) {
97
+ return config.__resolved === true;
98
+ }
99
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,68 @@
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
+ import type { CrawlerCategory } from "./types.js";
41
+ export interface AgentToken {
42
+ /** Lowercased agent token, matched with token-boundary anchoring. */
43
+ readonly token: string;
44
+ /** Canonical display name of the agent (original casing). */
45
+ readonly agent: string;
46
+ readonly provider: string;
47
+ readonly category: CrawlerCategory;
48
+ }
49
+ /** Tier 1 — exact agent tokens. First match wins. */
50
+ export declare const AGENT_TOKENS: readonly AgentToken[];
51
+ export interface ProviderAlias {
52
+ /** Lowercased alias, matched as a LEFT-anchored prefix at a token boundary. */
53
+ readonly alias: string;
54
+ readonly provider: string;
55
+ }
56
+ /**
57
+ * Tier 2 — coarse provider aliases, so a NEW agent from a known vendor
58
+ * (e.g. a hypothetical `ChatGPT-Reader/1.0`) is still captured without a
59
+ * package release. Left-boundary-anchored prefix match: the alias must start
60
+ * at the beginning of a token (start of string or after a non-token
61
+ * character), which is what defeats `not-really-GPTBot`-style spoofs while
62
+ * still catching `Claude-NewAgent/1.0`.
63
+ *
64
+ * Tier-2 matches are reported with category `ai_crawler` locally (the server
65
+ * assigns the real category) and are gated by `disableOtherCrawlers`.
66
+ */
67
+ export declare const PROVIDER_ALIASES: readonly ProviderAlias[];
68
+ //# sourceMappingURL=crawlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crawlers.d.ts","sourceRoot":"","sources":["../src/crawlers.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAElD,MAAM,WAAW,UAAU;IACzB,qEAAqE;IACrE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,6DAA6D;IAC7D,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;CACpC;AAED,qDAAqD;AACrD,eAAO,MAAM,YAAY,EAAE,SAAS,UAAU,EAwK7C,CAAC;AAEF,MAAM,WAAW,aAAa;IAC5B,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,gBAAgB,EAAE,SAAS,aAAa,EA6BpD,CAAC"}