{"_id":"@almlah/ads-sdk","name":"@almlah/ads-sdk","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@almlah/ads-sdk","version":"0.1.0","description":"Headless advertisement SDK for Almlah Ads — usePlacement + automatic impression/click tracking. Framework-agnostic core with React bindings.","license":"MIT","type":"module","sideEffects":false,"main":"./dist/index.cjs","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}}},"publishConfig":{"access":"public"},"keywords":["ads","advertisement","sdk","react","usePlacement","ad-delivery"],"scripts":{"typecheck":"tsc --noEmit","verify":"tsx scripts/verify.ts","build":"tsup","prepublishOnly":"npm run build"},"peerDependencies":{"react":">=18"},"peerDependenciesMeta":{"react":{"optional":true}},"devDependencies":{"@ads/types":"*","@types/react":"^19","react":"19.2.4","tsup":"^8","typescript":"^5"},"_id":"@almlah/ads-sdk@0.1.0","gitHead":"5ce6ebb4ba8513e2a3a84727d70b671e6d61c266","_nodeVersion":"22.22.0","_npmVersion":"10.9.4","dist":{"integrity":"sha512-K4TL2qFVo/zDM/KdcsUofGv6+Ex0oRJxVK++kniYLxYT+oZ4oEXPWwNTkCumOEq1F62WGcPMbEhptQdpjyiQjw==","shasum":"ed15079ecdf81873ec0d8f6a5784f16bfe411c52","tarball":"https://registry.npmjs.org/@almlah/ads-sdk/-/ads-sdk-0.1.0.tgz","fileCount":8,"unpackedSize":122897,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIEXOCQPPwSHBY3kPLMkw5hL/cHfGjnsNFCtuAzzz+C6uAiEA/PzqgPvVzmk+ZfQxJTZwVxE0X64TRFBddNtGsEEOOnw="}]},"_npmUser":{"name":"almlah","email":"almlah.om@gmail.com"},"directories":{},"maintainers":[{"name":"almlah","email":"almlah.om@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ads-sdk_0.1.0_1783505451480_0.3208879317699045"},"_hasShrinkwrap":false}},"time":{"created":"2026-07-08T10:10:51.260Z","0.1.0":"2026-07-08T10:10:51.618Z","modified":"2026-07-08T10:10:51.905Z"},"maintainers":[{"name":"almlah","email":"almlah.om@gmail.com"}],"description":"Headless advertisement SDK for Almlah Ads — usePlacement + automatic impression/click tracking. Framework-agnostic core with React bindings.","keywords":["ads","advertisement","sdk","react","usePlacement","ad-delivery"],"license":"MIT","readme":"# @almlah/ads-sdk — headless advertisement SDK\n\nThe **only** integration point for websites. It fetches ads, caches them, retries,\nand tracks impressions/clicks automatically. It is **headless**: it gives you\ndata; you render it. It never controls layout.\n\n## Setup\n\nWrap your app once:\n\n```tsx\nimport { AdsProvider } from \"@almlah/ads-sdk\";\n\nconst adsConfig = { endpoint: \"https://ads.example.com\", platform: \"website-a\" };\n\nexport default function App({ children }) {\n  return <AdsProvider config={adsConfig}>{children}</AdsProvider>;\n}\n```\n\n## Render a placement\n\n`usePlacement` returns data + a `getTrackProps` prop-getter. Spread it onto each\nad element — impressions fire once when visible (IntersectionObserver), clicks on\nclick. No manual tracking calls.\n\n```tsx\nimport { usePlacement } from \"@almlah/ads-sdk\";\n\nfunction HomepageProducts() {\n  const products = usePlacement(\"homepage-products\");\n\n  if (products.isLoading) return <Skeleton />;\n\n  return (\n    <div className=\"grid\">\n      {products.items.map((ad) => {\n        const c = ad.creative; // discriminated union on c.type\n        if (c.type !== \"product\") return null;\n        return (\n          <a key={ad.id} href={c.destinationUrl} {...products.getTrackProps(ad)}>\n            <img src={c.image.url} alt={c.productName} />\n            <strong>{c.brand}</strong> {c.productName} — {c.price}\n            <button>{c.ctaLabel}</button>\n          </a>\n        );\n      })}\n    </div>\n  );\n}\n```\n\n`items` is **always an array**, regardless of the placement's capacity.\n\n## Batching\n\nEvery `usePlacement()` that mounts in the same tick is coalesced into **one**\n`POST /api/v1/delivery` request carrying all the requested placement keys — never\none request per placement.\n\n## API\n\n| Export | What it does |\n|---|---|\n| `<AdsProvider config>` | Creates the client, wires unload-flush of tracking |\n| `usePlacement(key)` | `{ items, status, isLoading, capacity, error, refresh, getTrackProps }` |\n| `useTrack()` | `(token, \"impression\" \\| \"click\") => void` — low-level, rarely needed |\n| `useAds()` | The `AdsClient` (advanced: prefetch/refresh/clearCache/track) |\n| `prefetchPlacement(...keys)` | Warm the cache before render |\n| `refreshPlacement(key)` | Invalidate + refetch |\n| `clearCache()` | Drop all cached placements |\n| `AdsClient` | Framework-agnostic core (use directly outside React) |\n| `createLocalStorageStore()` | Optional offline cache backend for `config.store` |\n\n## Config\n\n`endpoint`, `platform` (required) · `context` `{ country, language, device }` ·\n`cacheTtlMs` (60000) · `maxRetries` (2) · `retryBackoffMs` (300) ·\n`trackBatchSize` (10) · `trackFlushMs` (2000) · `store` (offline cache) ·\n`fetchImpl`, `debug`.\n\n## Behavior notes\n\n- **Caching** with TTL; **stale-while-error** — a failed refetch keeps showing the\n  last good ads instead of erroring.\n- **Retries** with exponential backoff.\n- **Tracking** is batched and flushed on a timer, on `trackBatchSize`, and via\n  `navigator.sendBeacon` on page hide/visibility change. Impressions dedupe per\n  token so an ad counts once.\n- SSR-safe (no fetch on the server; stable server snapshot).\n\nVerify the core: `npm run verify -w @almlah/ads-sdk`.\n","readmeFilename":"README.md","_rev":"1-9ece30e0739cdc3244bf820d960b9400"}