{"_id":"@brandarmorai/ai-search-crawler","_rev":"2-ee19f68735a97b29fb49026da18c0a4a","name":"@brandarmorai/ai-search-crawler","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.2":{"name":"@brandarmorai/ai-search-crawler","version":"0.1.2","keywords":["ai-crawler","analytics","nextjs","express","cloudflare","hono","bun","deno"],"license":"MIT","_id":"@brandarmorai/ai-search-crawler@0.1.2","maintainers":[{"name":"brandarmorai","email":"admin@brandarmor.ai"}],"dist":{"shasum":"35da1c0b239433e99c542227d1517c3710865d42","tarball":"https://registry.npmjs.org/@brandarmorai/ai-search-crawler/-/ai-search-crawler-0.1.2.tgz","fileCount":5,"integrity":"sha512-XlrGRcNWFgUk9taiUUxtbia7x3pLROomDGdm+ndJqV17N9THdwzwYGxrxc3kZpiE9vKiMtkhlTr1Rz5ntrtV+A==","signatures":[{"sig":"MEYCIQDpB7IgxB5bxgg+T38h2i17SF/bi+bJa33k3Dosd3fC+wIhAI6E+aM67lF0/ur/qorUcCzkkqr9vqwyqWfiTxknkOIn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":21381},"main":"dist/index.js","types":"dist/index.d.ts","gitHead":"8f9d67f1aa4ec30c45bcb225330f7b6b1365bcc4","scripts":{"test":"npm run build && node --test test/*.test.js","build":"tsc -p tsconfig.json","prepack":"npm run build"},"_npmUser":{"name":"brandarmorai","email":"admin@brandarmor.ai"},"_npmVersion":"10.9.3","description":"Server-side AI crawler request tracking for Brand Armor AI","directories":{},"sideEffects":false,"_nodeVersion":"22.18.0","_hasShrinkwrap":false,"devDependencies":{"typescript":"^5.3.3"},"_npmOperationalInternal":{"tmp":"tmp/ai-search-crawler_0.1.2_1784834073796_0.5112603301702938","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@brandarmorai/ai-search-crawler","version":"0.1.3","description":"Server-side AI crawler request tracking for Brand Armor AI","main":"dist/index.js","types":"dist/index.d.ts","scripts":{"build":"tsc -p tsconfig.json","test":"npm run build && node --test test/*.test.js","prepack":"npm run build"},"keywords":["ai-crawler","analytics","nextjs","express","cloudflare","hono","bun","deno"],"license":"MIT","sideEffects":false,"devDependencies":{"typescript":"^5.3.3"},"_id":"@brandarmorai/ai-search-crawler@0.1.3","gitHead":"9bd372ff811795830936e1551181fa660e0d2920","_nodeVersion":"22.18.0","_npmVersion":"10.9.3","dist":{"integrity":"sha512-Ur2yP5gzAVCJOk+IfVZrV64Pvr4PFW28+saI0tAlcz4cSTqo5hNyjVBkIc2KkMXbEu66v/kF/DLDZbkJPmdFtw==","shasum":"f2b84b434196f895583c265adf74746259b105db","tarball":"https://registry.npmjs.org/@brandarmorai/ai-search-crawler/-/ai-search-crawler-0.1.3.tgz","fileCount":5,"unpackedSize":23613,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIBm6rMmo+ARbmlKG7vMUvJ2wx/tNS09UU73SOeo1dexRAiEAndmG/ru7/DwlLMlFaTawTfQs2uSiJGh8LAyiTaqZvik="}]},"_npmUser":{"name":"brandarmorai","email":"admin@brandarmor.ai"},"directories":{},"maintainers":[{"name":"brandarmorai","email":"admin@brandarmor.ai"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ai-search-crawler_0.1.3_1784921596070_0.04809015496787983"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-23T19:14:33.633Z","modified":"2026-07-24T19:33:16.502Z","0.1.2":"2026-07-23T19:14:33.942Z","0.1.3":"2026-07-24T19:33:16.340Z"},"license":"MIT","keywords":["ai-crawler","analytics","nextjs","express","cloudflare","hono","bun","deno"],"description":"Server-side AI crawler request tracking for Brand Armor AI","maintainers":[{"name":"brandarmorai","email":"admin@brandarmor.ai"}],"readme":"# `@brandarmorai/ai-search-crawler`\n\nBest-effort server-side AI crawler request tracking for Brand Armor AI. This package does not run in the browser and never exposes its token to client-side JavaScript.\n\n## Next.js middleware\n\n```ts\nimport { NextRequest, NextResponse, NextFetchEvent } from 'next/server';\nimport { trackNextRequest } from '@brandarmorai/ai-search-crawler';\n\nexport function middleware(request: NextRequest, event: NextFetchEvent) {\n  trackNextRequest(request, {\n    websiteId: process.env.BRAND_ARMOR_AI_WEBSITE_ID!,\n    token: process.env.BRAND_ARMOR_AI_CRAWLER_TOKEN!,\n  }, event);\n\n  return NextResponse.next();\n}\n```\n\n## Express\n\n```ts\nimport { createExpressAICrawlerMiddleware } from '@brandarmorai/ai-search-crawler';\n\napp.use(createExpressAICrawlerMiddleware({\n  websiteId: process.env.BRAND_ARMOR_AI_WEBSITE_ID!,\n  token: process.env.BRAND_ARMOR_AI_CRAWLER_TOKEN!,\n}));\n```\n\n## Cloudflare Workers\n\nUse the Worker execution context so delivery can finish after the response is returned:\n\n```ts\nimport { withCloudflareWorkerTracking } from '@brandarmorai/ai-search-crawler';\n\nexport default {\n  fetch: withCloudflareWorkerTracking(\n    async (request, env) => env.ASSETS.fetch(request),\n    env => ({\n      websiteId: 'YOUR_WEBSITE_ID',\n      token: env.BRAND_ARMOR_AI_CRAWLER_TOKEN,\n    })\n  ),\n};\n```\n\nStore the token as a Worker secret. The adapter reads `CF-Connecting-IP` only inside the trusted Cloudflare runtime.\n\n## Cloudflare Pages\n\n```ts\nimport { trackCloudflarePagesRequest } from '@brandarmorai/ai-search-crawler';\n\nexport function onRequest(context) {\n  return trackCloudflarePagesRequest(context, {\n    websiteId: context.env.BRAND_ARMOR_AI_WEBSITE_ID,\n    token: context.env.BRAND_ARMOR_AI_CRAWLER_TOKEN,\n  });\n}\n```\n\n## Hono\n\n```ts\nimport { Hono } from 'hono';\nimport { createHonoAICrawlerMiddleware } from '@brandarmorai/ai-search-crawler';\n\nconst app = new Hono();\n\napp.use('*', createHonoAICrawlerMiddleware({\n  websiteId: process.env.BRAND_ARMOR_AI_WEBSITE_ID!,\n  token: process.env.BRAND_ARMOR_AI_CRAWLER_TOKEN!,\n}));\n```\n\nThe middleware works with Hono on Node, Bun, Deno, and Workers. On Workers it automatically uses `executionCtx.waitUntil` when available.\n\nFor Hono on Cloudflare Workers, resolve the secret from the request context:\n\n```ts\napp.use('*', createHonoAICrawlerMiddleware(context => ({\n  websiteId: 'YOUR_WEBSITE_ID',\n  token: context.env.BRAND_ARMOR_AI_CRAWLER_TOKEN,\n})));\n```\n\n## Fetch, Bun, and Deno\n\nUse the standard request and response adapter around any Fetch-compatible handler:\n\n```ts\nimport { trackAICrawlerResponse } from '@brandarmorai/ai-search-crawler';\n\nexport default async function handle(request, context) {\n  const response = await app.fetch(request);\n\n  trackAICrawlerResponse(request, response, {\n    websiteId: process.env.BRAND_ARMOR_AI_WEBSITE_ID!,\n    token: process.env.BRAND_ARMOR_AI_CRAWLER_TOKEN!,\n    source: 'fetch', // Use \"bun\" or \"deno\" when applicable.\n  }, context);\n\n  return response;\n}\n```\n\n## Behavior and security\n\nOnly GET and HEAD requests from recognized candidate user agents are sent. API/framework/static paths are excluded. Query strings are removed by default. Tracking is fail-open and does not delay the customer response.\n\nWhen available, the SDK also sends the crawler network's two-letter country code from trusted Vercel, Cloudflare, CloudFront, or App Engine request metadata. It does not collect city or region data, and the country describes crawler infrastructure rather than the location of an AI user.\n\nThe backend remains authoritative for crawler classification and website validation. A detected request does not prove indexing, training use, citation, or recommendation.\n","readmeFilename":"README.md"}