{"_id":"@atlaskit/flag-client","_rev":"5-1a369dd6a451919ab6b3c71e366a6b3b","name":"@atlaskit/flag-client","dist-tags":{"latest":"1.0.3"},"versions":{"0.0.0-stage":{"name":"@atlaskit/flag-client","version":"0.0.0-stage","_id":"@atlaskit/flag-client@0.0.0-stage","stub":true,"maintainers":[{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"}],"dist":{"shasum":"e9814f112c8357d0abbe1e231748dd88f0d83186","tarball":"https://registry.npmjs.org/@atlaskit/flag-client/-/flag-client-0.0.0-stage.tgz","fileCount":2,"integrity":"sha512-I/dOGL3fLjkZBtPNd4qwvBsapcjwfZXhMjUvkSnCp1+ftW7YYYSoE95WlOUoYSiacMyXPAeUBnvQmS+xVJ+65A==","signatures":[{"sig":"MEQCIGcYoMqtPo9fyIwibeCveAqsNMGn4ucQwuiEqax7FQczAiBh8X7W55tL35BGixo+Ub1uiI4/juCQ/Yssy6jK8sjq7g==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":343},"_npmUser":{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"},"description":"Temporary package placeholder for staged publishing","directories":{}},"1.0.0":{"name":"@atlaskit/flag-client","version":"1.0.0","author":{"name":"Atlassian Pty Ltd"},"license":"Apache-2.0","_id":"@atlaskit/flag-client@1.0.0","maintainers":[{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"}],"homepage":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo#readme","bugs":{"url":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo/issues"},"dist":{"shasum":"496bfccfd5966f6523689d2eef65663acbd4ccbd","tarball":"https://registry.npmjs.org/@atlaskit/flag-client/-/flag-client-1.0.0.tgz","fileCount":70,"integrity":"sha512-9Ts+wlugLc4zkZDMA4RtbuWdGB+VIRKVd/wyjDV56xi143vVy6/X4RKuGpaFaMra2GyjH8DHN54fNO0xEfHI9g==","signatures":[{"sig":"MEYCIQCyMQNxhG35Rf7BqbyfxX6G85r0LUyrFvdOLCruD7t+nwIhAKLELeOfuu52SZCQo0cI+g22Myj/T12pU2OnzJ0T7iCn","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIDr8mU2m+dztsfIfN8/PIYiVE+vmBJqVxDtU4Isb3c8EAiEA9qPL3FiWgilhHroVko2TP46eaQ+c4ZkphSIlfazQlVQ=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":366589},"_from":"file:/tmp/package-1-6adf1dc7-e28c26dc46c8ff6d3628d836-1.tgz","_npmUser":{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"},"_resolved":"/tmp/package-1-6adf1dc7-e28c26dc46c8ff6d3628d836-1.tgz","atlassian":{"team":"MEP: Switcheroo"},"_integrity":"sha512-9Ts+wlugLc4zkZDMA4RtbuWdGB+VIRKVd/wyjDV56xi143vVy6/X4RKuGpaFaMra2GyjH8DHN54fNO0xEfHI9g==","repository":{"url":"git+https://bitbucket.org/atlassian/atlassian-frontend-monorepo.git","type":"git"},"_npmVersion":"10.8.2","description":"Browser-safe JavaScript client for reading server-evaluated feature flags from the Feature Flag Platform.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","dependencies":{"@babel/runtime":"^7.0.0"},"publishConfig":{"registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/flag-client_1.0.0_1793007130568_0.4886077140732348","host":"s3://npm-registry-packages-npm-production"}},"1.0.1":{"name":"@atlaskit/flag-client","version":"1.0.1","author":{"name":"Atlassian Pty Ltd"},"license":"Apache-2.0","_id":"@atlaskit/flag-client@1.0.1","maintainers":[{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"}],"homepage":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo#readme","bugs":{"url":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo/issues"},"dist":{"shasum":"17bb1bf1ace90faf83ffbf77cf54202625bb321d","tarball":"https://registry.npmjs.org/@atlaskit/flag-client/-/flag-client-1.0.1.tgz","fileCount":90,"integrity":"sha512-Flpuis9RKVSXKd9AhOSinOzBovmgdwHRF0CPGoDUmizYLp1E6Umpr0sVRNRmnl+3rF1fiHxgx5YlVzeapb283A==","signatures":[{"sig":"MEUCID76FfTdF45WMWj8hCBw004qfCHFSvNNWoetWCZYa9bAAiEA1kP3BnQM6kcKSDO/F22EssQPrZKQvAY6/dGikPlqlms=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEYCIQC/4D1YUhOUAmx9i7FtA6dFt1Q/4U7kShW3KAnX7C7UtAIhANAFNyVEF5jNkiPHtvseM2gqHZIOefXHd2i/c6fsxU7Y","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":408619},"_from":"file:/tmp/package-1-6ae1ae3d-eda8a7283f489c1e1e9d85b3-1.tgz","_npmUser":{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"},"_resolved":"/tmp/package-1-6ae1ae3d-eda8a7283f489c1e1e9d85b3-1.tgz","atlassian":{"team":"MEP: Switcheroo"},"_integrity":"sha512-Flpuis9RKVSXKd9AhOSinOzBovmgdwHRF0CPGoDUmizYLp1E6Umpr0sVRNRmnl+3rF1fiHxgx5YlVzeapb283A==","repository":{"url":"git+https://bitbucket.org/atlassian/atlassian-frontend-monorepo.git","type":"git"},"_npmVersion":"10.8.2","description":"Browser-safe JavaScript client for reading server-evaluated feature flags from the Feature Flag Platform.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","dependencies":{"@babel/runtime":"^7.0.0"},"publishConfig":{"registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/flag-client_1.0.1_1793175232843_0.35938859874196905","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@atlaskit/flag-client","version":"1.0.2","author":{"name":"Atlassian Pty Ltd"},"license":"Apache-2.0","_id":"@atlaskit/flag-client@1.0.2","maintainers":[{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"}],"homepage":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo#readme","bugs":{"url":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo/issues"},"dist":{"shasum":"7d8f5b90e9f2bf05fa7770c1c90ee272b86780e3","tarball":"https://registry.npmjs.org/@atlaskit/flag-client/-/flag-client-1.0.2.tgz","fileCount":90,"integrity":"sha512-8B2wDs/nD5uHE+I3vb5oAcZEdrOG+Pr7EY3Tj/V4tp1CamZkImj/BXE6hzqHY7ukJ5n9s5l+b1bxZhDyIifXXA==","signatures":[{"sig":"MEQCIDyXK1+KOBom4sPZKX+NhSGwbx1QHr9yD4SAVu3LsBXYAiAV2piPR+iaVNwvwPb0o+8P6dUGdCRyhRTe+5dmhCBpNg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"sig":"MEUCIFmT0hLaHDpU8yF6ee6PP5nEIJXnA+vHTtCnLtEbUMopAiEAgJBQKmv4D2vWgLv32Cwqc6u7+nHsuGlg/7n8uDF4neg=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":415188},"_from":"file:/tmp/package-1-6ae7ed54-f3759ea71d550b48f0d23d00-1.tgz","_npmUser":{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"},"_resolved":"/tmp/package-1-6ae7ed54-f3759ea71d550b48f0d23d00-1.tgz","atlassian":{"team":"MEP: Switcheroo"},"_integrity":"sha512-8B2wDs/nD5uHE+I3vb5oAcZEdrOG+Pr7EY3Tj/V4tp1CamZkImj/BXE6hzqHY7ukJ5n9s5l+b1bxZhDyIifXXA==","repository":{"url":"git+https://bitbucket.org/atlassian/atlassian-frontend-monorepo.git","type":"git"},"_npmVersion":"10.8.2","description":"Browser-safe JavaScript client for reading server-evaluated feature flags from the Feature Flag Platform.","directories":{},"sideEffects":false,"_nodeVersion":"20.20.2","dependencies":{"@babel/runtime":"^7.0.0"},"publishConfig":{"registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"tmp":"tmp/flag-client_1.0.2_1793584525622_0.8130734195566424","host":"s3://npm-registry-packages-npm-production"}},"1.0.3":{"_id":"@atlaskit/flag-client@1.0.3","bugs":{"url":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo/issues"},"dist":{"shasum":"ceb11190f29ee84606d1866a9234789ab8dd2b4d","tarball":"https://registry.npmjs.org/@atlaskit/flag-client/-/flag-client-1.0.3.tgz","fileCount":90,"integrity":"sha512-PRMNqm3glghC0tyPC85sEMhYYIOEmWlqFb6bzRQsRZIuT1K71bn5GkRIGaw+4hVg2MO/pzfZyJK1AIrWAhh4vQ==","signatures":[{"sig":"MEUCIFt/tU4DZrIWWztJYiqwjzWnzThsRzbHEXLxa3sG0oQCAiEA0wiq1drpf3mKJqlAtC/nSTTqNbto9pZqtwRNYYK85eI=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCIHbunWTVuc1Pe00LEpNYJNqcv8GvZJCwX1BASFcp8kqVAiA+3059l1u0dpfx9CMLVfwfBJ/tWZJu5my5WRxJZxbJlg=="}],"unpackedSize":442930},"name":"@atlaskit/flag-client","_from":"file:/tmp/package-1-6ae822e7-da285e5659c825f7235d083b-1.tgz","author":{"name":"Atlassian Pty Ltd"},"license":"Apache-2.0","version":"1.0.3","_npmUser":{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"},"homepage":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo#readme","_resolved":"/tmp/package-1-6ae822e7-da285e5659c825f7235d083b-1.tgz","atlassian":{"team":"MEP: Switcheroo"},"_integrity":"sha512-PRMNqm3glghC0tyPC85sEMhYYIOEmWlqFb6bzRQsRZIuT1K71bn5GkRIGaw+4hVg2MO/pzfZyJK1AIrWAhh4vQ==","repository":{"url":"git+https://bitbucket.org/atlassian/atlassian-frontend-monorepo.git","type":"git"},"_npmVersion":"10.8.2","description":"Browser-safe JavaScript client for reading server-evaluated feature flags from the Feature Flag Platform.","directories":{},"maintainers":[{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"}],"sideEffects":false,"_nodeVersion":"20.20.2","dependencies":{"@babel/runtime":"^7.0.0"},"publishConfig":{"registry":"https://registry.npmjs.org/"},"_hasShrinkwrap":false,"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/flag-client_1.0.3_1793598312076_0.8388476779476216"}}},"time":{"created":"2026-10-26T09:30:51.199Z","modified":"2026-11-02T05:45:12.393Z","0.0.0-stage":"2026-10-26T09:30:51.199Z","1.0.0":"2026-10-26T09:32:10.703Z","1.0.1":"2026-10-28T08:13:52.974Z","1.0.2":"2026-11-02T01:55:25.706Z","1.0.3":"2026-11-02T05:45:12.149Z"},"bugs":{"url":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo/issues"},"author":{"name":"Atlassian Pty Ltd"},"license":"Apache-2.0","homepage":"https://bitbucket.org/atlassian/atlassian-frontend-monorepo#readme","repository":{"url":"git+https://bitbucket.org/atlassian/atlassian-frontend-monorepo.git","type":"git"},"description":"Browser-safe JavaScript client for reading server-evaluated feature flags from the Feature Flag Platform.","maintainers":[{"name":"atlassianartifactteam","email":"eng-development-tooling-artifacts@atlassian.com"}],"readme":"# @atlaskit/flag-client\n\nBrowser-safe JavaScript client for reading server-evaluated feature flags from a\n`BulkEvaluationPayload`.\n\nThe client does not fetch, poll, subscribe, or evaluate flag rules locally. A caller, normally the\n`@atlaskit/feature-gate-js-client` wrapper, obtains a payload (from the Feature Flag Service\n`/evalFlags` response or a server-rendered bootstrap) and hands it to the client through a\nsynchronous provider.\n\nThis package is the FFP JavaScript client. It is maintained here, in the Atlassian Frontend\nmonorepo. Its behaviour is specified by the `client-js-*` capabilities under\n[`openspec/specs/`](https://bitbucket.org/atlassian/feature-flag-platform/src/main/openspec/specs/)\nin the `feature-flag-platform` repository.\n\n## Install and import\n\n```bash\nyarn add @atlaskit/flag-client\n```\n\n```ts\nimport { FlagClient } from '@atlaskit/flag-client/client';\nimport type { BulkEvaluationPayload, FlagUser } from '@atlaskit/flag-client/types';\n```\n\n## Payload shape\n\n`BulkEvaluationPayload`, which providers return, is the union of the payload wire formats this\nclient supports, discriminated by `vs`. It currently has one member, `BulkEvaluationPayloadV1`. A\nfuture schema version may have an entirely different shape and will always carry its `vs`.\n\nA `BulkEvaluationPayloadV1` is an object with:\n\n- `vs` (optional): the payload schema version. The only supported version is `\"1\"`, and an absent\n  `vs` is treated as `\"1\"`. A payload without `vs` and an otherwise identical payload with `vs: \"1\"`\n  produce identical flag values and details metadata. Any other value, including `null` or the\n  number `1`, fails initialization or replacement with `invalid_schema_version` before the rest of\n  the payload is validated.\n- `flags` (required): a `BulkEvaluationResult`, which maps each flag key to a `FlagResult`. Its\n  shape matches the `flags` member of the Feature Flag Service `ffp` payload. It may be empty.\n- `requestContext` (required): the identifiers and attributes the payload was evaluated for. It must\n  be well formed, but the client does not use it for reads or details. A payload without it fails\n  with `invalid_payload_shape`. Its `attributes` is optional and an absent `attributes` is retained\n  as `{}`; a `null` `attributes` is rejected with `invalid_payload_shape`.\n- `clientVersion` (optional): the version of the flag data, `{ token, updatedAt? }`, matching the\n  Feature Flag Service `FfpBootstrapPayload.ClientVersion`. When present, `token` must be a\n  non-empty string and `updatedAt` a string. The client keeps it with the payload, swaps it on\n  `replace`, and does not use it for reads or details.\n\nA `null` `requestContext` or `clientVersion` is invalid. Other payload members, such as `status` or\n`targetApp`, are ignored, so an `ffp` object can be passed as-is. Unknown members inside flag\nresults and their metadata are also ignored.\n\nThis input intentionally differs from the server SDK `BulkEvaluationPayload` described in\n[`sdks/README.md`](https://bitbucket.org/atlassian/feature-flag-platform/src/main/sdks/README.md):\nit has no payload-level `status`.\n\nEach flag result's metadata may also carry `holdoutAllocation`, `segments`, `targetApps`,\n`flagVersion`, and `errorMessage`. When present they must be an object or `null`, an array of\nobjects, an array of strings, a string, and a string respectively. A `holdoutAllocations` list from\nan older producer is ignored.\n\n## Initialize\n\nConstruct one client per target app and environment, then initialize it. Initialization is\nsynchronous, one-time per client instance, and never throws.\n\n```ts\nconst client = new FlagClient({\n\ttargetApp: 'jira-frontend',\n\tenvironmentType: 'prod', // 'dev' | 'local' | 'staging' | 'prod'\n\tserviceVersion: appVersion, // optional\n});\nconst user: FlagUser = {\n\tidentifiers: { atlassianAccountId: accountId },\n\tattributes: { plan: 'premium' }, // optional\n};\n\nconst init = client.initialize(() => payload, user);\n\nif (!init.initialized) {\n\t// Reads return safe defaults. The SDK logs the failure reason and a troubleshooting tip.\n}\n```\n\n- The constructor options are fixed for the life of the instance. `targetApp` and `environmentType`\n  are required and `serviceVersion` is optional. The client copies them without validating them at\n  runtime, never throws during construction, and does not use them for reads. They identify the\n  client in exposure telemetry.\n- The provider is invoked only during `initialize` or `replace`. The client keeps an immutable\n  snapshot of the payload it returns.\n- Failure reasons are `already_initialized`, `provider_threw`, `provider_returned_no_payload`,\n  `invalid_schema_version`, `invalid_payload_shape`, and `payload_capture_failed`. A failed\n  initialization, other than `already_initialized`, can be retried.\n\n### Users\n\nThe user is normalized into a frozen copy when it is stored:\n\n- Identifier values that are not strings are dropped.\n- Attribute values that are not strings, booleans, or finite numbers are dropped.\n- A user, `identifiers`, or `attributes` member that is not an object is treated as empty. Omitting\n  `attributes` is equivalent to `{}`.\n\nNormalization never fails initialization or replacement, and an empty identifier map is valid. When\nanything is dropped, the SDK logs one `console.warn(...)` per call that names the dropped keys but\nnever their values.\n\nThe client keeps the normalized user from every initialization attempt other than\n`already_initialized`, including one that fails, so exposure telemetry can attribute reads made\nbefore initialization succeeds.\n\n## Replace the payload and user\n\nUse `replace` when the user changes. It swaps the payload and user together; the constructor options\ndo not change.\n\n```ts\nconst outcome = client.replace(() => nextPayload, nextUser);\n\nif (!outcome.replaced) {\n\t// The previous payload and user are still served.\n}\n```\n\n`replace` fails with `not_initialized`, without invoking the provider, until `initialize` has\nsucceeded. Its other failure reasons match initialization's provider and payload reasons. Any failed\nreplacement keeps the previous payload and user, and the SDK logs the reason and a tip.\n\n## Read values and details\n\nTyped value reads return the initialized flag value when the payload can serve the requested flag\nand type. Otherwise, they return the caller-provided safe default.\n\n```ts\nconst enabled = client.getBooleanValue('admin-toolbar.enabled', false);\nconst label = client.getStringValue('button.label', 'Continue');\nconst maxResults = client.getNumberValue('search.max-results', 25);\nconst config = client.getObjectValue('ui.config', { density: 'comfortable' });\n```\n\nDetails reads return the same selected value plus metadata for the requested flag.\n\n```ts\nconst details = client.getBooleanDetails('admin-toolbar.enabled', false);\n\nconsole.log(details.value);\nconsole.log(details.metadata.matchReason);\nconsole.log(details.metadata.identifierValue); // from the current user\nconsole.log(details.metadata.holdoutAllocation);\n```\n\n`identifierValue` is looked up in the current user's identifiers using the result's\n`identifierType`, never in the payload's `requestContext`. `holdoutAllocation` is the result's\nallocation, or `null` when absent, and `segments` is the result's list, or an empty array when\nabsent. `targetApps`, `flagVersion`, and `errorMessage` are retained internally and are not part of\ndetails metadata.\n\n## Safe defaults and code defaults\n\nThe client returns the safe default when it is uninitialized, the flag is absent, the requested read\ntype does not match the flag result, the runtime value shape is unusable, or the result has\n`useCodeDefault = true`.\n\nFor `useCodeDefault = true`, details reads still preserve server metadata from the payload. Other\nfallback cases use client-generated metadata: `not_initialized`, `flag_not_found`, or\n`type_mismatch`.\n\n## Object and array values\n\nObject and array values selected from initialized payload state come from the client's immutable\npayload snapshot. Mutation attempts against those returned initialized values do not change later\nreads or details metadata. Clone a returned object or array before mutating it in application code.\n\nSafe default values are returned as provided by the caller.\n\n## Exposure telemetry\n\nIn a browser, every typed value or details read records one exposure: an OpenTelemetry log record in\nthe shared exposure schema (`sdks/design/otel-exposure-event-schema.md` in the feature-flag-platform\nrepository). The client batches exposures and posts them as OTLP/JSON to Flag Telemetry.\n\nExposure logging is on by default only when a global `window` exists at construction. In Node and\nSSR it is off unless `enabled: true` is passed. DOM test environments such as jsdom and happy-dom\ndefine `window`, so pass `enabled: false` in tests that must not post exposures. Configure it with\nthe `exposureLogging` constructor option:\n\n```ts\nconst client = new FlagClient({\n\ttargetApp: 'jira-frontend',\n\tenvironmentType: 'prod',\n\texposureLogging: {\n\t\tenabled: true, // default: whether a global `window` exists\n\t\tendpoint: 'https://xp.atlassian.com/ft/v1/logs', // default; or a same-origin path\n\t\tbatchSize: 100, // default: exposures that trigger an immediate delivery\n\t\tflushIntervalMs: 10_000, // default: how long a queued exposure waits\n\t},\n});\n```\n\nAn option that fails its type, such as `batchSize: 0`, `endpoint: 'relative/path'`, or an `endpoint`\nURL with a username or password, is ignored: its default applies and the client writes one\n`console.warn` naming the option, never its value. Construction never throws. A disabled client does\nnot check `endpoint`, `batchSize`, or `flushIntervalMs` at all, so passing one configuration to both\nbrowser and SSR clients is quiet.\n\n### What an exposure carries\n\n- `service.name` and `feature_flag.set.id` come from `targetApp`. `deployment.environment_type` and\n  `deployment.environment.name` come from `environmentType`, and `service.version` from\n  `serviceVersion`.\n- The Instrumentation Scope name and `atl.feature_flag.sdk.type` are `@atlaskit/flag-client`, and the\n  scope version is this package's version.\n- Identifiers and attributes come from the held payload's `requestContext`. While the client holds\n  no payload, they come from the current user, which after a failed `initialize` is the user passed\n  to it. Details `identifierValue` always comes from the current user.\n- `atl.feature_flag.result.source` is `bootstrap` when the read was made while the client held an\n  initialized payload, including for `flag_not_found` and `type_mismatch`, and `fallback` for a read\n  made before a successful `initialize`, even when `logExposure` replays it later.\n- Error reasons (`not_initialized`, `flag_not_found`, `type_mismatch`, `general_error`, and\n  `invalid_context`) carry no variant or rule, and their error message starts with `FFP evaluation\n  failed: <reason>`. `general_error` and `invalid_context` use the payload's `errorMessage` instead\n  when it is nonempty. A `not_initialized` message ends with the cause, such as `(no initialize\n  attempt completed)` or the most recent failure reason.\n- `type_mismatch` keeps the payload flag's version and target apps.\n- A holdout allocation becomes the `atl.feature_flag.holdout_allocation.*` attributes only when its\n  `holdoutKey`, `identifierType`, and `allocationReason` are strings, with `holdout_rule_id` only\n  for a string `holdoutRuleId`. Otherwise all four are omitted.\n\n### Suppress and log manually\n\nPass `{ disableExposureLogging: true }` to any read to skip its automatic exposure. Log one later,\nfor example when the user actually sees the feature, with `logExposure`:\n\n```ts\nconst details = client.getBooleanDetails('new-nav.enabled', false, {\n\tdisableExposureLogging: true,\n});\n// ...later, when the navigation is rendered:\nclient.logExposure(details);\n```\n\nFor details this client returned, including shallow copies, `logExposure` records the exposure the\nread would have produced, marked manual, even after a later `initialize` or `replace`. Details the\nclient did not return, such as ones rebuilt from JSON, use the client state current at the call. It\nnever throws and does nothing when exposure logging is disabled.\n\n### Deduplication, delivery, and persistence\n\n- An exposure with the same flag key, variation, rule, match reason, and identifiers as one queued\n  in the last ten minutes is discarded. Manual and automatic exposures share these windows. A\n  successful `replace` ends them all.\n- One request is in flight at a time, sent with `credentials: 'omit'`. Network errors, requests not\n  settled within 30 seconds, `429`, and `5xx` statuses are retried after 1 second and then 2\n  seconds. Any other non-`2xx` status drops the batch.\n- Each client has one circuit breaker. After 3 consecutive retryable failures it opens: exposures\n  stay queued, and one batch is sent about every 60 seconds to check whether the endpoint has\n  recovered. The first response that is not retryable closes it, and the queue is flushed. Retryable\n  failures never drop exposures, but from the first retryable failure until a delivery gets any\n  other outcome, the client keeps only the 1,000 most recent exposures in memory.\n- When the page is hidden, undelivered exposures are saved to `localStorage` under keys starting\n  `@atlaskit/flag-client:exposures:`, and so is each exposure queued while it stays hidden. The\n  next client constructed on the same origin delivers them to its own `endpoint`, whichever\n  endpoint the client that saved them was configured with. It sends them as soon as it takes them,\n  rather than after `flushIntervalMs`, unless its circuit breaker holds them back. Storage errors\n  never throw.\n- Every client on an origin shares a `localStorage` budget of 131,072 UTF-16 code units (256 KiB),\n  keys included, and saves only its most recent exposures that fit. The others stay in memory. There\n  is no separate limit on how many exposures are saved; the budget alone decides.\n- After a successful delivery, the client records the time under\n  `@atlaskit/flag-client:last-delivery`, at most once an hour. While deliveries are failing on an\n  origin with no recorded success in the last 7 days, such as one where a content blocker rejects\n  every request, the budget is 4,096 code units (8 KiB) instead.\n\n### Same-origin and cross-origin delivery\n\n`endpoint` also accepts a path that begins with a single `/`. The client resolves it once at\nconstruction against `window.location.origin`, so a product with a same-origin route to Flag\nTelemetry avoids CORS entirely, as GASv3 does:\n\n```ts\n// Delivers to https://<product host>/gateway/api/ft/v1/logs\nexposureLogging: { endpoint: '/gateway/api/ft/v1/logs' }\n```\n\nThe path above is illustrative; use the route your product actually serves. A path is ignored with a\nwarning when the client is enabled without a `window`, the origin is opaque, the path starts with\n`//` or `/\\`, or it resolves to another origin.\n\nWithout a path, exposures are posted cross-origin to `https://xp.atlassian.com/ft/v1/logs` as\n`application/json`, which requires a CORS preflight. Delivery from a product page then depends on\nthat route answering the preflight.\n\n## Development\n\nRun from the `platform/` directory:\n\n```bash\nafm test unit packages/measurement/flag-client\nafm ts check --project ./packages/measurement/flag-client/tsconfig.app.json\nafm lint eslint packages/measurement/flag-client\n```\n\nThe shared test vectors in `src/__tests__/__fixtures__/` are vendored from the feature-flag-platform\nrepository: `bulk-evaluation-fixtures.json` from `sdks/evaluation-test-vectors`, and\n`exposure-test-vectors/` (JSON files only) from `sdks/exposure-test-vectors`. Keep them in sync when\nthe shared fixtures change.\n","readmeFilename":"README.md"}