{"_id":"@bakaburg24/decisioning-js","_rev":"6-9b29237fc32f0c6e39e247aa9a638a1d","name":"@bakaburg24/decisioning-js","dist-tags":{"latest":"0.1.5"},"versions":{"0.1.0":{"name":"@bakaburg24/decisioning-js","version":"0.1.0","keywords":["decisioning","bandit","experimentation","ab-testing","thompson-sampling","epsilon-greedy","browser-sdk"],"author":{"name":"bakaburg24"},"license":"MIT","_id":"@bakaburg24/decisioning-js@0.1.0","maintainers":[{"name":"bakaburg24","email":"bakaburg24@gmail.com"}],"homepage":"https://github.com/bakaburg24/identity/tree/main/packages/decisioning-js#readme","bugs":{"url":"https://github.com/bakaburg24/identity/issues"},"dist":{"shasum":"75191b0d47b88f570e1ebfc8ae3c13365c3222b3","tarball":"https://registry.npmjs.org/@bakaburg24/decisioning-js/-/decisioning-js-0.1.0.tgz","fileCount":35,"integrity":"sha512-xW4wgkorL8c3946Vk+VhTwPpBuRK3A9DGOem/a3SgEwkOvc1ESOQJTqp3Mc8elnwfEHIT3iifzY5LiCUCS1aUQ==","signatures":[{"sig":"MEUCIQD4AdX+9cguW6Fzs5jrghUw0Eeil2DlS5y6dQsKtmTb8AIga1CN5HvxztM7TvHoEUd0X3DV0eytFYP1+jFj39YTOtI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":71549},"main":"./dist/index.js","type":"module","_from":"file:bakaburg24-decisioning-js-0.1.0.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit","typecheck:examples":"tsc --noEmit -p tsconfig.examples.json"},"_npmUser":{"name":"bakaburg24","email":"bakaburg24@gmail.com"},"_resolved":"/private/var/folders/g7/q5sv_7ss0mx66xg69js28kv40000gn/T/f9a16c2d2078a8911ec2d72368a8f861/bakaburg24-decisioning-js-0.1.0.tgz","_integrity":"sha512-xW4wgkorL8c3946Vk+VhTwPpBuRK3A9DGOem/a3SgEwkOvc1ESOQJTqp3Mc8elnwfEHIT3iifzY5LiCUCS1aUQ==","repository":{"url":"git+https://github.com/bakaburg24/identity.git","type":"git","directory":"packages/decisioning-js"},"_npmVersion":"11.5.1","description":"Decisioning platform SDK for browsers. Calls /v1/decide, manages anonymous_id, fires telemetry, renders via a tenant-supplied adapter. Zero runtime deps.","directories":{},"sideEffects":false,"_nodeVersion":"24.5.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5","@bakaburg24/decisioning-flag":"0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/decisioning-js_0.1.0_1778533136195_0.3226356134104651","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@bakaburg24/decisioning-js","version":"0.1.1","keywords":["decisioning","bandit","experimentation","ab-testing","thompson-sampling","epsilon-greedy","browser-sdk"],"author":{"name":"bakaburg24"},"license":"MIT","_id":"@bakaburg24/decisioning-js@0.1.1","maintainers":[{"name":"bakaburg24","email":"bakaburg24@gmail.com"}],"homepage":"https://github.com/bakaburg24/identity/tree/main/packages/decisioning-js#readme","bugs":{"url":"https://github.com/bakaburg24/identity/issues"},"dist":{"shasum":"b2e476cb7951213d1ade5b979d403634e93348e1","tarball":"https://registry.npmjs.org/@bakaburg24/decisioning-js/-/decisioning-js-0.1.1.tgz","fileCount":35,"integrity":"sha512-j9YWzQAcQw4/ljEa91C/4OeJ8L1m+9BhiBWO0+dNKRVdJvFKrMYxRxU92QsfWhG1SZJVtyU1mL1JY6dz8OKLzA==","signatures":[{"sig":"MEUCIDLhjrtArUbXr9R6Keq49v6v+AJ76kLd4Ws5I+qEnwe0AiEAoaB4rVosedNMTDNi3+TTDdnDh+xlwW4d+OACnyFkFmE=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":72095},"main":"./dist/index.js","type":"module","_from":"file:bakaburg24-decisioning-js-0.1.1.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit","typecheck:examples":"tsc --noEmit -p tsconfig.examples.json"},"_npmUser":{"name":"bakaburg24","email":"bakaburg24@gmail.com"},"_resolved":"/private/var/folders/g7/q5sv_7ss0mx66xg69js28kv40000gn/T/9dfee180c993f12249cf73e8c4e33419/bakaburg24-decisioning-js-0.1.1.tgz","_integrity":"sha512-j9YWzQAcQw4/ljEa91C/4OeJ8L1m+9BhiBWO0+dNKRVdJvFKrMYxRxU92QsfWhG1SZJVtyU1mL1JY6dz8OKLzA==","repository":{"url":"git+https://github.com/bakaburg24/identity.git","type":"git","directory":"packages/decisioning-js"},"_npmVersion":"11.5.1","description":"Decisioning platform SDK for browsers. Calls /v1/decide, manages anonymous_id, fires telemetry, renders via a tenant-supplied adapter. Zero runtime deps.","directories":{},"sideEffects":false,"_nodeVersion":"24.5.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5","@bakaburg24/decisioning-flag":"0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/decisioning-js_0.1.1_1778540562578_0.30356471123742645","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@bakaburg24/decisioning-js","version":"0.1.2","keywords":["decisioning","bandit","experimentation","ab-testing","thompson-sampling","epsilon-greedy","browser-sdk"],"author":{"name":"bakaburg24"},"license":"MIT","_id":"@bakaburg24/decisioning-js@0.1.2","maintainers":[{"name":"bakaburg24","email":"bakaburg24@gmail.com"}],"homepage":"https://github.com/bakaburg24/identity/tree/main/packages/decisioning-js#readme","bugs":{"url":"https://github.com/bakaburg24/identity/issues"},"dist":{"shasum":"8849b0ccb24a61679f4f466ee884c724230fa9ed","tarball":"https://registry.npmjs.org/@bakaburg24/decisioning-js/-/decisioning-js-0.1.2.tgz","fileCount":35,"integrity":"sha512-p7cfFVGMviyMOYR7bMVE/SM/w9if3uzMBygdJsmli2FL/B4caZDcgmHYJGM8cHZt85LqipGi0DacnZad1wxB2g==","signatures":[{"sig":"MEUCIHfMlCgfOrTlS2/wL+2WzMOxfSk/9vYt50G/j88eN2KuAiEA913qdNWTjn66HgO2+EkzDXfHb0smNytS6DToY9s94cM=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75265},"main":"./dist/index.js","type":"module","_from":"file:bakaburg24-decisioning-js-0.1.2.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit","typecheck:examples":"tsc --noEmit -p tsconfig.examples.json"},"_npmUser":{"name":"bakaburg24","email":"bakaburg24@gmail.com"},"_resolved":"/private/var/folders/g7/q5sv_7ss0mx66xg69js28kv40000gn/T/f2292480cb32b7d6d316edb4bca467bf/bakaburg24-decisioning-js-0.1.2.tgz","_integrity":"sha512-p7cfFVGMviyMOYR7bMVE/SM/w9if3uzMBygdJsmli2FL/B4caZDcgmHYJGM8cHZt85LqipGi0DacnZad1wxB2g==","repository":{"url":"git+https://github.com/bakaburg24/identity.git","type":"git","directory":"packages/decisioning-js"},"_npmVersion":"11.5.1","description":"Decisioning platform SDK for browsers. Calls /v1/decide, manages anonymous_id, fires telemetry, renders via a tenant-supplied adapter. Zero runtime deps.","directories":{},"sideEffects":false,"_nodeVersion":"24.5.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5","@bakaburg24/decisioning-flag":"0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/decisioning-js_0.1.2_1778711725450_0.9318522413396595","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@bakaburg24/decisioning-js","version":"0.1.3","keywords":["decisioning","bandit","experimentation","ab-testing","thompson-sampling","epsilon-greedy","browser-sdk"],"author":{"name":"bakaburg24"},"license":"MIT","_id":"@bakaburg24/decisioning-js@0.1.3","maintainers":[{"name":"bakaburg24","email":"bakaburg24@gmail.com"}],"homepage":"https://github.com/bakaburg24/identity/tree/main/packages/decisioning-js#readme","bugs":{"url":"https://github.com/bakaburg24/identity/issues"},"dist":{"shasum":"533feeb16d65ec518656d43bc5c5a8a9106e2ffc","tarball":"https://registry.npmjs.org/@bakaburg24/decisioning-js/-/decisioning-js-0.1.3.tgz","fileCount":39,"integrity":"sha512-dVy9mpglW3AWBg4+nhbt8QJxu4Soka2T6IZkGfB2rWf6fcYFNRHW7XngeZU23iVbIAu92OA8GVVr17EDupP9LQ==","signatures":[{"sig":"MEQCIEef86sPjFaapOqG1s2bF3Izh6RjM7sVH2XCOFr9rsQeAiBdDojXMuQkikM0O2KXOx9prlIprHiG1ihFDm+99CtAbg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":80957},"main":"./dist/index.js","type":"module","_from":"file:bakaburg24-decisioning-js-0.1.3.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit","typecheck:examples":"tsc --noEmit -p tsconfig.examples.json"},"_npmUser":{"name":"bakaburg24","email":"bakaburg24@gmail.com"},"_resolved":"/private/var/folders/g7/q5sv_7ss0mx66xg69js28kv40000gn/T/fa29027a9a560309a0f128df324f6c20/bakaburg24-decisioning-js-0.1.3.tgz","_integrity":"sha512-dVy9mpglW3AWBg4+nhbt8QJxu4Soka2T6IZkGfB2rWf6fcYFNRHW7XngeZU23iVbIAu92OA8GVVr17EDupP9LQ==","repository":{"url":"git+https://github.com/bakaburg24/identity.git","type":"git","directory":"packages/decisioning-js"},"_npmVersion":"11.5.1","description":"Decisioning platform SDK for browsers. Calls /v1/decide, manages anonymous_id, fires telemetry, renders via a tenant-supplied adapter. Zero runtime deps.","directories":{},"sideEffects":false,"_nodeVersion":"24.5.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5","@bakaburg24/decisioning-flag":"0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/decisioning-js_0.1.3_1778720717793_0.5408038455412605","host":"s3://npm-registry-packages-npm-production"}},"0.1.4":{"name":"@bakaburg24/decisioning-js","version":"0.1.4","keywords":["decisioning","bandit","experimentation","ab-testing","thompson-sampling","epsilon-greedy","browser-sdk"],"author":{"name":"bakaburg24"},"license":"MIT","_id":"@bakaburg24/decisioning-js@0.1.4","maintainers":[{"name":"bakaburg24","email":"bakaburg24@gmail.com"}],"homepage":"https://github.com/bakaburg24/identity/tree/main/packages/decisioning-js#readme","bugs":{"url":"https://github.com/bakaburg24/identity/issues"},"dist":{"shasum":"567c98c66db0d4a44035f01bb51a69d76460cdc5","tarball":"https://registry.npmjs.org/@bakaburg24/decisioning-js/-/decisioning-js-0.1.4.tgz","fileCount":47,"integrity":"sha512-f2TSaejwadhjRDmI9PcbDLEWwHnVjLwiJ1Rr0QOLdpp2w5dAzsIQBuDe7rgp5TYlFjAMLXB3XWC60Y2ovnYJMA==","signatures":[{"sig":"MEYCIQD5/7Vw7/+C2/iRhkxrMw4Wx4zKk5G93RtMKSICcvvmXgIhAO/ExQww32daEN465OVVn310oQE+F5p8Bj7j6pkh49qG","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":120261},"main":"./dist/index.js","type":"module","_from":"file:bakaburg24-decisioning-js-0.1.4.tgz","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit","typecheck:examples":"tsc --noEmit -p tsconfig.examples.json"},"_npmUser":{"name":"bakaburg24","email":"bakaburg24@gmail.com"},"_resolved":"/private/var/folders/g7/q5sv_7ss0mx66xg69js28kv40000gn/T/4176f79b6a5acafef0afc90d46be79a7/bakaburg24-decisioning-js-0.1.4.tgz","_integrity":"sha512-f2TSaejwadhjRDmI9PcbDLEWwHnVjLwiJ1Rr0QOLdpp2w5dAzsIQBuDe7rgp5TYlFjAMLXB3XWC60Y2ovnYJMA==","repository":{"url":"git+https://github.com/bakaburg24/identity.git","type":"git","directory":"packages/decisioning-js"},"_npmVersion":"11.5.1","description":"Decisioning platform SDK for browsers. Calls /v1/decide, manages anonymous_id, fires telemetry, renders via a tenant-supplied adapter. Zero runtime deps.","directories":{},"sideEffects":false,"_nodeVersion":"24.5.0","publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"typescript":"^5","@bakaburg24/decisioning-flag":"0.1.0"},"_npmOperationalInternal":{"tmp":"tmp/decisioning-js_0.1.4_1778725942078_0.23001600754150164","host":"s3://npm-registry-packages-npm-production"}},"0.1.5":{"name":"@bakaburg24/decisioning-js","version":"0.1.5","description":"Decisioning platform SDK for browsers. Calls /v1/decide, manages anonymous_id, fires telemetry, renders via a tenant-supplied adapter. Zero runtime deps.","type":"module","main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"keywords":["decisioning","bandit","experimentation","ab-testing","thompson-sampling","epsilon-greedy","browser-sdk"],"author":{"name":"bakaburg24"},"license":"MIT","homepage":"https://github.com/bakaburg24/identity/tree/main/packages/decisioning-js#readme","repository":{"type":"git","url":"git+https://github.com/bakaburg24/identity.git","directory":"packages/decisioning-js"},"bugs":{"url":"https://github.com/bakaburg24/identity/issues"},"sideEffects":false,"publishConfig":{"access":"public"},"devDependencies":{"typescript":"^5","@bakaburg24/decisioning-flag":"0.1.0"},"scripts":{"build":"tsc -p tsconfig.json","typecheck":"tsc --noEmit","typecheck:examples":"tsc --noEmit -p tsconfig.examples.json"},"_id":"@bakaburg24/decisioning-js@0.1.5","_integrity":"sha512-2eJEE0Rxr+uaKltu1Zxu9GV2nPcA/LYocOcpm1XoPv91pinhfmvswhahLKMxYbucAfZ+iHy/fynK/adackCmOg==","_resolved":"/private/var/folders/g7/q5sv_7ss0mx66xg69js28kv40000gn/T/d097d5af7e2435d5ebd5d5137739695c/bakaburg24-decisioning-js-0.1.5.tgz","_from":"file:bakaburg24-decisioning-js-0.1.5.tgz","_nodeVersion":"24.5.0","_npmVersion":"11.5.1","dist":{"integrity":"sha512-2eJEE0Rxr+uaKltu1Zxu9GV2nPcA/LYocOcpm1XoPv91pinhfmvswhahLKMxYbucAfZ+iHy/fynK/adackCmOg==","shasum":"cb93ec12335685af326e6e4dc8223f3d809ebf7c","tarball":"https://registry.npmjs.org/@bakaburg24/decisioning-js/-/decisioning-js-0.1.5.tgz","fileCount":47,"unpackedSize":122243,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIAeT2t5y9uBJcSzdu7bDlPtI1Sinipv5xOo9YI71BakKAiEA+6hahLyUarzv6Vx56nH/tsN0lFsRJTommmlSqxmbceI="}]},"_npmUser":{"name":"bakaburg24","email":"bakaburg24@gmail.com"},"directories":{},"maintainers":[{"name":"bakaburg24","email":"bakaburg24@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/decisioning-js_0.1.5_1778745052757_0.44333692569843497"},"_hasShrinkwrap":false}},"time":{"created":"2026-05-11T20:58:56.120Z","modified":"2026-05-14T07:50:53.018Z","0.1.0":"2026-05-11T20:58:56.362Z","0.1.1":"2026-05-11T23:02:42.698Z","0.1.2":"2026-05-13T22:35:25.673Z","0.1.3":"2026-05-14T01:05:17.931Z","0.1.4":"2026-05-14T02:32:22.234Z","0.1.5":"2026-05-14T07:50:52.912Z"},"bugs":{"url":"https://github.com/bakaburg24/identity/issues"},"author":{"name":"bakaburg24"},"license":"MIT","homepage":"https://github.com/bakaburg24/identity/tree/main/packages/decisioning-js#readme","keywords":["decisioning","bandit","experimentation","ab-testing","thompson-sampling","epsilon-greedy","browser-sdk"],"repository":{"type":"git","url":"git+https://github.com/bakaburg24/identity.git","directory":"packages/decisioning-js"},"description":"Decisioning platform SDK for browsers. Calls /v1/decide, manages anonymous_id, fires telemetry, renders via a tenant-supplied adapter. Zero runtime deps.","maintainers":[{"name":"bakaburg24","email":"bakaburg24@gmail.com"}],"readme":"# @bakaburg24/decisioning-js\n\nBrowser SDK for the decisioning platform. Drop it on your site once — the platform decides what to show, when, and to whom.\n\n```bash\npnpm add @bakaburg24/decisioning-js\n```\n\n## Quick start — autonomous mode (recommended)\n\nYou declare slots once. The SDK observes user behavior (page-views, scroll, dwell, idle, exit-intent), calls `/v1/decide` per slot, installs the variant's trigger, and renders only when the platform-supplied trigger fires.\n\n```ts\nimport { DecisioningClient } from '@bakaburg24/decisioning-js';\n\nconst client = new DecisioningClient({\n  apiKey: process.env.NEXT_PUBLIC_DECISIONING_KEY!,\n  endpoint: 'https://decision-service-production.up.railway.app',\n  eventsEndpoint: 'https://event-ingest-production-5912.up.railway.app',\n  consent: { behavioral: true, cookies: true },\n  renderAdapter: async (variant, ctx) => {\n    const el = document.createElement('div');\n    el.innerHTML = `\n      <h3>${variant.creative.headline}</h3>\n      <p>${variant.creative.subhead}</p>\n      <button>${variant.creative.cta}</button>\n    `;\n    el.querySelector('button')!.addEventListener('click', () => {\n      ctx.sdk.trackConversion({ decisionId: ctx.decisionId, outcomeName: 'newsletter_signup' });\n      ctx.sdk.unmount(ctx.decisionId);\n    });\n    ctx.target.appendChild(el);\n    return { unmount: () => el.remove() };\n  },\n});\n\n// Once per page. SDK does everything else.\nclient.observe([\n  { name: 'newsletter', target: () => document.getElementById('newsletter-slot') },\n  { name: 'cross_promo', target: () => document.getElementById('cross-promo-slot') },\n]);\n```\n\nThat's it. The operator hardcodes nothing about *when* to show — variants carry their trigger config, set by the AI generator or by the operator in the wizard. The platform's bandit (Phase 2+) learns which trigger+variant combo wins per audience.\n\n### What `observe()` does behind the scenes\n\n| Step | Detail |\n|---|---|\n| 1. Telemetry start | Fires `page-view` event (URL, referrer, locale, device class, viewport). Arms scroll-depth milestones (25/50/75/100%) and a 30s dwell pulse. |\n| 2. Decisions | Calls `/v1/decide` once per slot with the SDK-managed `anonymous_id` + your `context`. |\n| 3. Trigger install | Reads the variant's `trigger` (immediate / scroll / dwell / idle / exit-intent / custom-event) and installs the matching listener. |\n| 4. Render | When the trigger fires, the SDK looks up the slot's `target`, calls the render adapter, fires the impression event server-side (via decision-service). |\n| 5. Cleanup | `stopObserving()` tears down all listeners. Call it on SPA route change before the next `observe()`. |\n\n### Custom trigger from your code\n\nIf a variant uses `custom-event` type, dispatch the matching event from your own code at the right moment:\n\n```ts\n// Variant has trigger { type: 'custom-event', name: 'puzzle:completed' }\ndocument.dispatchEvent(new CustomEvent('puzzle:completed'));\n```\n\nThis lets you bridge app-specific moments into the platform's trigger model without hardcoding \"when\" elsewhere.\n\n## Manual mode — single decision\n\nWhen you want to control `/decide` explicitly (e.g. server-rendered slots, deferred mount):\n\n```ts\nawait client.decideAndRender({\n  slot: 'newsletter',\n  context: { page: { topic: 'finance' } },\n  target: document.getElementById('newsletter-slot')!,\n});\n```\n\nReturns the `DecideResponse` so you can inspect or skip rendering yourself.\n\n## Anonymous ID + consent\n\n- **Anonymous ID** is generated on first call, stored in `localStorage` (1-year sliding expiry), falls back to cookie, then to per-page UUID.\n- **Consent gating** — when `consent.behavioral` is `false`, the SDK omits the persisted `anonymous_id` (sends `\"anonymous\"`) so decisions are context-only.\n- **Update at runtime** — `client.setConsent({ behavioral: true, cookies: true })`. Existing cached decisions are not invalidated.\n\n## Telemetry events fired automatically\n\n| Event | When | Payload |\n|---|---|---|\n| `engagement:page_view` | Every `observe()` call | URL, referrer, locale, device class, viewport |\n| `engagement:scroll_depth` | Crossing 25 / 50 / 75 / 100% | `depth_pct` |\n| `engagement:dwell_pulse` | Every 30s while tab is foreground | `interval_ms` |\n| `impression` | When a variant renders | Server-emitted by decision-service |\n\nManual events (call from your code):\n\n```ts\nclient.trackEngagement({ decisionId, engagementType: 'click', payload: { target: 'cta' } });\nclient.trackConversion({ decisionId, outcomeName: 'newsletter_signup', value: 1.0 });\nclient.trackDismiss({ decisionId, dismissedAfterMs: 4200 });\n```\n\n## Render adapter contract\n\n```ts\ntype RenderAdapter = (\n  variant: Variant,\n  ctx: { decisionId: string; target: HTMLElement; sdk: DecisioningClientApi },\n) => Promise<{ unmount: () => void } | void> | { unmount: () => void } | void;\n```\n\nReturn `{ unmount }` so `client.unmount(decisionId)` can clean up on route change.\n\n## What the SDK doesn't do\n\n- Bundle any framework. Render adapters are the framework boundary.\n- Retry failed `/v1/decide` (1000ms timeout by default, then cache fallback or no-op — retries hurt conversion more than they help). Override with `timeoutMs`.\n- Block page load. Initialize early; lazy slot targets are fine.\n\n## What a variant looks like (generic creative shape)\n\nA variant returned from `/v1/decide` carries everything needed to\nrender an end-to-end UX, not just copy:\n\n```ts\n{\n  variant_id: \"v_xxx\",\n  slot: \"newsletter\",\n  format: \"modal\" | \"slide-in\" | \"inline\" | ...,\n  placement: \"...\",\n  tags: [...],\n  creative: {\n    headline: string,\n    subhead: string,\n    cta: string,\n    kind?: \"newsletter\" | \"discount\" | \"upsell\" | \"lead_capture\" | \"survey\" | \"announcement\",\n    fields?: Array<{ type: \"email\" | \"text\" | \"textarea\" | \"tel\", name, label?, placeholder?, required? }>,\n    submit?:\n      | { kind: \"mailflix\" }\n      | { kind: \"external_link\", url: string }\n      | { kind: \"webhook\", url: string }\n      | { kind: \"none\" },\n    success_message?: string,\n    trigger?: { type, ... },\n  }\n}\n```\n\nRender adapters can read `creative.fields` + `creative.submit` and\nbuild the form dynamically. See the host-app example in\n[examples/](examples/) for a reference adapter that handles all four\nsubmit kinds.\n\nThe full schema + examples live in\n[`decisions/docs/VARIANT_CREATIVE_SHAPE.md`](https://github.com/bakaburg24/identity/blob/main/docs/VARIANT_CREATIVE_SHAPE.md).\n\n## Changelog\n\n### 0.1.5 — Forward all form values downstream\n\nThe bundled default renderer's Mailflix POST previously dropped\nevery form field except `email` and `name`. For survey or\nlead-capture variants with a textarea, the actual response\ncontent (typed by the user) was sent to the platform's\nconversion event payload but never reached Mailflix's metadata\n— so the operator could see \"someone converted\" but not what\nthey actually said.\n\nNow: every non-email/name form value gets nested into Mailflix's\nmetadata blob alongside `decision_id` + `variant_id`, so survey\nresponses are recoverable from Mailflix's subscriber UI. (Same\ndata also lands in the platform's events warehouse via\ntrackConversion's `properties` field — that's now visible on\nthe portal's decision drawer via a new \"Response\" card.)\n\n### 0.1.4 — One-file embed\n\nThe SDK now ships a bundled vanilla-DOM renderer that handles every\nvariant format (modal, slide-in, inline, bottombar, topbar) and submit\nkind (mailflix, external_link, webhook, none) without operator code.\n`renderAdapter` is now optional on the client constructor; `observe()`\ncan be called with no arguments and auto-discovers slots from the DOM\n(elements with `data-decisioning-slot=\"<name>\"`), falling back to a\nsingle floating slot when none exist.\n\nMinimal embed becomes:\n\n```ts\nimport { DecisioningClient } from '@bakaburg24/decisioning-js';\n\nconst client = new DecisioningClient({\n  apiKey: '...',\n  endpoint: 'https://decision-service-production.up.railway.app',\n  eventsEndpoint: 'https://event-ingest-production-5912.up.railway.app',\n  consent: { behavioral: true, cookies: true },\n});\nclient.observe(); // no slots arg, no renderAdapter — just works\n```\n\nBrand tokens auto-extract from the host page's CSS. The SDK reads\n`--decisioning-primary` (or `--primary`), `--decisioning-surface`,\n`--decisioning-text`, `--decisioning-radius`, `--decisioning-font`\nfrom `:root`, falls back to the host body's computed font + colors,\nthen to neutral defaults (indigo accent, system font, white surface).\nPass explicit `brand: { primary, surface, text, radius, font }` at\nclient init to override.\n\nConversion tracking decoupled from downstream POST: the bundled\nrenderer fires `trackConversion` the moment form submit / CTA click\nfires — *before* hitting Mailflix or the operator's webhook. A\nmisconfigured downstream key or transient 5xx no longer silently\nblinds the bandit. (Custom render adapters should follow the same\npattern.)\n\n### 0.1.3\n\n- Debug logging gate. Set `localStorage.dx_debug = '1'` in the\n  browser console and reload — the SDK then logs every lifecycle\n  moment to the console with the `[decisioning]` prefix:\n  `observe()` called, `/v1/decide` resolved (and what variant +\n  trigger was picked), trigger armed, trigger fired, render adapter\n  invoked, adapter returned. Useful for diagnosing \"why isn't my\n  variant rendering\" on production sites without redeploying.\n  Off by default — negligible runtime cost when the flag is\n  unset (one localStorage check at SDK init, cached).\n\n### 0.1.2\n\n- Exit-intent trigger fix. The previous implementation listened for\n  `mouseleave` on `document`, which is unreliable across browsers\n  (mouseleave doesn't bubble and `document`'s leave-boundary is\n  browser-dependent — variants configured with `trigger.type:\n  'exit-intent'` never actually fired in production). Now uses\n  `mouseout` on document with `relatedTarget=null` + `clientY<=0`,\n  the browser-portable signal for \"cursor crossed the top edge.\"\n\n### 0.1.1\n\n- `DEFAULT_TIMEOUT_MS` raised from 200 → 1000. The old default matched\n  decision-service's server-side latency target but didn't account for\n  browser→Railway network cost (TLS + RTT is 100–300ms from EU).\n  Server responses that beat the old timeout still fired impressions\n  (decision-service emits them async before responding), but the SDK\n  aborted the body read and the variant copy never swapped in. Override\n  via `timeoutMs` for same-region setups where 200ms is realistic.\n\n### 0.1.0\n\n- Initial release. `DecisioningClient`, `observe()`, render-adapter\n  contract, anonymous-id management, telemetry events.\n","readmeFilename":"README.md"}