{"_id":"@bountain/attribution-kit","_rev":"3-56fdac12ba233e06a4a4d5dcf65751ff","name":"@bountain/attribution-kit","dist-tags":{"latest":"1.0.3"},"versions":{"1.0.0":{"name":"@bountain/attribution-kit","version":"1.0.0","_id":"@bountain/attribution-kit@1.0.0","maintainers":[{"name":"bounta.in","email":"admin@volc.org"}],"dist":{"shasum":"46b73c8d5da94b6026559f823da1d9f510aaf67a","tarball":"https://registry.npmjs.org/@bountain/attribution-kit/-/attribution-kit-1.0.0.tgz","fileCount":42,"integrity":"sha512-+kerhVEaJ//DVWKLBVYcYhB8Zq2zz/QQxj3wWUTBbk2TBfJKpLk49bsq57b+AW/S2d2nODlDcCbW7c+ienR/XA==","signatures":[{"sig":"MEYCIQCA6jsi6G/pwLYB+ET+SJTJXLwCMrI+6yYHfXfKKuVMXwIhAO3LOXJe3iTOZZ8odyb0cTshlw/NGJZXm37T2P2TVrX+","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":95553},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client.d.ts","default":"./dist/client.js"},"./server":{"types":"./dist/server.d.ts","default":"./dist/server.js"},"./shared":{"types":"./dist/shared.d.ts","default":"./dist/shared.js"}},"gitHead":"f3a3f9b83c5dacf9aa8764e83fca02afcb5f6927","private":false,"scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist"},"_npmUser":{"name":"bounta.in","email":"admin@volc.org"},"_npmVersion":"10.1.0","description":"Drop-in helpers to capture and verify Bountain click attribution (click_id + click_token) in Next.js.","directories":{},"sideEffects":false,"_nodeVersion":"20.9.0","dependencies":{"zod":"^4.1.13"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.0.15","typescript":"^5.9.3"},"_npmOperationalInternal":{"tmp":"tmp/attribution-kit_1.0.0_1770458046922_0.5427314792475146","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@bountain/attribution-kit","version":"1.0.2","_id":"@bountain/attribution-kit@1.0.2","maintainers":[{"name":"bounta.in","email":"admin@volc.org"}],"dist":{"shasum":"a59fed05cb8ead5ee00ddf5d92d00ed0e3efcd92","tarball":"https://registry.npmjs.org/@bountain/attribution-kit/-/attribution-kit-1.0.2.tgz","fileCount":42,"integrity":"sha512-xJVf3OYjum4zUz72ICkUeolThQoBwi0aa37lS2EZo72bJ8yKYgeY4OgfyJVa/CZJIbI26V72VzFXgc2z0udzwg==","signatures":[{"sig":"MEYCIQDmF/vVCK1kHu7Fh9VeDvm7oSUfUg9Ppc5xzHF7dxijDgIhAMO6LSGQthpP1vHmwA1gpkvQ400LT4qY7aehShXA9g37","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":97166},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","engines":{"node":">=20.0.0"},"exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client.d.ts","default":"./dist/client.js"},"./server":{"types":"./dist/server.d.ts","default":"./dist/server.js"},"./shared":{"types":"./dist/shared.d.ts","default":"./dist/shared.js"}},"gitHead":"68591e1b1a5b353d169f27dd823ce253603ef52b","private":false,"scripts":{"test":"vitest run","build":"tsc -p tsconfig.build.json","clean":"rm -rf dist"},"_npmUser":{"name":"bounta.in","email":"admin@volc.org"},"_npmVersion":"10.1.0","description":"Drop-in helpers to capture and verify Bountain click attribution (click_id + click_token) in Next.js.","directories":{},"sideEffects":false,"_nodeVersion":"20.9.0","dependencies":{"zod":"^4.1.13"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"vitest":"^4.0.15","typescript":"^5.9.3"},"_npmOperationalInternal":{"tmp":"tmp/attribution-kit_1.0.2_1770461602872_0.3410549847658637","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@bountain/attribution-kit","version":"1.0.3","private":false,"type":"module","description":"Drop-in helpers to capture and verify Bountain click attribution (click_id + click_token) in Next.js.","sideEffects":false,"main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"./client":{"types":"./dist/client.d.ts","default":"./dist/client.js"},"./server":{"types":"./dist/server.d.ts","default":"./dist/server.js"},"./shared":{"types":"./dist/shared.d.ts","default":"./dist/shared.js"}},"scripts":{"build":"tsc -p tsconfig.build.json","clean":"rm -rf dist","test":"vitest run"},"publishConfig":{"access":"public"},"dependencies":{"zod":"^4.1.13"},"devDependencies":{"typescript":"^5.9.3","vitest":"^4.0.15"},"engines":{"node":">=20.0.0"},"_id":"@bountain/attribution-kit@1.0.3","gitHead":"68591e1b1a5b353d169f27dd823ce253603ef52b","_nodeVersion":"20.9.0","_npmVersion":"10.1.0","dist":{"integrity":"sha512-xV/3Pm3ClkSOZ5fJ2HLJBf4XPpf8l2IosbTdHvc6plaWip5peK8n+FrVlIDuxLPMGu5MeAZfCJ2H/2w3mHyEWw==","shasum":"78b435fcd546a589c1c3c3185c03209dbd482117","tarball":"https://registry.npmjs.org/@bountain/attribution-kit/-/attribution-kit-1.0.3.tgz","fileCount":42,"unpackedSize":97166,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIH3Sqfv2cA3RWfZS+M4jhiVOIpweCKesoD3qhhb6fm90AiBhA/la6g2Svure7BvVmI3r4dQhuhpDgM2n2EA9tD3lwg=="}]},"_npmUser":{"name":"bounta.in","email":"admin@volc.org"},"directories":{},"maintainers":[{"name":"bounta.in","email":"admin@volc.org"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/attribution-kit_1.0.3_1770461751018_0.2821355341937173"},"_hasShrinkwrap":false}},"time":{"created":"2026-02-07T09:54:06.846Z","modified":"2026-02-07T10:55:51.312Z","1.0.0":"2026-02-07T09:54:07.055Z","1.0.2":"2026-02-07T10:53:23.032Z","1.0.3":"2026-02-07T10:55:51.167Z"},"description":"Drop-in helpers to capture and verify Bountain click attribution (click_id + click_token) in Next.js.","maintainers":[{"name":"bounta.in","email":"admin@volc.org"}],"readme":"# @bountain/attribution-kit\n\nDrop-in helpers to capture and verify Bountain click attribution (`click_id` + signed `click_token`) in **Next.js App Router**.\n\n## What it solves\n\n- Visitors arrive at your site with:\n  - `click_id` (UUID)\n  - `click_token` (HMAC-signed short-lived eligibility token)\n- You want to *persist* that token as a **first-party HttpOnly cookie** and later *verify* it on the server when recording a conversion.\n\n## Install\n\nThis repo is a pnpm workspace. From the root:\n\n- `pnpm add @bountain/attribution-kit`\n\n(When published, this will work the same from any repo.)\n\n## 1) Add a persist route (server)\n\nCreate `src/app/api/attribution/persist/route.ts`:\n\n```ts\nimport { createPersistAttributionRoute } from '@bountain/attribution-kit/server';\n\nexport const { GET, POST } = createPersistAttributionRoute();\n```\n\nThis route sets cookies:\n- `yd_click_id` (non-HttpOnly)\n- `yd_click_token` (HttpOnly)\n\n## 2) Capture on your landing page (client)\n\nIn any `page.tsx` that receives traffic, add a tiny client component:\n\n```tsx\n'use client';\n\nimport { useAttributionCapture } from '@bountain/attribution-kit/client';\n\nexport function AttributionCapture() {\n  useAttributionCapture({ endpoint: '/api/attribution/persist' });\n  return null;\n}\n```\n\nThen render it in your page:\n\n```tsx\nimport { AttributionCapture } from './AttributionCapture';\n\nexport default function Page() {\n  return (\n    <main>\n      <AttributionCapture />\n      {/* ... */}\n    </main>\n  );\n}\n```\n\n## 3) Verify on conversion (server)\n\nExample in a route handler or server action:\n\n```ts\nimport { verifyAttributionFromRequest } from '@bountain/attribution-kit/server';\n\nexport async function POST(req: Request) {\n  const secret = process.env.APP_SECRET;\n  if (!secret) return new Response('missing APP_SECRET', { status: 500 });\n\n  const verified = verifyAttributionFromRequest(req, { secret });\n  if (!verified.ok) {\n    // analytics-only conversion\n    return new Response(JSON.stringify({ ok: true, eligible: false, error: verified.error }), {\n      status: 200,\n      headers: { 'content-type': 'application/json' },\n    });\n  }\n\n  // eligible conversion; verified.clickId binds lineage to a real click\n  return new Response(JSON.stringify({ ok: true, eligible: true, clickId: verified.clickId }), {\n    status: 200,\n    headers: { 'content-type': 'application/json' },\n  });\n}\n```\n\n## Notes\n\n- A browser **cannot** set an HttpOnly cookie, so capture must call a server endpoint.\n- Token verification uses Node `crypto` (HMAC SHA-256).\n- If verification fails (`expired`, `bad_signature`, etc.), record the conversion as **analytics-only**.\n","readmeFilename":"README.md"}