{"_id":"@derian-cordoba/ab-testing-sdk","_rev":"6-a3f26a0ddb1e5ba487dda60800f4688c","name":"@derian-cordoba/ab-testing-sdk","dist-tags":{"latest":"0.1.3"},"versions":{"0.1.0":{"name":"@derian-cordoba/ab-testing-sdk","version":"0.1.0","license":"MIT","_id":"@derian-cordoba/ab-testing-sdk@0.1.0","maintainers":[{"name":"derian-cordoba","email":"derianricardo451@gmail.com"}],"dist":{"shasum":"423788bb4e64027be5e373776618b7c5947d6403","tarball":"https://registry.npmjs.org/@derian-cordoba/ab-testing-sdk/-/ab-testing-sdk-0.1.0.tgz","fileCount":54,"integrity":"sha512-5mZ7TPzQWw1WWD86yV/HCO69NuR90j0nDUoVD1qCGQcWMYD/wIYbD9JjgVzlxK3N/Utey7tYIcZYzgWx9lv2Uw==","signatures":[{"sig":"MEYCIQDR3unDSNEW5Ed5f7RWKpBAFCLqmj8fwLkpwrsmmiV75QIhAO8PI0UZ5mhwYJY2AqEeS4xX0F1hLBGsAFN7wNTsH/8S","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":70565},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./vue":{"types":"./dist/vue.d.ts","import":"./dist/vue.js"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js"}},"gitHead":"fb68a95c8e2e34344b0deb0d713ef1c6303945ad","scripts":{"test":"vitest run","build":"tsc -p tsconfig.json","clean":"rm -rf dist .tmp","test:types":"tsc -p tsconfig.test.json --noEmit","test:watch":"vitest"},"_npmUser":{"name":"derian-cordoba","email":"derianricardo451@gmail.com"},"deprecated":"Wrong built app. Please upgrade.","_npmVersion":"11.6.2","description":"Server-authoritative A/B testing hydration SDK for JavaScript and TypeScript clients.","directories":{},"sideEffects":false,"_nodeVersion":"24.12.0","dependencies":{"dotenv":"^17.4.2"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.38","jsdom":"^29.1.1","react":"^19.2.7","vitest":"^4.1.8","react-dom":"^19.2.7","typescript":"^5.6.3","@types/node":"^25.9.3","@types/react":"^19.2.17","@vue/test-utils":"^2.4.11","@types/react-dom":"^19.2.3","@testing-library/react":"^16.3.2"},"peerDependencies":{"vue":">=3","react":">=18"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ab-testing-sdk_0.1.0_1781340719947_0.390723608975337","host":"s3://npm-registry-packages-npm-production"}},"0.1.1":{"name":"@derian-cordoba/ab-testing-sdk","version":"0.1.1","license":"MIT","_id":"@derian-cordoba/ab-testing-sdk@0.1.1","maintainers":[{"name":"derian-cordoba","email":"derianricardo451@gmail.com"}],"dist":{"shasum":"9c5068a8bb9cdb595883318e4fe74d02753e98ff","tarball":"https://registry.npmjs.org/@derian-cordoba/ab-testing-sdk/-/ab-testing-sdk-0.1.1.tgz","fileCount":84,"integrity":"sha512-A1e0PSgyBAckG3WY0U/1/Wr4+lpeVwedQMD403pNYp43iBS+LWob+fBZ8L8hWF4yk9+5owV01/LVz8S0SQj5Sw==","signatures":[{"sig":"MEYCIQCcuHNlg3uUwbP4z94/q2LaNhPphIxx29HvFEc4Ysh6cQIhAJTeZWbgPbJsZVFhdaSbPG65wIYngzmUdfYJ9xRK/hQJ","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":149528},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./vue":{"types":"./dist/vue.d.ts","import":"./dist/vue.js"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js"}},"gitHead":"1933eda7005d8c7f4a70c95370c883e9a8a7dc2c","scripts":{"test":"vitest run","build":"tsc -p tsconfig.json","clean":"rm -rf dist .tmp","test:types":"tsc -p tsconfig.test.json --noEmit","test:watch":"vitest"},"_npmUser":{"name":"derian-cordoba","email":"derianricardo451@gmail.com"},"deprecated":"Wrong built app. Please upgrade.","_npmVersion":"11.6.2","description":"Server-authoritative A/B testing hydration SDK for JavaScript and TypeScript clients.","directories":{},"sideEffects":false,"_nodeVersion":"24.12.0","dependencies":{"dotenv":"^17.4.2"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.38","jsdom":"^29.1.1","react":"^19.2.7","vitest":"^4.1.8","react-dom":"^19.2.7","typescript":"^5.6.3","@types/node":"^25.9.3","@types/react":"^19.2.17","@vue/test-utils":"^2.4.11","@types/react-dom":"^19.2.3","@testing-library/react":"^16.3.2"},"peerDependencies":{"vue":">=3","react":">=18"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ab-testing-sdk_0.1.1_1781384163210_0.6873231594068347","host":"s3://npm-registry-packages-npm-production"}},"0.1.2":{"name":"@derian-cordoba/ab-testing-sdk","version":"0.1.2","license":"MIT","_id":"@derian-cordoba/ab-testing-sdk@0.1.2","maintainers":[{"name":"derian-cordoba","email":"derianricardo451@gmail.com"}],"dist":{"shasum":"663ed3eb5a0028fc21405171b4e422f665e2c99d","tarball":"https://registry.npmjs.org/@derian-cordoba/ab-testing-sdk/-/ab-testing-sdk-0.1.2.tgz","fileCount":84,"integrity":"sha512-Kcfo6H74+OGkKYH1AIZb4B2zU3v53i5UyIduFQPtGja/GAliXqINT2kXOi5awg1oGWdxafNM7zzY5HT8cBXbWw==","signatures":[{"sig":"MEUCIQDqtR8HoztZD7/0wmMw6NnXnJHv9i4FE/R+uEO40GPefAIgOITqkHDqECEucuYvZhP7X4IBQJ/fgFiUgVdRNzSGvoo=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":149528},"main":"./dist/index.js","type":"module","types":"./dist/index.d.ts","module":"./dist/index.js","exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./vue":{"types":"./dist/vue.d.ts","import":"./dist/vue.js"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js"}},"gitHead":"f1353899ef24438bf4a8d1c614933b74640fa021","scripts":{"test":"vitest run","build":"tsc -p tsconfig.json","clean":"rm -rf dist .tmp","test:types":"tsc -p tsconfig.test.json --noEmit","test:watch":"vitest"},"_npmUser":{"name":"derian-cordoba","email":"derianricardo451@gmail.com"},"_npmVersion":"11.6.2","description":"Server-authoritative A/B testing hydration SDK for JavaScript and TypeScript clients.","directories":{},"sideEffects":false,"_nodeVersion":"24.12.0","dependencies":{"dotenv":"^17.4.2"},"_hasShrinkwrap":false,"devDependencies":{"vue":"^3.5.38","jsdom":"^29.1.1","react":"^19.2.7","vitest":"^4.1.8","react-dom":"^19.2.7","typescript":"^5.6.3","@types/node":"^25.9.3","@types/react":"^19.2.17","@vue/test-utils":"^2.4.11","@types/react-dom":"^19.2.3","@testing-library/react":"^16.3.2"},"peerDependencies":{"vue":">=3","react":">=18"},"peerDependenciesMeta":{"vue":{"optional":true},"react":{"optional":true}},"_npmOperationalInternal":{"tmp":"tmp/ab-testing-sdk_0.1.2_1781384365733_0.07566348296993608","host":"s3://npm-registry-packages-npm-production"}},"0.1.3":{"name":"@derian-cordoba/ab-testing-sdk","version":"0.1.3","description":"Server-authoritative A/B testing hydration SDK for JavaScript and TypeScript clients.","license":"MIT","type":"module","sideEffects":false,"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"},"./react":{"types":"./dist/react.d.ts","import":"./dist/react.js"},"./vue":{"types":"./dist/vue.d.ts","import":"./dist/vue.js"}},"main":"./dist/index.js","module":"./dist/index.js","types":"./dist/index.d.ts","scripts":{"build":"tsc -p tsconfig.json","clean":"rm -rf dist .tmp","test":"vitest run","test:watch":"vitest","test:types":"tsc -p tsconfig.test.json --noEmit"},"peerDependencies":{"react":">=18","vue":">=3"},"peerDependenciesMeta":{"react":{"optional":true},"vue":{"optional":true}},"devDependencies":{"@testing-library/react":"^16.3.2","@types/node":"^25.9.3","@types/react":"^19.2.17","@types/react-dom":"^19.2.3","@vue/test-utils":"^2.4.11","jsdom":"^29.1.1","react":"^19.2.7","react-dom":"^19.2.7","typescript":"^5.6.3","vitest":"^4.1.8","vue":"^3.5.38"},"dependencies":{"dotenv":"^17.4.2"},"gitHead":"1ac28ea0ae337da8978f0b1c39a04dff2741cb0c","_id":"@derian-cordoba/ab-testing-sdk@0.1.3","_nodeVersion":"24.12.0","_npmVersion":"11.6.2","dist":{"integrity":"sha512-4S+TK74zdwSVpvg4IfzmckjN4gErlrCL6LJl3f4CFkBwaMSGCmXUX2wNkfiWBqZuDfURgB28IF118MhZh979RQ==","shasum":"a7c6c8a0788d6122d346e338980aa5973b8f8e7d","tarball":"https://registry.npmjs.org/@derian-cordoba/ab-testing-sdk/-/ab-testing-sdk-0.1.3.tgz","fileCount":96,"unpackedSize":211935,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIGNG8MT375FSgpeglDUx0RnmQbV6OJJd33cu+o4TkhJsAiA+T/wOQrgkwc44+n25U8s0BMO1B2QKJ29KIunyue/haA=="}]},"_npmUser":{"name":"derian-cordoba","email":"derianricardo451@gmail.com"},"directories":{},"maintainers":[{"name":"derian-cordoba","email":"derianricardo451@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/ab-testing-sdk_0.1.3_1781394539062_0.9566541310616081"},"_hasShrinkwrap":false}},"time":{"created":"2026-06-13T08:51:59.740Z","modified":"2026-06-13T23:48:59.319Z","0.1.0":"2026-06-13T08:52:00.094Z","0.1.1":"2026-06-13T20:56:03.381Z","0.1.2":"2026-06-13T20:59:25.846Z","0.1.3":"2026-06-13T23:48:59.203Z"},"license":"MIT","description":"Server-authoritative A/B testing hydration SDK for JavaScript and TypeScript clients.","maintainers":[{"name":"derian-cordoba","email":"derianricardo451@gmail.com"}],"readme":"# AB Testing SDK\n\n`@derian-cordoba/ab-testing-sdk` is a small JavaScript/TypeScript SDK for consuming server-authoritative A/B test assignments.\n\nThis package is designed to work with the Laravel package in this ecosystem. The Laravel side resolves and persists sticky assignments, exposes them through SSR meta tags and the assignments API, and this SDK reads those assignments in the browser without re-bucketing.\n\n## Relationship to the Laravel package\n\nThis SDK is not a standalone experimentation engine.\n\nThe expected architecture is:\n\n- `laravel-ab-testing`: source of truth for experiment resolution, sticky assignment persistence, exposure/metrics, and the assignments API contract\n- `@derian-cordoba/ab-testing-sdk`: browser-side consumer of those server assignments for hydration, UI branching, and framework integration\n\nIn practice, the Laravel package should provide at least one of these integration points:\n\n- an SSR meta tag like:\n\n```html\n<meta\n  name=\"ab-testing:assignments\"\n  content='{\"unit_type\":\"user\",\"unit_key\":\"42\",\"assignments\":{\"checkout-button-color\":\"green\"}}'\n/>\n```\n\n- a versioned assignments endpoint like:\n\n```http\nGET /api/v1/ab-testing/assignments?unit_type=user&unit_key=42\nAccept: application/vnd.ab-testing.v1+json\n```\n\nIf you are using the Laravel package from this same ecosystem, this SDK should be treated as the browser/client companion to that package.\n\n## What this SDK does\n\n- reads the assignment the server already resolved\n- normalizes DOM and HTTP payloads into one in-memory shape\n- avoids client-side re-bucketing by default\n- exposes one shared client for runtime hydration and feature flag management\n- provides optional React and Vue integration helpers\n\n## What this SDK does not do\n\n- it does not define experiments\n- it does not hash or bucket users in the browser by default\n- it does not replace the Laravel package's resolver or persistence model\n- it does not track metrics or exposures on its own yet\n\n## Structure\n\nThe package uses a small layered architecture:\n\n- `src/domain`: contracts, guards, and shared errors\n- `src/application`: client factory and orchestration service\n- `src/infrastructure`: DOM and HTTP transport adapters\n- `src/presentation`: framework adapters for React and Vue\n\nThis keeps transport-specific logic away from the public contract and makes it easier to grow the SDK without turning `src/` into a flat utility folder.\n\n## Install\n\n```bash\nnpm install @derian-cordoba/ab-testing-sdk\n```\n\n## Environment configuration\n\nThe SDK can configure itself automatically from environment variables.\n\nThe root package includes a [`.env.example`](/Users/deriancordoba/Developer/personal/ab-testing/.env.example) file with the supported variables:\n\n```dotenv\nAB_TESTING_API_BASE_URL=\nAB_TESTING_ASSIGNMENTS_PATH=/api/v1/ab-testing/assignments\nAB_TESTING_FEATURE_FLAGS_PATH=/api/v1/ab-testing/feature-flags\nAB_TESTING_ACCEPT_HEADER=application/vnd.ab-testing.v1+json\nAB_TESTING_META_NAME=ab-testing:assignments\nAB_TESTING_UNIT_TYPE=user\nAB_TESTING_UNIT_KEY=42\n```\n\nThese variables are used by the SDK `EnvService` and the default client factory.\n\n### Supported variables\n\n- `AB_TESTING_API_BASE_URL`: optional backend origin such as `http://localhost:8765`\n- `AB_TESTING_ASSIGNMENTS_PATH`: assignments endpoint path or full URL\n- `AB_TESTING_FEATURE_FLAGS_PATH`: feature flags admin endpoint path or full URL\n- `AB_TESTING_ACCEPT_HEADER`: media type sent to the assignments API\n- `AB_TESTING_META_NAME`: SSR meta tag name used by `hydrateFromMeta()`\n- `AB_TESTING_UNIT_TYPE`: optional fallback unit type for application code\n- `AB_TESTING_UNIT_KEY`: optional fallback unit key for application code\n\n### Configuration precedence\n\nThe SDK resolves configuration in this order:\n\n1. built-in SDK defaults\n2. environment-derived values from `EnvService`\n3. explicit options passed by the consumer\n\nThat means you can rely on env defaults for shared setup and still override any field directly when creating the client.\n\n## Core concepts\n\n### Hydrated assignments\n\nEvery transport is normalized into the same client-side shape:\n\n```ts\ninterface HydratedAssignments {\n  unitType: string;\n  unitKey: string;\n  assignments: Record<string, string>;\n  source: \"meta\" | \"api\";\n}\n```\n\n### Server-authoritative behavior\n\nThe SDK is built around one rule: if the server already resolved the assignment, the browser should reuse it.\n\nThat prevents:\n\n- flicker during hydration\n- SSR/CSR mismatch between server HTML and browser state\n- assignment drift caused by client-side hashing differences\n\n## Usage\n\n### 1. SSR meta bootstrap\n\nUse this when the Laravel application injects a meta tag into the rendered HTML.\n\n```html\n<meta\n  name=\"ab-testing:assignments\"\n  content='{\"unit_type\":\"user\",\"unit_key\":\"42\",\"assignments\":{\"checkout-button-color\":\"green\"}}'\n/>\n```\n\n```ts\nimport {\n  createABClient,\n  readAssignmentsFromMeta,\n} from \"@derian-cordoba/ab-testing-sdk\";\n\nconst client = createABClient({\n  initial: readAssignmentsFromMeta(),\n});\n\nconst variant = client.getVariant(\"checkout-button-color\");\n```\n\nThis is the preferred path for SSR pages because it avoids an extra request and guarantees the browser reads the same assignment used by the server render.\n\n### 2. API bootstrap\n\nUse this when the page cannot hydrate from HTML or when the client needs to fetch assignments explicitly.\n\n```ts\nimport { createABClient } from \"@derian-cordoba/ab-testing-sdk\";\n\nconst client = createABClient({\n  assignmentsEndpoint: \"/api/v1/ab-testing/assignments\",\n  acceptHeader: \"application/vnd.ab-testing.v1+json\",\n});\n\nawait client.hydrateFromApi({\n  unitType: \"user\",\n  unitKey: \"42\",\n});\n```\n\nThe default expected media type is:\n\n```http\nAccept: application/vnd.ab-testing.v1+json\n```\n\n## Client API\n\n### `createABClient(options)`\n\nCreates the unified SDK client.\n\nThis factory now reads env-derived defaults automatically through `EnvService`, then applies any explicit `options` you pass.\n\n```ts\nimport { createABClient } from \"@derian-cordoba/ab-testing-sdk\";\n\nconst client = createABClient();\n```\n\nExample with an explicit override:\n\n```ts\nconst client = createABClient({\n  assignmentsEndpoint: \"https://api.example.test/api/v1/ab-testing/assignments\",\n  featureFlagsEndpoint:\n    \"https://api.example.test/api/v1/ab-testing/feature-flags\",\n});\n```\n\nThe same instance exposes:\n\n- runtime assignment methods such as `hydrateFromMeta()`, `hydrateFromApi()`, and `getVariant()`\n- feature flags admin methods such as `listFeatureFlags()`, `enableFeatureFlag()`, and `setFeatureFlagRollout()`\n- cache control through `clearCache()` when the optional in-memory cache is enabled\n\nOptional in-memory cache example:\n\n```ts\nconst client = createABClient({\n  cache: {\n    ttlMs: 60_000,\n    assignments: { enabled: true },\n    featureFlags: { enabled: true },\n  },\n});\n```\n\nCache behavior:\n\n- disabled by default\n- enabled only when `cache: true` or `cache: { ... }` is passed\n- assignment cache keys are scoped by `unitType` and `unitKey`\n- feature flag read caches are invalidated automatically after feature flag write operations\n\n### `createABClientFromEnv(envOptions?, clientOptions?)`\n\nCreates the unified client from a custom env source or prefix.\n\nThis is useful when your runtime does not use `process.env` directly, such as Vite with `import.meta.env`.\n\n```ts\nimport { createABClientFromEnv } from \"@derian-cordoba/ab-testing-sdk\";\n\nconst client = createABClientFromEnv(\n  {\n    source: import.meta.env,\n    prefix: \"VITE_AB_\",\n  },\n  {\n    initial: null,\n  }\n);\n```\n\n### `EnvService`\n\nReads SDK configuration from one env source and normalizes it into a stable runtime config.\n\n```ts\nimport { EnvService } from \"@derian-cordoba/ab-testing-sdk\";\n\nconst env = EnvService.fromProcessEnv();\nconst config = env.loadConfig();\nconst unit = env.loadUnitIdentity();\n```\n\nUse `fromSource()` when your host framework exposes env variables through a custom object:\n\n```ts\nconst env = EnvService.fromSource(import.meta.env, \"VITE_AB_\");\n```\n\nThe resolved config now includes both:\n\n- `assignmentsEndpoint`: assignments endpoint used by the unified client\n- `featureFlagsEndpoint`: feature flags admin endpoint used by the management client\n\n### `configureDotenv(options?)`\n\nLoads env variables from a dotenv file for Node.js or SSR entrypoints.\n\nThis helper should not be used in browser-only bundles. It is intended for server runtimes before you create the client.\n\n```ts\nimport {\n  configureDotenv,\n  createABClientFromEnv,\n} from \"@derian-cordoba/ab-testing-sdk\";\n\nawait configureDotenv();\n\nconst client = createABClientFromEnv();\n```\n\n### Compatibility factories\n\nThe SDK exports:\n\n- `createABClient(...)`\n- `createABClientFromEnv(...)`\n\nThese are the preferred entrypoints going forward.\n\n## Feature flags admin API\n\nThe SDK now includes a dedicated management client for the server-side feature\nflags API under `/api/v1/ab-testing/feature-flags`.\n\nThis client is intended for:\n\n- admin dashboards\n- internal tooling\n- CI/CD operational controls\n- rollout and kill-switch automation\n\nIt is not the runtime flag-evaluation client for end-user rendering. Runtime\nflag hydration should still come from a server-authoritative evaluated payload.\n\n### Admin client methods\n\nThe unified `ABTestingClient` instance exposes:\n\n- `listFeatureFlags(params?)`\n- `getFeatureFlag(key)`\n- `createFeatureFlag(params)`\n- `deleteFeatureFlag(key)`\n- `enableFeatureFlag(key)`\n- `disableFeatureFlag(key)`\n- `setFeatureFlagRollout(key, params)`\n- `activateFeatureFlagKillSwitch(key)`\n- `deactivateFeatureFlagKillSwitch(key)`\n- `setFeatureFlagConditions(key, params)`\n- `clearFeatureFlagConditions(key)`\n\n### Example\n\n```ts\nimport { createABClient } from \"@derian-cordoba/ab-testing-sdk\";\n\nconst client = createABClient({\n  featureFlagsEndpoint: \"http://localhost:8765/api/v1/ab-testing/feature-flags\",\n});\n\nawait client.enableFeatureFlag(\"dark-mode\");\n\nawait client.setFeatureFlagRollout(\"dark-mode\", {\n  rolloutPercentage: 25,\n});\n\nawait client.setFeatureFlagConditions(\"dark-mode\", {\n  conditions: [{ attribute: \"plan\", operator: \"equals\", expected: \"pro\" }],\n  conditionsLogic: \"all\",\n});\n```\n\n### React integration\n\nThe React adapter uses the unified `ABTestingClient` through one shared\nprovider and exposes cache-aware hooks for assignments and feature flag reads.\n\n```ts\nimport {\n  ABProvider,\n  useABClient,\n  useAssignments,\n  useFeatureFlag,\n  useFeatureFlags,\n  useFeatureFlagsAdminClient,\n} from \"@derian-cordoba/ab-testing-sdk/react\";\n```\n\nExample:\n\n```tsx\nimport React from \"react\";\nimport { createABClient } from \"@derian-cordoba/ab-testing-sdk\";\nimport {\n  ABProvider,\n  useAssignments,\n  useFeatureFlags,\n} from \"@derian-cordoba/ab-testing-sdk/react\";\n\nconst client = createABClient({\n  assignmentsEndpoint: \"http://localhost:8765/api/v1/ab-testing/assignments\",\n  featureFlagsEndpoint: \"http://localhost:8765/api/v1/ab-testing/feature-flags\",\n  cache: {\n    assignments: { enabled: true, ttlMs: 60_000 },\n    featureFlags: { enabled: true, ttlMs: 60_000 },\n  },\n});\n\nfunction Dashboard() {\n  const assignments = useAssignments({\n    apiParams: { unitType: \"user\", unitKey: \"42\" },\n    hydrateFromApiOnMount: true,\n  });\n  const flags = useFeatureFlags({ loadOnMount: true });\n\n  if (assignments.isLoading || flags.isLoading) {\n    return <div>Loading</div>;\n  }\n\n  return (\n    <div>\n      <pre>{JSON.stringify(assignments.assignments, null, 2)}</pre>\n      <pre>{JSON.stringify(flags.collection?.items ?? [], null, 2)}</pre>\n    </div>\n  );\n}\n\nexport function App() {\n  return (\n    <ABProvider client={client}>\n      <Dashboard />\n    </ABProvider>\n  );\n}\n```\n\nAvailable React helpers:\n\n- `ABProvider`\n- `FeatureFlagsAdminProvider`\n- `useABClient()`\n- `useAssignments()`\n- `useFeatureFlag()`\n- `useFeatureFlags()`\n- `useFeatureFlagsAdminClient()`\n\n### Vue integration\n\nThe Vue adapter also uses the unified `ABTestingClient` and exposes cache-aware\ncomposables for assignments and feature flag reads.\n\n```ts\nimport {\n  installABTesting,\n  useABClient,\n  useAssignments,\n  useFeatureFlag,\n  useFeatureFlags,\n  useFeatureFlagsAdminClient,\n} from \"@derian-cordoba/ab-testing-sdk/vue\";\n```\n\nExample:\n\n```ts\nimport { createApp, defineComponent, h } from \"vue\";\nimport { createABClient } from \"@derian-cordoba/ab-testing-sdk\";\nimport {\n  installABTesting,\n  useAssignments,\n  useFeatureFlags,\n} from \"@derian-cordoba/ab-testing-sdk/vue\";\n\nconst client = createABClient({\n  assignmentsEndpoint: \"http://localhost:8765/api/v1/ab-testing/assignments\",\n  featureFlagsEndpoint: \"http://localhost:8765/api/v1/ab-testing/feature-flags\",\n  cache: {\n    assignments: { enabled: true, ttlMs: 60_000 },\n    featureFlags: { enabled: true, ttlMs: 60_000 },\n  },\n});\n\nconst Root = defineComponent({\n  setup() {\n    const assignments = useAssignments({\n      apiParams: { unitType: \"user\", unitKey: \"42\" },\n      hydrateFromApiOnMount: true,\n    });\n    const flags = useFeatureFlags({ loadOnMount: true });\n\n    return () =>\n      h(\n        \"pre\",\n        JSON.stringify(\n          {\n            assignments: assignments.assignments.value,\n            flags: flags.collection.value?.items ?? [],\n          },\n          null,\n          2\n        )\n      );\n  },\n});\n\nconst app = createApp(Root);\ninstallABTesting(app, { client });\napp.mount(\"#app\");\n```\n\nAvailable Vue helpers:\n\n- `installABTesting(app, { client })`\n- `installFeatureFlagsAdmin(app, { client })`\n- `useABClient()`\n- `useAssignments()`\n- `useFeatureFlag()`\n- `useFeatureFlags()`\n- `useFeatureFlagsAdminClient()`\n\n### `readAssignmentsFromMeta(name?)`\n\nReads the SSR meta tag and returns normalized assignments.\n\nReturns `null` when the tag is missing or when the code runs outside the browser.\n\n### `normalizeAssignmentsDocument(document)`\n\nNormalizes the Laravel assignments API response into the SDK's in-memory shape.\n\nUseful if you want to bring your own fetch layer.\n\n```ts\nimport { normalizeAssignmentsDocument } from \"@derian-cordoba/ab-testing-sdk\";\n\nconst response = await fetch(\n  \"/api/v1/ab-testing/assignments?unit_type=user&unit_key=42\",\n  {\n    headers: {\n      Accept: \"application/vnd.ab-testing.v1+json\",\n    },\n  }\n);\n\nconst document = await response.json();\nconst hydrated = normalizeAssignmentsDocument(document);\n```\n\n### Client methods\n\nThe unified `ABTestingClient` instance exposes:\n\n- `getVariant(experimentKey)`: returns the variant key or `null`\n- `all()`: returns the full assignment map\n- `has(experimentKey)`: returns whether the experiment exists in the current state\n- `isHydrated()`: returns whether the client currently holds hydrated assignments\n- `unit()`: returns `{ unitType, unitKey }` or `null`\n- `replace(assignments)`: replaces the current in-memory state\n- `hydrateFromMeta(name?)`: reads and stores assignments from the SSR meta tag\n- `hydrateFromApi(params)`: fetches and stores assignments from the server API\n- `clearCache()`: clears all SDK-managed in-memory cache entries\n\n## React integration\n\n```ts\nimport {\n  ABProvider,\n  useABClient,\n  useAssignments,\n} from \"@derian-cordoba/ab-testing-sdk/react\";\n```\n\nExample:\n\n```tsx\nimport React from \"react\";\nimport {\n  createABClient,\n  readAssignmentsFromMeta,\n} from \"@derian-cordoba/ab-testing-sdk\";\nimport {\n  ABProvider,\n  useABClient,\n  useAssignments,\n} from \"@derian-cordoba/ab-testing-sdk/react\";\n\nconst client = createABClient({\n  initial: readAssignmentsFromMeta(),\n  cache: {\n    assignments: { enabled: true, ttlMs: 60_000 },\n  },\n});\n\nfunction CheckoutButton() {\n  const ab = useABClient();\n  const assignments = useAssignments();\n  const variant =\n    assignments.assignments[\"checkout-button-color\"] ??\n    ab.getVariant(\"checkout-button-color\");\n\n  return variant === \"green\" ? (\n    <button className=\"btn-green\">Buy now</button>\n  ) : (\n    <button className=\"btn-default\">Buy now</button>\n  );\n}\n\nexport function App() {\n  return (\n    <ABProvider client={client}>\n      <CheckoutButton />\n    </ABProvider>\n  );\n}\n```\n\n## Vue integration\n\n```ts\nimport {\n  installABTesting,\n  useABClient,\n  useAssignments,\n} from \"@derian-cordoba/ab-testing-sdk/vue\";\n```\n\nExample:\n\n```ts\nimport { createApp, computed } from \"vue\";\nimport {\n  createABClient,\n  readAssignmentsFromMeta,\n} from \"@derian-cordoba/ab-testing-sdk\";\nimport {\n  installABTesting,\n  useABClient,\n  useAssignments,\n} from \"@derian-cordoba/ab-testing-sdk/vue\";\n\nconst client = createABClient({\n  initial: readAssignmentsFromMeta(),\n  cache: {\n    assignments: { enabled: true, ttlMs: 60_000 },\n  },\n});\n\nconst app = createApp({\n  setup() {\n    const ab = useABClient();\n    const { assignments } = useAssignments();\n    const variant = computed(\n      () =>\n        assignments.value[\"checkout-button-color\"] ??\n        ab.getVariant(\"checkout-button-color\")\n    );\n\n    return { variant };\n  },\n});\n\ninstallABTesting(app, { client });\napp.mount(\"#app\");\n```\n\n## Error handling\n\nThe SDK exposes three error classes:\n\n- `ABTestingError`: base SDK error\n- `ABTestingParseError`: invalid meta payload or invalid JSON:API document\n- `ABTestingFetchError`: non-success HTTP response from the assignments endpoint\n\nExample:\n\n```ts\nimport { ABTestingFetchError } from \"@derian-cordoba/ab-testing-sdk\";\n\ntry {\n  await client.hydrateFromApi({ unitType: \"user\", unitKey: \"42\" });\n} catch (error) {\n  if (error instanceof ABTestingFetchError) {\n    console.error(error.status);\n  }\n}\n```\n\n## Development\n\nUseful scripts:\n\n```bash\nnpm run test:types\nnpm test\nnpm run test:watch\nnpm run build\n```\n\n## Reference implementation\n\nThe primary server-side reference for this SDK is the Laravel package in this ecosystem:\n\n- `laravel-ab-testing`\n\nThat package is responsible for:\n\n- versioned assignments endpoints\n- the `application/vnd.ab-testing.v1+json` media type\n- SSR assignment meta emission\n- sticky assignment persistence\n- experiment resolution on the server\n\nThis SDK should follow that contract rather than inventing a second source of truth in the browser.\n","readmeFilename":"README.md"}