{"_id":"@adhub-so/adhub-nextjs-sdk","name":"@adhub-so/adhub-nextjs-sdk","dist-tags":{"latest":"0.1.1"},"versions":{"0.1.1":{"name":"@adhub-so/adhub-nextjs-sdk","version":"0.1.1","description":"First-party attribution, identity, and event SDK for Next.js","type":"module","main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js","require":"./dist/index.cjs"},"./server":{"types":"./dist/server.d.ts","import":"./dist/server.js","require":"./dist/server.cjs"},"./testing":{"types":"./dist/testing.d.ts","import":"./dist/testing.js","require":"./dist/testing.cjs"}},"scripts":{"build":"tsup","test":"vitest run","test:watch":"vitest","typecheck":"tsc --noEmit"},"devDependencies":{"@types/react":"^19.2.2","@types/react-dom":"^19.2.2","happy-dom":"^20.11.2","next":"^16.2.12","react":"^19.2.4","react-dom":"^19.2.4","tsup":"^8.5.0","typescript":"^5.9.2","vitest":"^3.2.4"},"engines":{"node":">=20"},"peerDependencies":{"next":"^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0","react":"^18.2.0 || ^19.0.0","react-dom":"^18.2.0 || ^19.0.0"},"overrides":{"esbuild":"^0.28.1"},"sideEffects":false,"publishConfig":{"access":"public"},"license":"UNLICENSED","_id":"@adhub-so/adhub-nextjs-sdk@0.1.1","_nodeVersion":"20.9.0","_npmVersion":"10.1.0","dist":{"integrity":"sha512-NANHuRvbAGVPx3/6V5yYQbi8BQVZO6vm8peXMWCTmqz9q07dyTCUoOVDuSXRDIRqkwiCfRoVdG+Io9WneGGF/Q==","shasum":"293ef22da532e1c74918ee115bba54603c792fe8","tarball":"https://registry.npmjs.org/@adhub-so/adhub-nextjs-sdk/-/adhub-nextjs-sdk-0.1.1.tgz","fileCount":24,"unpackedSize":407695,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIETLeHu0wDnwsdbxqtzcxlidDIt7FjRQspoxe/nOWjllAiEAhWAXGdtTh6NI67myKke/Lec6CD9ayaPKYtVUUw3J5mQ="}]},"_npmUser":{"name":"apipass_dev","email":"myemailliao@gmail.com"},"directories":{},"maintainers":[{"name":"apipass_dev","email":"myemailliao@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/adhub-nextjs-sdk_0.1.1_1787221445707_0.7218781275504447"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-20T10:24:05.515Z","0.1.1":"2026-08-20T10:24:05.850Z","modified":"2026-08-20T10:24:06.191Z"},"maintainers":[{"name":"apipass_dev","email":"myemailliao@gmail.com"}],"description":"First-party attribution, identity, and event SDK for Next.js","license":"UNLICENSED","readme":"# AdHub Next.js SDK\n\n`@adhub-so/adhub-nextjs-sdk` is AdHub's first-party SDK for Next.js. It captures Google Ads landing touchpoints, maintains anonymous and authenticated identity, records page and product behavior, sends browser business events, and proxies requests through a Next.js Route Handler so AdHub receives the original browser IP, user agent, and edge geo headers.\n\nThis package targets Next.js only. It does not publish a framework-neutral browser entry, SPA bootstrap API, or global IIFE bundle.\n\n## Install\n\n```bash\nnpm install @adhub-so/adhub-nextjs-sdk\n```\n\nFor a local sibling checkout:\n\n```json\n{\n  \"dependencies\": {\n    \"@adhub-so/adhub-nextjs-sdk\": \"file:../adhub-nextjs-sdk\"\n  }\n}\n```\n\n## App Router setup\n\n### 1. Add the component\n\nRender `AdHubComponent` once in the root layout. A relative `endpoint` sends browser requests through your own Next.js origin, which improves delivery and preserves the real browser context through the Route Handler in the next step.\n\n```tsx\n// app/layout.tsx\nimport { AdHubComponent } from \"@adhub-so/adhub-nextjs-sdk\";\n\nexport default function RootLayout({ children }: { children: React.ReactNode }) {\n  return (\n    <html lang=\"en\">\n      <body>\n        <AdHubComponent\n          projectKey={process.env.NEXT_PUBLIC_ADHUB_PROJECT_KEY!}\n          endpoint=\"/api/adhub\"\n        />\n        {children}\n      </body>\n    </html>\n  );\n}\n```\n\nThe Project key is a public browser identifier. Never pass an ingestion key or another server secret to `AdHubComponent` or a `NEXT_PUBLIC_*` environment variable.\n\nIf authenticated profile data is already available to the layout, it can be supplied as a serializable prop:\n\n```tsx\n<AdHubComponent\n  projectKey={process.env.NEXT_PUBLIC_ADHUB_PROJECT_KEY!}\n  endpoint=\"/api/adhub\"\n  profile={{ profileId: user.id, email: user.email }}\n/>\n```\n\n`profileId` can be passed directly when no other profile fields are needed. Changing directly from one non-null profile to another rotates the browser identity boundary before identifying the next user. Passing `null` resets the authenticated identity while retaining a new anonymous browser identity.\n\n### 2. Add the Route Handler\n\n```ts\n// app/api/adhub/[...path]/route.ts\nimport { createRouteHandler } from \"@adhub-so/adhub-nextjs-sdk/server\";\n\nexport const runtime = \"nodejs\";\n\nconst handler = createRouteHandler({\n  apiUrl: process.env.ADHUB_API_URL ?? \"https://api.adhub.so\",\n});\n\nexport const { POST, OPTIONS } = handler;\n```\n\nThe handler only forwards the supported SDK ingestion paths. It carries the original `Origin`, `User-Agent`, client IP, country, region, city, project key, idempotency key, SDK name, and SDK version to AdHub. It does not proxy general SaaS APIs.\n\n### 3. Track from Client Components\n\n`useAdHub()` is the client API for Next.js Client Components. It exposes a reactive `state` plus identity, page, behavior, event, reset, and flush actions.\n\n```tsx\n\"use client\";\n\nimport { useAdHub } from \"@adhub-so/adhub-nextjs-sdk\";\n\nexport function SignupButton({ userId }: { userId: string }) {\n  const adhub = useAdHub();\n\n  async function completeSignup() {\n    const currentProfileId = adhub.getState().profileId;\n    if (currentProfileId && currentProfileId !== userId) adhub.reset();\n    if (adhub.getState().profileId !== userId) {\n      adhub.identify({ profileId: userId });\n    }\n\n    adhub.track({ eventType: \"signup.completed\" });\n    await adhub.flush();\n  }\n\n  return <button onClick={() => void completeSignup()}>Create account</button>;\n}\n```\n\nUse `behavior()` for non-sensitive product analytics and `track()` for configured AdHub business events:\n\n```ts\nadhub.behavior({\n  eventName: \"pricing_cta_clicked\",\n  properties: { plan: \"growth\" },\n});\n\nadhub.track({\n  eventType: \"purchase\",\n  eventId: `order:${order.id}`,\n  value: order.total,\n  currency: order.currency,\n});\n```\n\nBrowser `track()` requires an identified profile. Trusted payment, refund, and subscription facts should normally be sent from the server instead.\n\n## Capture before hydration\n\nNext.js 15.3 and newer can initialize AdHub synchronously from `instrumentation-client.ts`. Use this advanced setup when application code may remove attribution query parameters before React hydrates. In that case, use `initAdHub()` here instead of relying on `AdHubComponent` to initialize.\n\n```ts\n// src/instrumentation-client.ts\nimport { initAdHub } from \"@adhub-so/adhub-nextjs-sdk\";\n\ninitAdHub({\n  projectKey: process.env.NEXT_PUBLIC_ADHUB_PROJECT_KEY!,\n  endpoint: \"/api/adhub\",\n});\n```\n\nOnly synchronous top-level work in `instrumentation-client.ts` is guaranteed to complete before hydration. `initAdHub()` captures and persists the landing touchpoint synchronously; network delivery remains queued and asynchronous.\n\n## Server events\n\nImport trusted server functionality only from `@adhub-so/adhub-nextjs-sdk/server` so ingestion credentials cannot enter the client module graph.\n\n```ts\nimport { createAdHubServerClient } from \"@adhub-so/adhub-nextjs-sdk/server\";\n\nconst adhub = createAdHubServerClient({\n  projectKey: process.env.ADHUB_PROJECT_KEY!,\n  ingestionKey: process.env.ADHUB_INGESTION_KEY!,\n});\n\nawait adhub.track({\n  eventType: \"purchase\",\n  profileId: payment.userId,\n  eventId: `payment:${payment.id}`,\n  occurredAt: payment.paidAt.toISOString(),\n  value: payment.amount,\n  currency: payment.currency,\n  properties: { orderId: payment.orderId },\n});\n```\n\n`eventId` is required for trusted server events and is used as the idempotency key. Reuse the same ID for retries of the same business fact.\n\nWhen a trusted event is sent during an OAuth callback, pass the anonymous ID\nthat this SDK persisted in the browser cookie. AdHub will claim that anonymous\nidentity for the profile before attributing the event:\n\n```ts\nimport {\n  createAdHubServerClient,\n  readAdHubAnonymousId,\n} from \"@adhub-so/adhub-nextjs-sdk/server\";\n\nconst adhub = createAdHubServerClient({\n  projectKey: process.env.ADHUB_PROJECT_KEY!,\n  ingestionKey: process.env.ADHUB_INGESTION_KEY!,\n});\n\nawait adhub.track({\n  eventType: process.env.ADHUB_SIGNUP_EVENT_TYPE_ID!,\n  profileId: user.id,\n  anonymousId: readAdHubAnonymousId(request.headers.get(\"cookie\"), process.env.ADHUB_PROJECT_KEY!),\n  eventId: `signup:${user.id}`,\n  occurredAt: user.created_at,\n});\n```\n\n## Attribution testing\n\nAdHub's dashboard can create zero-write preview envelopes through the explicit testing entry:\n\n```ts\nimport { createAttributionTestEnvelopes } from \"@adhub-so/adhub-nextjs-sdk/testing\";\n```\n\nThis helper does not initialize browser tracking, modify storage, or send network requests.\n\n## Public entries\n\n- `@adhub-so/adhub-nextjs-sdk`: `AdHubComponent`, `initAdHub`, `useAdHub`, and client-side types.\n- `@adhub-so/adhub-nextjs-sdk/server`: `createRouteHandler`, `createAdHubServerClient`, server payload helpers, errors, and server types.\n- `@adhub-so/adhub-nextjs-sdk/testing`: the dashboard attribution-test envelope helper and its types.\n\nThe package does not expose `./browser`, redundant framework subpaths, a global IIFE, a client factory, or generic singleton event functions. Browser operations stay behind the Next.js component, initialization entry, and client Hook.\n","readmeFilename":"README.md","_rev":"1-8a595b8d2ebf8731c8dfec51b4200698"}