{"id":15653754,"url":"https://github.com/witodelnat/keat","last_synced_at":"2026-02-26T06:46:06.511Z","repository":{"id":40313368,"uuid":"369566282","full_name":"WitoDelnat/keat","owner":"WitoDelnat","description":"Serverless feature flags for progressive releases.","archived":false,"fork":false,"pushed_at":"2025-07-02T09:35:49.000Z","size":3145,"stargazers_count":27,"open_issues_count":1,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-08-16T10:54:27.909Z","etag":null,"topics":["continuous-delivery","feature-flags","feature-management","feature-toggles","javascript","react","serverless","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/WitoDelnat.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-05-21T14:45:18.000Z","updated_at":"2025-07-02T09:35:30.000Z","dependencies_parsed_at":"2024-06-21T16:43:58.786Z","dependency_job_id":"2bb45163-dd19-4f43-90cc-b314ff8afc09","html_url":"https://github.com/WitoDelnat/keat","commit_stats":{"total_commits":144,"total_committers":2,"mean_commits":72.0,"dds":0.00694444444444442,"last_synced_commit":"52a913971330042c1fd30c011768d0cc3d03ac83"},"previous_names":["witodelnat/keat-node"],"tags_count":15,"template":false,"template_full_name":null,"purl":"pkg:github/WitoDelnat/keat","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WitoDelnat%2Fkeat","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WitoDelnat%2Fkeat/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WitoDelnat%2Fkeat/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WitoDelnat%2Fkeat/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/WitoDelnat","download_url":"https://codeload.github.com/WitoDelnat/keat/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/WitoDelnat%2Fkeat/sbom","scorecard":{"id":151810,"data":{"date":"2025-08-11","repo":{"name":"github.com/WitoDelnat/keat","commit":"13b32cb6bedb20340c19f94fa90acab96d7fdb0f"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2.9,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":10,"reason":"no dangerous workflow patterns detected","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Maintained","score":1,"reason":"2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Code-Review","score":0,"reason":"Found 0/28 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Token-Permissions","score":0,"reason":"detected GitHub workflow tokens with excessive permissions","details":["Warn: no topLevel permission defined: .github/workflows/release.yaml:1","Warn: no topLevel permission defined: .github/workflows/test-pr.yaml:1","Info: no jobLevel write permissions found"],"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Pinned-Dependencies","score":5,"reason":"dependency not pinned by hash detected -- score normalized to 5","details":["Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yaml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/WitoDelnat/keat/release.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/release.yaml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/WitoDelnat/keat/release.yaml/main?enable=pin","Warn: third-party GitHubAction not pinned by hash: .github/workflows/release.yaml:28: update your workflow using https://app.stepsecurity.io/secureworkflow/WitoDelnat/keat/release.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test-pr.yaml:16: update your workflow using https://app.stepsecurity.io/secureworkflow/WitoDelnat/keat/test-pr.yaml/main?enable=pin","Warn: GitHub-owned GitHubAction not pinned by hash: .github/workflows/test-pr.yaml:19: update your workflow using https://app.stepsecurity.io/secureworkflow/WitoDelnat/keat/test-pr.yaml/main?enable=pin","Info:   0 out of   4 GitHub-owned GitHubAction dependencies pinned","Info:   0 out of   1 third-party GitHubAction dependencies pinned","Info:   2 out of   2 npmCommand dependencies pinned"],"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'main'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 3 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"52 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-968p-4wvh-cqc8","Warn: Project is vulnerable to: GHSA-67hx-6x53-jw92","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: GHSA-9pv7-vfvm-6vr7","Warn: Project is vulnerable to: GHSA-952p-6rrq-rcjv","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-7fh5-64p2-3v2j","Warn: Project is vulnerable to: GHSA-8775-5hwv-wr6v","Warn: Project is vulnerable to: GHSA-gcx4-mw62-g8wm","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-92r3-m2mg-pj97","Warn: Project is vulnerable to: GHSA-c24v-8rfc-w8vw","Warn: Project is vulnerable to: GHSA-8jhw-289h-jh2g","Warn: Project is vulnerable to: GHSA-64vr-g452-qvp3","Warn: Project is vulnerable to: GHSA-9cwx-2883-4wfx","Warn: Project is vulnerable to: GHSA-vg6x-rcgg-rjx6","Warn: Project is vulnerable to: GHSA-x574-m823-4x7w","Warn: Project is vulnerable to: GHSA-4r4m-qw57-chr8","Warn: Project is vulnerable to: GHSA-xcj6-pq6g-qj4x","Warn: Project is vulnerable to: GHSA-356w-63v5-8wf4","Warn: Project is vulnerable to: GHSA-859w-5945-r5v3","Warn: Project is vulnerable to: GHSA-qwcr-r2fm-qrc7","Warn: Project is vulnerable to: GHSA-pxg6-pf52-xh8x","Warn: Project is vulnerable to: GHSA-ghr5-ch3p-vcr6","Warn: Project is vulnerable to: GHSA-rv95-896h-c2vc","Warn: Project is vulnerable to: GHSA-qw6h-vgh9-j6wx","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-78xj-cgh5-2h22","Warn: Project is vulnerable to: GHSA-2p57-rm9w-gvfp","Warn: Project is vulnerable to: GHSA-4wx3-54gh-9fr9","Warn: Project is vulnerable to: GHSA-76c9-3jph-rj3q","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-rhx6-c78j-4q9w","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-m6fv-jmcg-4jfg","Warn: Project is vulnerable to: GHSA-76p7-773f-r4q5","Warn: Project is vulnerable to: GHSA-cm22-4g7w-348p","Warn: Project is vulnerable to: GHSA-w5hq-hm5m-4548","Warn: Project is vulnerable to: GHSA-f5x3-32g6-xq36","Warn: Project is vulnerable to: GHSA-pq67-2wwv-3xjx","Warn: Project is vulnerable to: GHSA-8cj5-5rvv-wf4v","Warn: Project is vulnerable to: GHSA-353f-5xf4-qw67","Warn: Project is vulnerable to: GHSA-g3ch-rx76-35fx","Warn: Project is vulnerable to: GHSA-hc6q-2mpp-qw7j","Warn: Project is vulnerable to: GHSA-4vvj-4cpr-p986","Warn: Project is vulnerable to: GHSA-wr3j-pwj9-hqq6","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7","Warn: Project is vulnerable to: GHSA-3h5v-q93c-6h6q"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-16T10:54:27.312Z","repository_id":40313368,"created_at":"2025-08-16T10:54:27.312Z","updated_at":"2025-08-16T10:54:27.312Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270702562,"owners_count":24630877,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-16T02:00:11.002Z","response_time":91,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["continuous-delivery","feature-flags","feature-management","feature-toggles","javascript","react","serverless","typescript"],"created_at":"2024-10-03T12:46:55.412Z","updated_at":"2026-02-26T06:46:06.470Z","avatar_url":"https://github.com/WitoDelnat.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Keat\n\nProgressive and type-safe feature flags.\n\nAn easy way to increase your deployment frequency and reduce stress of releases.\n\n## Key Features\n\n- 🚀 Progressive rollouts, 🎯 targeted audiences and 📅 scheduled launches.\n- 🛠 Remote configuration without vendor lock-in.\n- 💙 Amazing TypeScript support.\n- 💡 Framework agnostic with React adaptor included.\n- 🌳 Lightweight core with tree shakeable plugins.\n- 🧪 Bi- and multivariates of any type.\n\n## Getting started\n\nStart by adding Keat to your codebase:\n\n```bash\nnpm install keat\n```\n\nAfter installing Keat, you can define your first **feature** together with its **rule**.\n\n```typescript\nimport { keatCore } from \"keat\";\n\nconst { variation } = keatCore({\n  features: {\n    recommendations: true,\n  },\n});\n\nvariation(\"recommendations\") === true;\n```\n\nBy default the rule can either be `true` or `false`, respectively to enable or disable it.\nThis is not very useful so let's continue by adding **plugins** to supercharge Keat.\n\n### Enable features for particular users\n\nEnabling features for particular users allows you to test in production and preview releases to your adventurous users.\n\nTo do this you use the `audience` plugin.\nThis plugin looks whether the rule contains its name and enables the feature when its function evaluates truthy.\n\n```typescript\nimport { keatCore, audience } from \"keat\";\n\nconst { variation } = keatCore({\n  features: {\n    recommendations: \"staff\",\n  },\n  plugins: [audience(\"staff\", (user) =\u003e user.email?.endsWith(\"example.io\"))],\n});\n\nvariation(\"recommendations\", { email: \"dev@example.io\" }) === true;\nvariation(\"recommendations\", { email: \"jef@gmail.com\" }) === false;\n```\n\n### Enable features for a percentage of users\n\nEnabling features for a percentage of users allows canary and A/B testing.\nBy releasing to a small and gradually increasing amount of users you gain the confidence you need.\n\nTo do this you use the `rollouts` plugin.\nThis plugin takes the first `number` of a rule and enables the feature for a percentage of users equal to that amount. Under the hood a murmurhash will ensure sticky behavior across sessions for the same user.\n\n```typescript\nimport { keatCore, audience, rollouts } from \"keat\";\n\nconst { variation } = keatCore({\n  features: {\n    recommendations: { OR: [\"staff\", 25] },\n  },\n  plugins: [\n    audience(\"staff\", (user) =\u003e user.email?.endsWith(\"example.io\")),\n    rollouts(),\n  ],\n});\n\nvariation(\"recommendations\", { email: \"dev@example.io\" }) === true;\nvariation(\"recommendations\", { email: randomEmail() }); // `true` for 25% of users.\n```\n\nYou might also wonder how multiple plugins relate to each other.\nPlugins are evaluated in FIFO order, so in this example the audiences are checked before the rollouts.\nThe evaluation short-circuits whenever a plugin sets a result,\nand when none is set the default behavior is used instead.\n\n### Toggle features remotely\n\nToggling features is the bread and butter of any feature management tool.\n\nKeat uses **configuration** to toggle features.\n\nThe format is a basic JSON object that maps the feature to its updated rule:\n\n```json\n{\n  \"recommendations\": { \"OR\": [\"staff\", 50] }\n}\n```\n\nThe plain format combined with custom plugins means possibilities are endless:\n\n- Serve from Cloud Object Storage or embed it within your API.\n- Use CloudFlare at edge or tools like Firebase Remote configuration.\n- Open a WebSocket or use server-sent events to stream changes in real-time.\n\nOr you can use the build-in `remoteConfig` to fetch it from an endpoint:\n\n```typescript\nimport { keatCore, remoteConfig, audience, rollouts } from \"keat\";\n\nconst { variation } = keatCore({\n  features: {\n    recommendations: false,\n  },\n  plugins: [\n    remoteConfig(\"http://example.io/config\", { interval: 300 }),\n    audience(\"staff\", (user) =\u003e user.email?.endsWith(\"example.io\")),\n    rollouts(),\n  ],\n});\n```\n\n## Examples\n\n### Public landing page\n\nWebsite without login or stable identity where you can still preview and A/B test optimal engagement.\n\nConsider embedding **configuration** at build time since modern CI can rebuild it within a minute or two.\nEnvironment variables favour operational simplicity over propagation speed.\nYou can get all the benefits of feature flags without the burden of infrastructure.\n\n```typescript\nimport { keatCore, localConfig, …, rollouts } from \"keat\";\nimport featureJson from \"./features.json\";\n\nexport const keat = keatCore({\n  features: {\n    search: 30,\n    halloweenDesign: { OR: [\"preview\", \"2022-10-20\"] },\n  },\n  plugins: [\n    localConfig({\n      ...featureJson,\n      search: fromEnv(process.env[\"TOGGLE_SEARCH\"]),\n    }),\n    anonymous({ persist: true }),\n    queryParam(\"preview\"), // takes \"preview\" and toggles when \"preview\" is in the URL's query parameters.\n    launchDay(), // takes all ISO 8601 date strings and toggles when the date is in the past.\n    rollouts(),\n  ],\n});\n```\n\n### Microservice with NodeJs\n\nKeat works both in the browser and on NodeJs. Use it to measure performance optimizations, gradually migrate to a new integration or degrade your services when there is trouble on the horizon.\n\nKeat is not restricted traditional boolean flags. Use **bi- or multi-variates of any type** to be more expressive in your feature flags. Keat will also properly infer the return type of your variates so you get immediate feedback on your usage.\n\n```typescript\nimport { keatCore, rollouts } from \"keat\";\n\nexport const keat = keatCore({\n  features: {\n    enableJitCache: 50,\n    notificationService: {\n      variates: [\"modern\", \"legacy\"],\n      when: 5,\n    },\n    rateLimit: {\n      variates: [\n        {\n          level: \"default\",\n          average: 1000,\n          burst: 2000\n        },\n        {\n          level: \"degraded\",\n          average: 500,\n          burst: 800,\n        },\n        {\n          level: \"disaster\",\n          average: 100,\n          burst: 150,\n        },\n      ],\n      when: [false, true, false],\n    },\n  } as const, // tip: use `as const` to narrow the return types\n  plugins: [rollouts()],\n});\n\nkeat.variation(\"notificationService\");\nReturnType\u003ctypeof keat.variation(\"rateLimit\")\u003e = { level: string; average: number; burst: number};\nReturnType\u003ctypeof keat.variation(\"notificationService\")\u003e = \"modern\" | \"legacy\"; // or `string` without `as const`\n```\n\n### SaaS application with React\n\nModern web application where developers can test in production, gather feedback through early previews and progressively rollout to maximise the chance of success.\n\nYour remote configuration might be slow for a variety of reasons (e.g. viewer has slow 3G).\nWith **feature display** you can optimize individual boundaries instead of blocking your whole application.\nIt will feel familiar if you've worked with `font-display` before ([Playground](https://font-display.glitch.me/), [MDN Docs](https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display)).\n\n```tsx\nimport { keatReact, audience, remoteConfig, rollouts } from \"keat\";\n\nconst { useKeat, FeatureBoundary } = keatReact({\n  features: {\n    search: false,\n    redesign: false,\n    sortAlgorithm: {\n      variates: [\"quicksort\", \"insertionSort\", \"heapsort\"],\n    },\n  } as const,\n  plugins: [\n    remoteConfig(\"https://example.io/slowConfig\", { interval: 300 }),\n    audience(\"staff\", (user) =\u003e user.email?.endsWith(\"example.io\")),\n    audience(\"preview\", (user) =\u003e user.settings.previewEnabled),\n    rollouts(),\n  ],\n});\n\nexport function App() {\n  const { variation } = useKeat();\n\n  return (\n    \u003cdiv\u003e\n      \u003ch1\u003eKeat\u003c/h1\u003e\n\n      \u003cFeatureBoundary name=\"redesign\" fallback={\u003cp\u003eYour old design\u003c/p\u003e}\u003e\n        \u003cp\u003eYour new design\u003c/p\u003e\n      \u003c/FeatureBoundary\u003e\n\n      \u003cFeatureBoundary\n        name=\"search\"\n        display=\"block\"\n        invisible={\u003cSearchSkeleton /\u003e}\n      \u003e\n        \u003cSearch /\u003e\n      \u003c/FeatureBoundary\u003e\n\n      \u003cSortedList data={[1, 3, 4]} algorithm={variation(\"sortAlgorithm\")} /\u003e\n    \u003c/div\u003e\n  );\n}\n```\n\n## Plugins\n\n### Build-in plugins\n\nRules:\n\n- **audience** checks whether the rule contains its name and enables the feature when its function responds truthy.\n- **queryParam** checks whether the rule contains its name and enables the feature depending on the URLs query parameter.\n- **rollouts** takes the first `number` and enables the feature for a percentage of users equal to that amount.\n- **launchDay** takes all `ISO 8601 date strings` and enables the feature when the date is in the past.\n\nConfigurations:\n\n- **localConfig** fetches your configuration from a local JSON file or environment variables.\n- **remoteConfig** fetches your configuration from a remote endpoint, which allows decoupling deploy from release.\n- **customConfig** fetches your configuration with a customizable fetch.\n\nMiscellaneous:\n\n- **cache** adds simple caching to your evaluations which improve performance.\n- **anonymous** adds a generated, stable identity, which allows reliable rollout results.\n\n### Custom plugins\n\nPlugins are plain old JavaScript objects with a simple interface that hooks\ninto the lifecycle of Keat. Checkout [the common plugin interface on GitHub](https://github.com/WitoDelnat/keat/blob/main/src/core/plugin.ts) to get a full view on the available context and API.\n\nHere is the code for the launch day plugin:\n\n```typescript\nexport const launchDay = () =\u003e {\n  createPlugin({\n    matcher: isDate,\n    evaluate({ literal }) {\n      return literal.getTime() \u003c Date.now();\n    },\n  });\n};\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwitodelnat%2Fkeat","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwitodelnat%2Fkeat","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwitodelnat%2Fkeat/lists"}