{"_id":"@alphalang/alphalang","_rev":"4-adba6db089ea480ba3a4d14415be0257","name":"@alphalang/alphalang","dist-tags":{"latest":"0.0.6-alpha"},"versions":{"0.0.2-alpha":{"name":"@alphalang/alphalang","version":"0.0.2-alpha","description":"[AlphaLang](https://www.alphalang.ai) is an open source Feature Flagging and Experimentation platform.","main":"dist/index.js","scripts":{"build:clean":"rimraf dist","build:types":"tsc --project tsconfig.build.json","build":"yarn build:clean && yarn build:types","test":"jest"},"repository":{"type":"git","url":"git+https://github.com/alphalang-ai/alphalang.git","directory":"packages/alphalang"},"author":{},"license":"ISC","bugs":{"url":"https://github.com/alphalang-ai/alphalang/issues"},"homepage":"https://github.com/alphalang-ai/alphalang#readme","devDependencies":{"@types/jest":"^27.0.1","jest":"^27.1.1","typescript":"4.7.4"},"dependencies":{"@alphalang/alphalang-core":"^0.0.1","langchain":"^0.0.195"},"engines":{"node":">=18"},"_id":"@alphalang/alphalang@0.0.2-alpha","dist":{"shasum":"a87ff9e901ea68904fe9a25a44d62d8719d2eeeb","integrity":"sha512-dAczziO99Aa38jByAsCcxYjnHkjxWNSu+qBR04wUDgImEc0nIGPjv2p2FTeskAarHSsa/043R1fAaDOadq2M9A==","tarball":"https://registry.npmjs.org/@alphalang/alphalang/-/alphalang-0.0.2-alpha.tgz","fileCount":53,"unpackedSize":80672,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIC96ofXoutTvtZzrskJ5G7VeBQzWSKruFVvDnbxT662bAiB7EpMSwAM79xnkzOOrlpNnZDRdFg9Sb5xU+bkC4eClqQ=="}]},"_npmUser":{"name":"gshigeto","email":"gshigeto@gmail.com"},"directories":{},"maintainers":[{"name":"gshigeto","email":"gshigeto@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alphalang_0.0.2-alpha_1700596878545_0.9374069540651633"},"_hasShrinkwrap":false},"0.0.3-alpha":{"name":"@alphalang/alphalang","version":"0.0.3-alpha","description":"[AlphaLang](https://www.alphalang.ai) is an open source Feature Flagging and Experimentation platform.","main":"dist/index.js","type":"module","scripts":{"build:clean":"rimraf dist","build:types":"tsc --project tsconfig.build.json","build":"yarn build:clean && yarn build:types","test":"jest"},"repository":{"type":"git","url":"git+https://github.com/alphalang-ai/alphalang.git","directory":"packages/alphalang"},"author":{},"license":"ISC","bugs":{"url":"https://github.com/alphalang-ai/alphalang/issues"},"homepage":"https://github.com/alphalang-ai/alphalang#readme","devDependencies":{"@types/jest":"^27.0.1","jest":"^27.1.1","typescript":"4.7.4"},"dependencies":{"@alphalang/alphalang-core":"^0.0.1","@aws-crypto/sha256-js":"^5.2.0","@aws-sdk/types":"^3.451.0","@google-ai/generativelanguage":"^1.1.0","@smithy/eventstream-codec":"^2.0.14","@smithy/protocol-http":"^3.0.10","@smithy/signature-v4":"^2.0.16","@smithy/util-utf8":"^2.0.2","google-auth-library":"^9.2.0","langchain":"^0.0.195"},"engines":{"node":">=18"},"_id":"@alphalang/alphalang@0.0.3-alpha","dist":{"shasum":"954fcb397105e6da90d653cceb27bc23c6444838","integrity":"sha512-/tliibjE8jxvSu3wHVmixo7GOy6eXvK4kspTDXhnn9XgY+roDrxDbsobjtML4SB4P6bO4+xhYwfolg79hQfK+Q==","tarball":"https://registry.npmjs.org/@alphalang/alphalang/-/alphalang-0.0.3-alpha.tgz","fileCount":53,"unpackedSize":80949,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCZ977sRUwfi6cDhfhK4OAjUg7I9zKrS7V6haoZ1iiilgIgbpixctl+8L86QHIelSZ4CFI2U27zuhvrXVL4yaDSIMA="}]},"_npmUser":{"name":"gshigeto","email":"gshigeto@gmail.com"},"directories":{},"maintainers":[{"name":"gshigeto","email":"gshigeto@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alphalang_0.0.3-alpha_1700603433852_0.127820816479844"},"_hasShrinkwrap":false},"0.0.4-alpha":{"name":"@alphalang/alphalang","version":"0.0.4-alpha","description":"[AlphaLang](https://www.alphalang.ai) is an open source Feature Flagging and Experimentation platform.","main":"dist/index.js","type":"module","scripts":{"build:clean":"rimraf dist","build:types":"tsc --project tsconfig.build.json","build":"yarn build:clean && yarn build:types","test":"jest"},"repository":{"type":"git","url":"git+https://github.com/alphalang-ai/alphalang.git","directory":"packages/alphalang"},"author":{},"license":"ISC","bugs":{"url":"https://github.com/alphalang-ai/alphalang/issues"},"homepage":"https://github.com/alphalang-ai/alphalang#readme","devDependencies":{"@types/jest":"^27.0.1","jest":"^27.1.1","typescript":"4.7.4"},"dependencies":{"@alphalang/alphalang-core":"^0.0.1","@aws-crypto/sha256-js":"^5.2.0","@aws-sdk/types":"^3.451.0","@google-ai/generativelanguage":"^1.1.0","@smithy/eventstream-codec":"^2.0.14","@smithy/protocol-http":"^3.0.10","@smithy/signature-v4":"^2.0.16","@smithy/util-utf8":"^2.0.2","google-auth-library":"^9.2.0","langchain":"^0.0.195"},"engines":{"node":">=18"},"_id":"@alphalang/alphalang@0.0.4-alpha","dist":{"shasum":"6fd8db6ff5c32fe33288dbaff0fba4296913579a","integrity":"sha512-/otWl0LF2YCd6zPb9Fi7hnZDQfZ82t1pmHEQIqRVvBy3iScsgzo/RHLPmTXsUuswpx5YXqT0+smVByh5EFpL3Q==","tarball":"https://registry.npmjs.org/@alphalang/alphalang/-/alphalang-0.0.4-alpha.tgz","fileCount":53,"unpackedSize":80891,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIBVo/20sAHd5YeINuiYh8HPzuiO3snUOgem1tfq/KqyAAiABWqsOYxWUHQeT307AHqDY0TlgcVGsyMX5PIzPPGp+9Q=="}]},"_npmUser":{"name":"gshigeto","email":"gshigeto@gmail.com"},"directories":{},"maintainers":[{"name":"gshigeto","email":"gshigeto@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alphalang_0.0.4-alpha_1700603635250_0.15775957224018988"},"_hasShrinkwrap":false},"0.0.5-alpha":{"name":"@alphalang/alphalang","version":"0.0.5-alpha","description":"[AlphaLang](https://www.alphalang.ai) is an open source Feature Flagging and Experimentation platform.","main":"dist/index.js","type":"commonjs","scripts":{"build:clean":"rimraf dist","build:types":"tsc --project tsconfig.build.json","build":"yarn build:clean && yarn build:types","test":"jest"},"repository":{"type":"git","url":"git+https://github.com/alphalang-ai/alphalang.git","directory":"packages/alphalang"},"author":{},"license":"ISC","bugs":{"url":"https://github.com/alphalang-ai/alphalang/issues"},"homepage":"https://github.com/alphalang-ai/alphalang#readme","devDependencies":{"@types/jest":"^27.0.1","jest":"^27.1.1","typescript":"4.7.4"},"dependencies":{"@alphalang/alphalang-core":"^0.0.1","@aws-crypto/sha256-js":"^5.2.0","@aws-sdk/types":"^3.451.0","@google-ai/generativelanguage":"^1.1.0","@smithy/eventstream-codec":"^2.0.14","@smithy/protocol-http":"^3.0.10","@smithy/signature-v4":"^2.0.16","@smithy/util-utf8":"^2.0.2","google-auth-library":"^9.2.0","langchain":"^0.0.195"},"engines":{"node":">=18"},"_id":"@alphalang/alphalang@0.0.5-alpha","dist":{"shasum":"a3b689d36136d6fb7991755128179b7ddeab2d72","integrity":"sha512-1GIQWu3EPdcQzfZ+FOboLKHp820PXBDAPvkMMjlC1yqLuxpoLmf/zwxTklx5JY7BS6btl0reCQ6wCjQjYd4Ztg==","tarball":"https://registry.npmjs.org/@alphalang/alphalang/-/alphalang-0.0.5-alpha.tgz","fileCount":53,"unpackedSize":80893,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEQCIGR4FEhbPnmuA27WPW1AM9z77vXF3LGDdiMdhBJ+/9JkAiA98zfqnXZlOeD243irNwIDzq/l0sGuc+pJVB2nwhL2nA=="}]},"_npmUser":{"name":"gshigeto","email":"gshigeto@gmail.com"},"directories":{},"maintainers":[{"name":"gshigeto","email":"gshigeto@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alphalang_0.0.5-alpha_1700629896961_0.399922187755688"},"_hasShrinkwrap":false},"0.0.6-alpha":{"name":"@alphalang/alphalang","version":"0.0.6-alpha","description":"[AlphaLang](https://www.alphalang.ai) is an open source Feature Flagging and Experimentation platform.","main":"dist/index.js","type":"commonjs","scripts":{"build:clean":"rimraf dist","build:types":"tsc --project tsconfig.build.json","build":"yarn build:clean && yarn build:types","test":"jest"},"repository":{"type":"git","url":"git+https://github.com/alphalang-ai/alphalang.git","directory":"packages/alphalang"},"author":{},"license":"ISC","bugs":{"url":"https://github.com/alphalang-ai/alphalang/issues"},"homepage":"https://github.com/alphalang-ai/alphalang#readme","devDependencies":{"@types/jest":"^27.0.1","jest":"^27.1.1","typescript":"4.7.4"},"dependencies":{"@alphalang/alphalang-core":"^0.0.1","@aws-crypto/sha256-js":"^5.2.0","@aws-sdk/types":"^3.451.0","@google-ai/generativelanguage":"^1.1.0","@smithy/eventstream-codec":"^2.0.14","@smithy/protocol-http":"^3.0.10","@smithy/signature-v4":"^2.0.16","@smithy/util-utf8":"^2.0.2","google-auth-library":"^9.2.0","langchain":"^0.0.195"},"engines":{"node":">=18"},"_id":"@alphalang/alphalang@0.0.6-alpha","dist":{"shasum":"a6ed2113b58e5151bbb8adefd142cbb1542c714a","integrity":"sha512-zl6lSsVSry9ThF5ozAY63SeM50P5XL3usztRPUNowj1unE6whYf2lC5ZZEUuUehmanMJ+9McF3O9aj7BJD1jlQ==","tarball":"https://registry.npmjs.org/@alphalang/alphalang/-/alphalang-0.0.6-alpha.tgz","fileCount":53,"unpackedSize":80901,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQCt8EVY1f6RRT/q35Bn+Cb4gLAhAdZJoIg6cGHx0hK+JAIgDLKsB7aW4sHnt9bnLTKfq3F0zXtsGLlk9fjqCn07m88="}]},"_npmUser":{"name":"gshigeto","email":"gshigeto@gmail.com"},"directories":{},"maintainers":[{"name":"gshigeto","email":"gshigeto@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/alphalang_0.0.6-alpha_1701102989915_0.4295801103836876"},"_hasShrinkwrap":false}},"time":{"created":"2023-11-21T20:01:18.419Z","0.0.2-alpha":"2023-11-21T20:01:18.718Z","modified":"2023-11-27T16:36:30.308Z","0.0.3-alpha":"2023-11-21T21:50:34.080Z","0.0.4-alpha":"2023-11-21T21:53:55.425Z","0.0.5-alpha":"2023-11-22T05:11:37.118Z","0.0.6-alpha":"2023-11-27T16:36:30.111Z"},"maintainers":[{"name":"gshigeto","email":"gshigeto@gmail.com"}],"description":"[AlphaLang](https://www.alphalang.ai) is an open source Feature Flagging and Experimentation platform.","homepage":"https://github.com/alphalang-ai/alphalang#readme","repository":{"type":"git","url":"git+https://github.com/alphalang-ai/alphalang.git","directory":"packages/alphalang"},"author":{},"bugs":{"url":"https://github.com/alphalang-ai/alphalang/issues"},"license":"ISC","readme":"# AlphaLang Javascript SDK\n\n[AlphaLang](https://www.alphalang.ai) is an open source Feature Flagging and Experimentation platform.\n\nThis is the Javascript client library that lets you evaluate feature flags and run experiments (A/B tests) within a Javascript application.\n\n![Build Status](https://github.com/growthbook/growthbook/workflows/CI/badge.svg) ![GZIP Size](https://img.shields.io/badge/gzip%20size-6.9KB-informational) ![NPM Version](https://img.shields.io/npm/v/@alphalang/alphalang)\n\n- **No external dependencies**\n- **Lightweight and fast**\n- Supports both **modern browsers and Node.js**\n- Local targeting and evaluation, **no HTTP requests**\n- **No flickering** when running A/B tests\n- Written in **Typescript** with 100% test coverage\n- **Use your existing event tracking** (GA, Segment, Mixpanel, custom)\n- Run mutually exclusive experiments with **namespaces**\n- **Remote configuration** to change feature values without deploying new code\n- Run **Visual Experiments** without writing code by using the AlphaLang Visual Editor\n\n## Installation\n\n```\nyarn add @alphalang/alphalang\n```\n\nor\n\n```\nnpm i --save @alphalang/alphalang\n```\n\nor use directly in your HTML without installing first:\n\n```html\n<!-- Creates `window.growthbook` with all of the exported classes -->\n<script src=\"https://cdn.jsdelivr.net/npm/@alphalang/alphalang/dist/bundles/index.js\"></script>\n```\n\n## Quick Usage\n\n### Step 1: Configure your app\n\n```js\nimport { AlphaLang } from \"@alphalang/alphalang\";\n\n// Create a AlphaLang instance\nconst gb = new AlphaLang({\n  apiHost: \"https://app.alphalang.ai\",\n  clientKey: \"sdk-abc123\",\n  // Enable easier debugging during development\n  enableDevMode: true,\n  // Targeting attributes\n  attributes: {\n    id: \"123\",\n    country: \"US\",\n  },\n  // Only required for A/B testing\n  // Called every time a user is put into an experiment\n  trackingCallback: (experiment, result) => {\n    console.log(\"Experiment Viewed\", {\n      experimentId: experiment.key,\n      variationId: result.key,\n    });\n  },\n});\n\n// Wait for features to be available\nawait gb.loadFeatures({ autoRefresh: true });\n```\n\n### Step 2: Start Feature Flagging!\n\nThere are 2 main methods for evaluating features: `isOn` and `getFeatureValue`:\n\n```js\n// Simple boolean (on/off) feature flag\nif (gb.isOn(\"my-feature\")) {\n  console.log(\"Feature enabled!\");\n}\n\n// Get the value of a string/JSON/number feature with a fallback\nconst color = gb.getFeatureValue(\"button-color\", \"blue\");\n```\n\n## Node.js\n\nIf using this SDK in a server-side environment, you may need to configure some polyfills for missing browser APIs.\n\n```js\nconst { setPolyfills } = require(\"@alphalang/alphalang-core\");\n\nsetPolyfills({\n  // Required when using built-in feature loading and Node 17 or lower\n  fetch: require(\"cross-fetch\"),\n  // Required when using encrypted feature flags and Node 18 or lower\n  SubtleCrypto: require(\"node:crypto\").webcrypto.subtle,\n  // Optional, can make feature rollouts faster\n  EventSource: require(\"eventsource\"),\n  // Optional, can reduce startup times by persisting cached feature flags\n  localStorage: {\n    // Example using Redis\n    getItem: (key) => redisClient.get(key),\n    setItem: (key, value) => redisClient.set(key, value),\n  },\n});\n```\n\nCreate a separate AlphaLang instance for every incoming request. This is easiest if you use a middleware:\n\n```js\n// Example using Express\napp.use(function (req, res, next) {\n  // Create a AlphaLang instance and store in the request\n  req.growthbook = new AlphaLang({\n    apiHost: \"https://app.alphalang.ai\",\n    clientKey: \"sdk-abc123\",\n    enableDevMode: true,\n  });\n\n  // Clean up at the end of the request\n  res.on(\"close\", () => req.growthbook.destroy());\n\n  // Wait for features to load (will be cached in-memory for future requests)\n  req.growthbook\n    .loadFeatures()\n    .then(() => next())\n    .catch((e) => {\n      console.error(\"Failed to load features from AlphaLang\", e);\n      next();\n    });\n});\n```\n\nThen, you can access the AlphaLang instance from any route:\n\n```js\napp.get(\"/\", (req, res) => {\n  const gb = req.growthbook;\n  // ...\n});\n```\n\n## Loading Features\n\nIn order for the AlphaLang SDK to work, it needs to have feature definitions from the AlphaLang API. There are 2 ways to get this data into the SDK.\n\n### Built-in Fetching and Caching\n\nIf you pass an `apiHost` and `clientKey` into the AlphaLang constructor, it will handle the network requests, caching, retry logic, etc. for you automatically. If your feature payload is encrypted, you can also pass in a `decryptionKey`.\n\n```ts\nconst gb = new AlphaLang({\n  apiHost: \"https://app.alphalang.ai\",\n  clientKey: \"sdk-abc123\",\n  decryptionKey: \"key_abc123\", // Only if you have feature encryption turned on\n});\n\n// Wait for features to be downloaded\nawait gb.loadFeatures({\n  // When features change, update the AlphaLang instance automatically\n  // Default: `false`\n  autoRefresh: true,\n  // If the network request takes longer than this (in milliseconds), continue\n  // Default: `0` (no timeout)\n  timeout: 2000,\n});\n```\n\nUntil features are loaded, all features will evaluate to `null`. If you're ok with a potential flicker in your application (features going from `null` to their real value), you can call `loadFeatures` without awaiting the result.\n\nIf you want to refresh the features at any time (e.g. when a navigation event occurs), you can call `gb.refreshFeatures()`.\n\n### Custom Integration\n\nIf you prefer to handle the network and caching logic yourself, you can instead pass in a features JSON object directly. For example, you might store features in Postgres and send it down to your front-end as part of your app's initial bootstrap API call.\n\n```ts\nconst gb = new AlphaLang({\n  features: {\n    \"feature-1\": {...},\n    \"feature-2\": {...},\n    \"another-feature\": {...},\n  }\n})\n```\n\nNote that you don't have to call `gb.loadFeatures()`. There's nothing to load - everything required is already passed in.\n\nYou can update features at any time by calling `gb.setFeatures()` with a new JSON object.\n\n### Re-rendering When Features Change\n\nWhen features change (e.g. by calling `gb.refreshFeatures()`), you need to re-render your app so that all of your feature flag checks can be re-evaluated. You can specify your own custom rendering function for this purpose:\n\n```js\n// Callback to re-render your app when feature flag values change\ngb.setRenderer(() => {\n  // TODO: re-render your app\n});\n```\n\n## Experimentation (A/B Testing)\n\nIn order to run A/B tests, you need to set up a tracking callback function. This is called every time a user is put into an experiment and can be used to track the exposure event in your analytics system (Segment, Mixpanel, GA, etc.).\n\n```js\nconst gb = new AlphaLang({\n  apiHost: \"https://app.alphalang.ai\",\n  clientKey: \"sdk-abc123\",\n  trackingCallback: (experiment, result) => {\n    // Example using Segment\n    analytics.track(\"Experiment Viewed\", {\n      experimentId: experiment.key,\n      variationId: result.key,\n    });\n  },\n});\n```\n\nThis same tracking callback is used for both feature flag experiments and Visual Editor experiments.\n\n### Feature Flag Experiments\n\nThere is nothing special you have to do for feature flag experiments. Just evaluate the feature flag like you would normally do. If the user is put into an experiment as part of the feature flag, it will call the `trackingCallback` automatically in the background.\n\n```js\n// If this has an active experiment and the user is included,\n// it will call trackingCallback automatically\nconst newLogin = gb.isOn(\"new-signup-form\");\n```\n\nIf the experiment came from a feature rule, `result.featureId` in the trackingCallback will contain the feature id, which may be useful for tracking/logging purposes.\n\n### Visual Editor Experiments\n\nExperiments created through the AlphaLang Visual Editor will run automatically as soon as their targeting conditions are met.\n\n**Note**: Visual Editor experiments are only supported in a web browser environment. They will not run in Node.js, Mobile apps, or Desktop apps.\n\nIf you are using this SDK in a Single Page App (SPA), you will need to let the AlphaLang instance know when the URL changes so the active experiments can update accordingly.\n\n```js\n// Call this every time a navigation event happens in your SPA\nfunction onRouteChange() {\n  gb.setURL(window.location.href);\n}\n```\n\n## TypeScript\n\nWhen used in a TypeScript project, AlphaLang includes basic type inference out of the box:\n\n```ts\n// Type will be `string` based on the fallback provided (\"blue\")\nconst color = gb.getFeatureValue(\"button-color\", \"blue\");\n\n// You can manually specify types as well\n// feature.value will be type `number`\nconst feature = gb.evalFeature<number>(\"font-size\");\nconsole.log(feature.value);\n\n// Experiments will use the variations to infer the return value\n// result.value will be type \"string\"\nconst result = gb.run({\n  key: \"my-test\",\n  variations: [\"blue\", \"green\"],\n});\n```\n\n### Strict Typing\n\nIf you want to enforce stricter types in your application, you can do that when creating the AlphaLang instance:\n\n```ts\n// Define all your feature flags and types here\ninterface AppFeatures {\n  \"button-color\": string;\n  \"font-size\": number;\n  \"newForm\": boolean;\n}\n\n// Pass into the AlphaLang instance\nconst gb = new AlphaLang<AppFeatures>({\n  ...\n});\n```\n\nNow, all feature flag methods will be strictly typed.\n\n```ts\n// feature.value will by type `number`\nconst feature = gb.evalFeature(\"font-size\");\nconsole.log(feature.value);\n\n// Typos will cause compile-time errors\ngb.isOn(\"buton-color\"); // \"buton\" instead of \"button\"\n```\n\nInstead of defining the `AppFeatures` interface manually like above, you can auto-generate it from your AlphaLang account using the [AlphaLang CLI](https://docs.alphalang.ai/tools/cli).\n\n## AlphaLang Instance (reference)\n\n### Attributes\n\nYou can specify attributes about the current user and request. These are used for two things:\n\n1.  Feature targeting (e.g. paid users get one value, free users get another)\n2.  Assigning persistent variations in A/B tests (e.g. user id \"123\" always gets variation B)\n\nThe following are some comonly used attributes, but use whatever makes sense for your application.\n\n```ts\nnew AlphaLang({\n  attributes: {\n    id: \"123\",\n    loggedIn: true,\n    deviceId: \"abc123def456\",\n    company: \"acme\",\n    paid: false,\n    url: \"/pricing\",\n    browser: \"chrome\",\n    mobile: false,\n    country: \"US\",\n  },\n});\n```\n\nIf you need to set or update attributes asynchronously, you can do so with `setAttributes()`. This will completely overwrite the attributes object with whatever you pass in. Also, be aware that changing attributes may change the assigned feature values. This can be disorienting to users if not handled carefully.\n\n### Feature Usage Callback\n\nAlphaLang can fire a callback whenever a feature is evaluated for a user. This can be useful to update 3rd party tools like NewRelic or DataDog.\n\n```ts\nnew AlphaLang({\n  onFeatureUsage: (featureKey, result) => {\n    console.log(\"feature\", featureKey, \"has value\", result.value);\n  },\n});\n```\n\nThe `result` argument is the same thing returned from `gb.evalFeature`.\n\nNote: If you evaluate the same feature multiple times (and the value doesn't change), the callback will only be fired the first time.\n\n### Dev Mode\n\nThere is a [AlphaLang Chrome DevTools Extension](https://chrome.google.com/webstore/detail/growthbook-devtools/opemhndcehfgipokneipaafbglcecjia) that can help you debug and test your feature flags in development.\n\nIn order for this to work, you must explicitly enable dev mode when creating your AlphaLang instance:\n\n```js\nconst gb = new AlphaLang({\n  enableDevMode: true,\n});\n```\n\nTo avoid exposing all of your internal feature flags and experiments to users, we recommend setting this to `false` in production in most cases.\n\n### evalFeature\n\nIn addition to the `isOn` and `getFeatureValue` helper methods, there is the `evalFeature` method that gives you more detailed information about why the value was assigned to the user.\n\n```ts\n// Get detailed information about the feature evaluation\nconst result = gb.evalFeature(\"my-feature\");\n\n// The value of the feature (or `null` if not defined)\nconsole.log(result.value);\n\n// Why the value was assigned to the user\n// One of: `override`, `unknownFeature`, `defaultValue`, `force`, or `experiment`\nconsole.log(result.source);\n\n// The string id of the rule (if any) which was used\nconsole.log(result.ruleId);\n\n// Information about the experiment (if any) which was used\nconsole.log(result.experiment);\n\n// The result of the experiment (or `undefined`)\nconsole.log(result.experimentResult);\n```\n\n### Inline Experiments\n\nInstead of declaring all features up-front in the context and referencing them by ids in your code, you can also just run an experiment directly. This is done with the `gb.run` method:\n\n```js\n// These are the only required options\nconst { value } = gb.run({\n  key: \"my-experiment\",\n  variations: [\"red\", \"blue\", \"green\"],\n});\n```\n\n#### Customizing the Traffic Split\n\nBy default, this will include all traffic and do an even split between all variations. There are 2 ways to customize this behavior:\n\n```js\n// Option 1: Using weights and coverage\ngb.run({\n  key: \"my-experiment\",\n  variations: [\"red\", \"blue\", \"green\"],\n  // Only include 10% of traffic\n  coverage: 0.1,\n  // Split the included traffic 50/25/25 instead of the default 33/33/33\n  weights: [0.5, 0.25, 0.25],\n});\n\n// Option 2: Specifying ranges\ngb.run({\n  key: \"my-experiment\",\n  variations: [\"red\", \"blue\", \"green\"],\n  // Identical to the above\n  // 5% of traffic in A, 2.5% each in B and C\n  ranges: [\n    [0, 0.05],\n    [0.5, 0.525],\n    [0.75, 0.775],\n  ],\n});\n```\n\n#### Hashing\n\nWe use deterministic hashing to assign a variation to a user. We hash together the user's id and experiment key, which produces a number between `0` and `1`. Each variation is assigned a range of numbers, and whichever one the user's hash value falls into will be assigned.\n\nYou can customize this hashing behavior:\n\n```js\ngb.run({\n  key: \"my-experiment\",\n  variations: [\"A\", \"B\"],\n\n  // Which hashing algorithm to use\n  // Version 2 is the latest and the one we recommend\n  hashVersion: 2,\n\n  // Use a different seed instead of the experiment key\n  seed: \"abcdef123456\",\n\n  // Use a different user attribute (default is `id`)\n  hashAttribute: \"device_id\",\n});\n```\n\n**Note**: For backwards compatibility, if no `hashVersion` is specified, it will fall back to using version `1`, which is deprecated. In the future, version `2` will become the default. We recommend specifying version `2` now for all new experiments to avoid migration issues down the line.\n\n#### Meta Info\n\nYou can also define meta info for the experiment and/or variations. These do not affect the behavior, but they are passed through to the `trackingCallback`, so they can be used to annotate events.\n\n```js\ngb.run({\n  key: \"results-per-page\",\n  variations: [10, 20],\n\n  // Experiment meta info\n  name: \"Results per Page\",\n  phase: \"full-traffic\"\n\n  // Variation meta info\n  meta: [\n    {\n      key: \"control\",\n      name: \"10 Results per Page\",\n    },\n    {\n      key: \"variation\",\n      name: \"20 Results per Page\",\n    },\n  ]\n})\n```\n\n#### Mutual Exclusion\n\nSometimes you want to run multiple conflicting experiments at the same time. You can use the `filters` setting to run mutually exclusive experiments.\n\nWe do this using deterministic hashing to assign users a value between 0 and 1 for each filter.\n\n```js\n// Will include 60% of users - ones with a hash between 0 and 0.6\ngb.run({\n  key: \"experiment-1\",\n  variation: [0, 1],\n  filters: [\n    {\n      seed: \"pricing\",\n      attribute: \"id\",\n      ranges: [[0, 0.6]],\n    },\n  ],\n});\n\n// Will include the other 40% of users - ones with a hash between 0.6 and 1\ngb.run({\n  key: \"experiment-2\",\n  variation: [0, 1],\n  filters: [\n    {\n      seed: \"pricing\",\n      attribute: \"id\",\n      ranges: [[0.6, 1.0]],\n    },\n  ],\n});\n```\n\n**Note** - If a user is excluded from an experiment due to a filter, the rule will be skipped and the next matching rule will be used instead.\n\n#### Holdout Groups\n\nTo use global holdout groups, use a nested experiment design:\n\n```js\n// The value will be `true` if in the holdout group, otherwise `false`\nconst holdout = gb.run({\n  key: \"holdout\",\n  variations: [true, false],\n  // 10% of users in the holdout group\n  weights: [0.1, 0.9],\n});\n\n// Only run your main experiment if the user is NOT in the holdout\nif (!holdout.value) {\n  const res = gb.run({\n    key: \"my-experiment\",\n    variations: [\"A\", \"B\"],\n  });\n}\n```\n\n#### Targeting Conditions\n\nYou can also define targeting conditions that limit which users are included in the experiment. These conditions are evaluated against the `attributes` passed into the AlphaLang context. The syntax for conditions is based on the MongoDB query syntax and is straightforward to read and write.\n\nFor example, if the attributes are:\n\n```json\n{\n  \"id\": \"123\",\n  \"browser\": {\n    \"vendor\": \"firefox\",\n    \"version\": 94\n  },\n  \"country\": \"CA\"\n}\n```\n\nThe following condition would evaluate to `true` and the user would be included in the experiment:\n\n```js\ngb.run({\n  key: \"my-experiment\",\n  variation: [0, 1],\n  condition: {\n    \"browser.vendor\": \"firefox\",\n    country: {\n      $in: [\"US\", \"CA\", \"IN\"],\n    },\n  },\n});\n```\n\n#### Inline Experiment Return Value\n\nA call to `gb.run(experiment)` returns an object with a few useful properties:\n\n```ts\nconst {\n  value,\n  key,\n  name,\n  variationId,\n  inExperiment,\n  hashUsed,\n  hashAttribute,\n  hashValue,\n} = gb.run({\n  key: \"my-experiment\",\n  variations: [\"A\", \"B\"],\n});\n\n// If user is included in the experiment\nconsole.log(inExperiment); // true or false\n\n// The index of the assigned variation\nconsole.log(variationId); // 0 or 1\n\n// The value of the assigned variation\nconsole.log(value); // \"A\" or \"B\"\n\n// The key and name of the assigned variation (if specified in `meta`)\nconsole.log(key); // \"0\" or \"1\"\nconsole.log(name); // \"\"\n\n// If the variation was randomly assigned by hashing\nconsole.log(hashUsed);\n\n// The user attribute that was hashed\nconsole.log(hashAttribute); // \"id\"\n\n// The value of that attribute\nconsole.log(hashValue); // e.g. \"123\"\n```\n\nThe `inExperiment` flag will be false if the user was excluded from being part of the experiment for any reason (e.g. failed targeting conditions).\n\nThe `hashUsed` flag will only be true if the user was randomly assigned a variation. If the user was forced into a specific variation instead, this flag will be false.\n\n## Feature Definitions (reference)\n\nThe feature definition JSON file contains information about all of the features in your application.\n\nEach feature consists of a unique key, a list of possible values, and rules for how to assign those values to users.\n\n```ts\n{\n  \"feature-1\": {...},\n  \"feature-2\": {...},\n  \"another-feature\": {...},\n}\n```\n\n### Basic Feature\n\nAn empty feature always has the value `null`:\n\n```js\n{\n  \"my-feature\": {}\n}\n```\n\n#### Default Values\n\nYou can change the default assigned value with the `defaultValue` property:\n\n```js\n{\n  \"my-feature\": {\n    defaultValue: \"green\"\n  }\n}\n```\n\n### Override Rules\n\nYou can override the default value with **rules**.\n\nRules give you fine-grained control over how feature values are assigned to users. There are 2 types of feature rules: `force` and `experiment`. Force rules give the same value to everyone. Experiment rules assign values to users randomly.\n\n#### Rule Ids\n\nRules can specify a unique identifier with the `id` property. This can help with debugging and QA by letting you see exactly why a specific value was assigned to a user.\n\n#### Rule Conditions\n\nRules can optionally define targeting conditions that limit which users the rule applies to. These conditions are evaluated against the `attributes` passed into the AlphaLang context. The syntax for conditions is based on the MongoDB query syntax and is straightforward to read and write.\n\nFor example, if the attributes are:\n\n```json\n{\n  \"id\": \"123\",\n  \"browser\": {\n    \"vendor\": \"firefox\",\n    \"version\": 94\n  },\n  \"country\": \"CA\"\n}\n```\n\nThe following condition would evaluate to `true`:\n\n```json\n{\n  \"browser.vendor\": \"firefox\",\n  \"country\": {\n    \"$in\": [\"US\", \"CA\", \"IN\"]\n  }\n}\n```\n\nIf a condition evaluates to `false`, the rule will be skipped. This means you can chain rules together with different conditions to support even the most complex use cases.\n\n#### Force Rules\n\nForce rules do what you'd expect - force a specific value for the feature\n\n```js\n// Firefox users in the US or Canada get \"green\"\n// Everyone else gets the default \"blue\"\n{\n  \"button-color\": {\n    defaultValue: \"blue\",\n    rules: [\n      {\n        id: \"rule-123\",\n        condition: {\n          browser: \"firefox\",\n          country: {\n            $in: [\"US\", \"CA\"]\n          }\n        },\n        force: \"green\"\n      }\n    ],\n  }\n}\n```\n\n##### Gradual Rollouts\n\nYou can specify a `range` for your rule, which determines what percent of users will get the rule applied to them. Users who do not get the rule applied will fall through to the next matching rule (or default value). You can also specify a `seed` that will be used for hashing.\n\nIn order to figure out if a user is included or not, we use deterministic hashing. By default, we use the user attribute `id` for this, but you can override this by specifying `hashAttribute` for the rule:\n\nThis is useful for gradually rolling out features to users (start with a small range and slowly increase).\n\n```js\n{\n  \"new-feature\": {\n    defaultValue: false,\n    rules: [\n      {\n        force: true,\n        hashAttribute: \"device-id\",\n        seed: 'new-feature-rollout-abcdef123',\n        // 20% of users\n        range: [0, 0.2]\n        // Increase to 40%:\n        // range: [0, 0.4]\n      }\n    ]\n  }\n}\n```\n\n#### Experiment Rules\n\nExperiment rules let you adjust the percent of users who get randomly assigned to each variation. This can either be used for hypothesis-driven A/B tests or to simply mitigate risk by gradually rolling out new features to your users.\n\n```js\n// Each variation gets assigned to a random 1/3rd of users\n{\n  \"image-size\": {\n    rules: [\n      {\n        variations: [\"small\", \"medium\", \"large\"]\n      }\n    ]\n  }\n}\n```\n\n##### Customizing the Traffic Split\n\nBy default, an experiment rule will include all traffic and do an even split between all variations. There are 2 ways to customize this behavior:\n\n```js\n// Option 1: Using weights and coverage\n{\n  variations: [\"red\", \"blue\", \"green\"],\n  // Only include 10% of traffic\n  coverage: 0.1,\n  // Split the included traffic 50/25/25 instead of the default 33/33/33\n  weights: [0.5, 0.25, 0.25]\n}\n\n// Option 2: Specifying ranges\n{\n  variations: [\"red\", \"blue\", \"green\"],\n  // Identical to the above\n  // 5% of traffic in A, 2.5% each in B and C\n  ranges: [\n    [0, 0.05],\n    [0.5, 0.525],\n    [0.75, 0.775]\n  ]\n}\n```\n\nA user is assigned a number from 0 to 1 and whichever variation's range includes their number will be assigned to them.\n\n##### Variation Meta Info\n\nYou can use the `meta` setting to provide additional info about the variations such as name.\n\n```js\n{\n  \"image-size\": {\n    rules: [\n      {\n        variations: [\"sm\", \"md\", \"lg\"],\n        ranges: [\n          [0, 0.5],\n          [0.5, 0.75],\n          [0.75, 1.0]\n        ],\n        meta: [\n          {\n            key: \"control\",\n            name: \"Small\",\n          },\n          {\n            key: \"v1\",\n            name: \"Medium\",\n          },\n          {\n            key: \"v2\",\n            name: \"Large\",\n          }\n        ]\n      }\n    ]\n  }\n}\n```\n\n##### Tracking Key and Name\n\nWhen a user is assigned a variation, we call the `trackingCallback` function so you can record the exposure with your analytics event tracking system. By default, we use the feature id to identify the experiment, but this can be overridden if needed with the `key` setting. You can also optionally provide a human-readable name.\n\n```js\n{\n  \"feature-1\": {\n    rules: [\n      {\n        // Use \"my-experiment\" as the key instead of \"feature-1\"\n        key: \"my-experiment\",\n        name: \"My Experiment\",\n        variations: [\"A\", \"B\"]\n      }\n    ]\n  },\n}\n```\n\n##### Hash Attribute\n\nWe use deterministic hashing to make sure the same user always gets assigned the same value. By default, we use the attribute `id`, but this can be overridden with the `hashAttribute` setting:\n\n```js\nconst gb = new AlphaLang({\n  attributes: {\n    id: \"123\",\n    company: \"acme\",\n  },\n  features: {\n    \"my-feature\": {\n      rules: [\n        // All users with the same \"company\" value\n        // will be assigned the same variation\n        {\n          variations: [\"A\", \"B\"],\n          hashAttribute: \"company\",\n        },\n        // If \"company\" is empty for the user (e.g. if they are logged out)\n        // The experiment will be skipped and fall through to this next rule\n        {\n          force: \"A\",\n        },\n      ],\n    },\n  },\n});\n```\n\n##### Filters\n\nSometimes you want to run multiple conflicting experiments at the same time. You can use the `filters` setting to run mutually exclusive experiments.\n\nWe do this using deterministic hashing to assign users a value between 0 and 1 for each filter.\n\n```js\n{\n  \"feature1\": {\n    rules: [\n      // Will include 60% of users - ones with a hash between 0 and 0.6\n      {\n        variations: [false, true],\n        filters: [\n          {\n            seed: \"pricing\",\n            attribute: \"id\",\n            ranges: [[0, 0.6]]\n          }\n        ]\n      }\n    ]\n  },\n  \"feature2\": {\n    rules: [\n      // Will include the other 40% of users - ones with a hash between 0.6 and 1\n      {\n        variations: [false, true],\n        filters: [\n          {\n            seed: \"pricing\",\n            attribute: \"id\",\n            ranges: [[0.6, 1.0]]\n          }\n        ]\n      },\n    ]\n  }\n}\n```\n\n**Note** - If a user is excluded from an experiment due to a filter, the rule will be skipped and the next matching rule will be used instead.\n\n## Examples\n\n- [Typescript example app with strict typing <ExternalLink />](https://github.com/growthbook/examples/tree/main/vanilla-typescript).\n","readmeFilename":"README.md"}