{"_id":"@avsbhq/flags-adapter","_rev":"2-5767c6239494abc6a22b74986333b913","name":"@avsbhq/flags-adapter","dist-tags":{"latest":"1.0.3"},"versions":{"1.0.2":{"name":"@avsbhq/flags-adapter","version":"1.0.2","keywords":["avsb","feature-flags","ab-testing","experiments","flags-sdk","vercel","adapter","sdk"],"license":"MIT","_id":"@avsbhq/flags-adapter@1.0.2","maintainers":[{"name":"main1479","email":"m.main2402@gmail.com"}],"homepage":"https://github.com/avsbhq/a-vs-b/tree/main/packages/avsb-flags-adapter#readme","bugs":{"url":"https://github.com/avsbhq/a-vs-b/issues"},"dist":{"shasum":"176d37c4850951312812bc576b68724abb43caa3","tarball":"https://registry.npmjs.org/@avsbhq/flags-adapter/-/flags-adapter-1.0.2.tgz","fileCount":7,"integrity":"sha512-npna5pS0q8r3x2j9oazimYxeYlwfXcA09Q6gUhoSIbIGMONzVX034xwx4aqMji54wBVclpz1W1TSsJJ2DkqhWA==","signatures":[{"sig":"MEUCIQDjxgen++9cq/fRpBvDyjHlGnvlRaW0mFfAesw1heb1TgIgCArJ7bxb8JIbad/4ZjCeWzC8pllbXWKVh0zCchOKkVo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":46524},"main":"dist/index.cjs","type":"module","types":"dist/index.d.ts","module":"dist/index.js","exports":{".":{"import":{"types":"./dist/index.d.ts","default":"./dist/index.js"},"require":{"types":"./dist/index.d.cts","default":"./dist/index.cjs"}},"./package.json":"./package.json"},"gitHead":"5eed46d5944b522ba0aa34e82850c98bb91667fe","scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","typecheck":"tsc --noEmit"},"_npmUser":{"name":"main1479","email":"m.main2402@gmail.com"},"repository":{"url":"git+https://github.com/avsbhq/a-vs-b.git","type":"git","directory":"packages/avsb-flags-adapter"},"_npmVersion":"10.9.8","description":"A vs B adapter for the Flags SDK: evaluate A vs B feature flags and experiments from flag declarations, with server-side exposure tracking.","directories":{},"sideEffects":false,"_nodeVersion":"22.22.3","dependencies":{"@avsbhq/core":"^1.4.0","@avsbhq/node":"^1.6.2"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"tsup":"^8.0.0","vitest":"^4.1.4","typescript":"^5.5.0","@types/node":"^20.0.0"},"peerDependencies":{"flags":">=3.0.0"},"peerDependenciesMeta":{"flags":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/flags-adapter_1.0.2_1785971352299_0.9696589775600604","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"name":"@avsbhq/flags-adapter","version":"1.0.3","description":"A vs B adapter for the Flags SDK: evaluate A vs B feature flags and experiments from flag declarations, with server-side exposure tracking.","keywords":["avsb","feature-flags","ab-testing","experiments","flags-sdk","vercel","adapter","sdk"],"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"}},"./package.json":"./package.json"},"publishConfig":{"access":"public"},"repository":{"type":"git","url":"git+https://github.com/avsbhq/a-vs-b.git","directory":"packages/avsb-flags-adapter"},"homepage":"https://github.com/avsbhq/a-vs-b/tree/main/packages/avsb-flags-adapter#readme","bugs":{"url":"https://github.com/avsbhq/a-vs-b/issues"},"scripts":{"build":"tsup","dev":"tsup --watch","test":"vitest run","typecheck":"tsc --noEmit"},"dependencies":{"@avsbhq/core":"^1.4.0","@avsbhq/node":"^1.6.2"},"peerDependencies":{"flags":">=3.0.0"},"peerDependenciesMeta":{"flags":{"optional":true}},"devDependencies":{"@types/node":"^20.0.0","tsup":"^8.0.0","typescript":"^5.5.0","vitest":"^4.1.4"},"_id":"@avsbhq/flags-adapter@1.0.3","gitHead":"d31d88fbf202f40041b4cb47f77882f454fa5ef8","_nodeVersion":"22.22.3","_npmVersion":"10.9.8","dist":{"integrity":"sha512-6G+jTfea2TcRP9HDSOqSgC9MWfwLLvJjkJsg6WAiI5pkROKTG/HfBQT2I1dNAJkX4j3YRj0ht97Rrp+T3S0D0g==","shasum":"6e0b7845f9b4f7f491f9f640ac1cbf2b92f0c205","tarball":"https://registry.npmjs.org/@avsbhq/flags-adapter/-/flags-adapter-1.0.3.tgz","fileCount":7,"unpackedSize":46540,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIB+OKNXLcpkYfSvRUvxxklm86+XJKcxJwO4l4rxJCYy1AiEA/bBcwGy7WQnZ2RHT4jrg6O96ntBK+7FwgHnNuwjjNyc="}]},"_npmUser":{"name":"main1479","email":"m.main2402@gmail.com"},"directories":{},"maintainers":[{"name":"main1479","email":"m.main2402@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/flags-adapter_1.0.3_1786795911765_0.6711285125583772"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-05T23:09:12.108Z","modified":"2026-08-15T12:11:52.069Z","1.0.2":"2026-08-05T23:09:12.437Z","1.0.3":"2026-08-15T12:11:51.907Z"},"bugs":{"url":"https://github.com/avsbhq/a-vs-b/issues"},"license":"MIT","homepage":"https://github.com/avsbhq/a-vs-b/tree/main/packages/avsb-flags-adapter#readme","keywords":["avsb","feature-flags","ab-testing","experiments","flags-sdk","vercel","adapter","sdk"],"repository":{"type":"git","url":"git+https://github.com/avsbhq/a-vs-b.git","directory":"packages/avsb-flags-adapter"},"description":"A vs B adapter for the Flags SDK: evaluate A vs B feature flags and experiments from flag declarations, with server-side exposure tracking.","maintainers":[{"name":"main1479","email":"m.main2402@gmail.com"}],"readme":"# @avsbhq/flags-adapter\n\n[A vs B](https://app.avsb.cloud) adapter for the [Flags SDK](https://flags-sdk.dev).\n\nDeclare a flag once with `flag()`, point it at this adapter, and every read is a real A vs B evaluation: targeting rules, A/B splits, holdouts, bandits, and a server-side exposure so the decision shows up in your results.\n\n---\n\n## 1. Install\n\n```bash\nnpm install @avsbhq/flags-adapter flags\n```\n\n`flags` is a peer dependency. This package never imports it: it matches the adapter shape structurally, so a `flags` upgrade cannot break it at build time.\n\n---\n\n## 2. Quickstart\n\n```ts\n// flags.ts\nimport { flag } from 'flags/next';\nimport { avsbAdapter } from '@avsbhq/flags-adapter';\nimport type { EvalContext } from '@avsbhq/flags-adapter';\n\nexport const newCheckout = flag<boolean, EvalContext>({\n  key: 'new-checkout-flow',\n  adapter: avsbAdapter.booleanValue(),\n  defaultValue: false,\n  identify: ({ cookies }) => ({\n    kind: 'user',\n    key: cookies.get('uid')?.value ?? 'anonymous',\n  }),\n});\n```\n\n```tsx\n// app/page.tsx\nimport { newCheckout } from '../flags';\n\nexport default async function Page() {\n  const showNewCheckout = await newCheckout();\n  return showNewCheckout ? <NewCheckout /> : <Checkout />;\n}\n```\n\n`AVSB_SDK_KEY` in the environment is the only configuration `avsbAdapter` needs.\n\n---\n\n## 3. SDK keys\n\nGet the SDK key for the environment you want from your project: **app.avsb.cloud**, then **Settings**, then **Environments**. The format is `sdk_<environment>_<id>`, for example `sdk_production_ttqm0eaj4vth1krcb2xn`.\n\nThere is one SDK key per environment, and \"SDK key\" is its only name. There is no separate client key and server key to choose between.\n\nYour SDK key is a public identifier, not a secret: it is safe to ship in browser and mobile bundles, it can only fetch that environment's flag configuration and send events, and it can never read or change anything in your dashboard.\n\n```bash\nAVSB_SDK_KEY=sdk_production_ttqm0eaj4vth1krcb2xn\n```\n\nWith no key, the adapter logs one error naming the fix and every flag returns the `defaultValue` from its declaration. It never throws, so a missing key cannot take a page down.\n\n---\n\n## 4. The whole type surface\n\nEvery name below is exported by this package, with one exception: `Logger` is the shared logger interface from `@avsbhq/core`.\n\n```ts\nimport type { Adapter, AvsbClientFactory, AvsbEvaluator, EvalContext } from '@avsbhq/flags-adapter';\nimport type { Logger } from '@avsbhq/core';\n```\n\n```ts\nfunction createAvsbAdapter(options?: CreateAvsbAdapterOptions): AvsbAdapter;\n\n/** Reads AVSB_SDK_KEY from the environment. Builds its client on first use. */\nconst avsbAdapter: AvsbAdapter;\n\ninterface AvsbAdapter {\n  booleanValue(): Adapter<boolean, EvalContext>;\n  stringValue(): Adapter<string, EvalContext>;\n  numberValue(): Adapter<number, EvalContext>;\n  /** The fallback is required: there is no empty value for an arbitrary shape. */\n  jsonValue<T>(fallback: T): Adapter<T, EvalContext>;\n  /** Which arm of the experiment this request landed in. Null when nothing was decided. */\n  variationKey(): Adapter<string | null, EvalContext>;\n  /** The underlying client once ready, or null when it could not be created. */\n  avsbClient(): Promise<AvsbEvaluator | null>;\n  /** Flush queued events and stop polling. */\n  close(): Promise<void>;\n}\n\ninterface CreateAvsbAdapterOptions {\n  /** Defaults to process.env.AVSB_SDK_KEY. */\n  sdkKey?: string;\n  /** CDN base for datafile fetches. Default 'https://cdn.avsb.cloud'. */\n  cdnHost?: string;\n  /** A client you already run, or a factory for one. */\n  client?: AvsbEvaluator | AvsbClientFactory;\n  /** Record an exposure per decision. Default 'auto'. */\n  exposure?: 'auto' | 'off';\n  /** Cookie carrying the anonymous visitor id. Default 'avsb_anon_id'. */\n  anonCookieName?: string;\n  /** Where the Vercel Toolbar links this flag. */\n  origin?: string | ((flagKey: string) => string | undefined);\n  /** Defaults to the console at warn level. */\n  logger?: Logger;\n}\n```\n\nThe entities type is `EvalContext`, the same context object every A vs B SDK takes:\n\n```ts\ntype EvalContext = SingleContext | MultiContext;\n\ninterface SingleContext {\n  kind: string;\n  key: string;\n  [attribute: string]: unknown;\n}\n\ninterface MultiContext {\n  kind: 'multi';\n  [contextKind: string]: SingleContext | 'multi';\n}\n```\n\nMulti-context works here exactly as it does elsewhere, so a rule can bucket on `user.key` while matching an audience condition on `organization.tier`:\n\n```ts\nimport type { CookiesLike } from '@avsbhq/flags-adapter';\n\n// Hand this to a flag declaration as its `identify`.\nconst identifyUserAndOrg = ({ cookies }: { cookies: CookiesLike }): EvalContext => ({\n  kind: 'multi',\n  user: { kind: 'user', key: cookies.get('uid')?.value ?? 'anonymous' },\n  organization: { kind: 'organization', key: 'org_456', tier: 'enterprise' },\n});\n```\n\n---\n\n## 5. Identity\n\n`identify` is where a decision gets its visitor, and it is worth writing: without one, results are attributed to whoever the fallback finds.\n\nThe fallback, in order:\n\n1. The `avsb_anon_id` cookie. Set it server-side with the `withAvsb` middleware from `@avsbhq/next`, which maintains it on every request. Do not count on the browser SDK for it: the browser persists the same id in `localStorage` first and only writes a cookie when `localStorage` is unavailable, so on most sites this cookie exists only because your middleware wrote it.\n2. The web snippet's `_avsb_visitor` cookie, decoded the way the snippet writes it (percent-encoded JSON, id in `v`). When Web Experiments is installed on the same domain, this is what makes the flag decision and the experiment exposure land on one visitor instead of two. A cookie of that name that does not parse is ignored.\n3. Nothing. The flag returns its `defaultValue`, no exposure is recorded, and one warning per flag key says what to add.\n\nThat last case is deliberate. Inventing an id per request would split one visitor across every page view and quietly corrupt the experiment.\n\n`readSnippetVisitorId(rawCookieValue: string | null): string | null` is exported if you want that decoding inside your own `identify`.\n\n---\n\n## 6. Exposures\n\nEvery `decide` records an exposure by default, because a Flags SDK read happens at request time for one visitor: that is a decision, and results need it.\n\nTurn it off for flags you read early (middleware, a layout) and show later:\n\n```ts\nimport { createAvsbAdapter } from '@avsbhq/flags-adapter';\n\nexport const avsbAdapter = createAvsbAdapter({ exposure: 'off' });\n```\n\nThen record the exposure where the visitor actually sees the variation, either with `useExposure` from the framework SDK on the client, or `manualExposure` on a bound server client.\n\n---\n\n## 7. Bring your own client\n\nAn app that already runs `AvsbServer` should hand it over, so the process holds one datafile, one polling loop and one event queue:\n\n```ts\nimport { AvsbServer } from '@avsbhq/node';\nimport { createAvsbAdapter } from '@avsbhq/flags-adapter';\n\nexport const avsb = new AvsbServer({ sdkKey: process.env.AVSB_SDK_KEY ?? '' });\nexport const avsbAdapter = createAvsbAdapter({ client: avsb });\n```\n\n`AvsbServer` satisfies `AvsbEvaluator`, so nothing needs casting. Pass a factory instead of an instance to defer construction:\n\n```ts\nexport const lazyAvsbAdapter = createAvsbAdapter({\n  client: () => new AvsbServer({ sdkKey: process.env.AVSB_SDK_KEY ?? '' }),\n});\n```\n\n---\n\n## 8. Failure behaviour\n\nNothing in this adapter throws. Each of these ends with the flag's declared default value and one log line:\n\n| What happened                    | What you see                                                 |\n| -------------------------------- | ------------------------------------------------------------ |\n| No SDK key                       | One error naming `AVSB_SDK_KEY` and where the key lives.     |\n| Datafile never loaded            | One error with the HTTP status and the URL tried.            |\n| Nobody could be identified       | One warning per flag key naming `identify()` and the cookie. |\n| Flag key not in this environment | The SDK logs the unknown key; the default is served.         |\n\n---\n\n## 9. What this adapter does not do\n\n- It does not evaluate in the browser. The Flags SDK runs flags on the server; for client-side reading use `@avsbhq/react`, `@avsbhq/vue`, `@avsbhq/svelte`, `@avsbhq/solid` or `@avsbhq/angular`.\n- It does not expose `Flag<T>` metadata (reasons, rule ids) through `decide`, because a flag value has to survive serialisation. Reach for `avsbClient()` when you need the whole decision.\n- It is not an OpenFeature provider. That is a separate integration, documented in the A vs B docs.\n","readmeFilename":"README.md"}