PageSourceSearch

https://openfeature.dev/assets/js/1968f07e.46b1941c.js

js openfeature.dev collected 2026-09-24 08:38:40 UTC 15,111 bytes, 1 lines download raw bytes

1"use strict";(globalThis.webpackChunkdocs||=[]).push([[3741],{84775(e,n,t){t.r(n),t.d(n,{assets:()=>d,contentTitle:()=>c,default:()=>v,frontMatter:()=>l,metadata:()=>a,toc:()=>h});const a=JSON.parse('{"id":"reference/concepts/events","title":"Events","description":"Events enable the ability to react to state changes in the provider or underlying flag management system.","source":"@site/docs/reference/concepts/05-events.mdx","sourceDirName":"reference/concepts","slug":"/reference/concepts/events","permalink":"/docs/reference/concepts/events","draft":false,"unlisted":false,"editUrl":"https://github.com/open-feature/openfeature.dev/edit/main/docs/reference/concepts/05-events.mdx","tags":[],"version":"current","sidebarPosition":5,"frontMatter":{"sidebar_position":5,"id":"events"},"sidebar":"docs","previous":{"title":"Hooks","permalink":"/docs/reference/concepts/hooks"},"next":{"title":"SDK Paradigms","permalink":"/docs/reference/concepts/sdk-paradigms"}}');var r=t(74848),s=t(28453),i=t(4865),o=t(19365);const l={sidebar_position:5,id:"events"},c="Events",d={},h=[{value:"Event Handlers",id:"event-handlers",level:2},{value:"Event Types",id:"event-types",level:2},{value:"PROVIDER_READY",id:"provider_ready",level:3},{value:"PROVIDER_CONFIGURATION_CHANGED",id:"provider_configuration_changed",level:3},{value:"PROVIDER_ERROR",id:"provider_error",level:3},{value:"PROVIDER_STALE",id:"provider_stale",level:3},{value:"PROVIDER_RECONCILING (Static-context/Client-side only)",id:"provider_reconciling-static-contextclient-side-only",level:3},{value:"PROVIDER_CONTEXT_CHANGED (Static-context/Client-side only)",id:"provider_context_changed-static-contextclient-side-only",level:3}];function u(e){const n={a:"a",code:"code",em:"em",h1:"h1",h2:"h2",h3:"h3",header:"header",p:"p",pre:"pre",...(0,s.R)(),...e.components};return(0,r.jsxs)(r.Fragment,{children:[(0,r.jsx)(n.header,{children:(0,r.jsx)(n.h1,{id:"events",children:"Events"})}),"\n","\n",(0,r.jsx)(n.p,{children:"Events enable the ability to react to state changes in the provider or underlying flag management system.\nThese include changes in provider readiness, error status, or perhaps most interestingly, flag configuration changes."}),"\n",(0,r.jsx)(n.h2,{id:"event-handlers",children:"Event Handlers"}),"\n",(0,r.jsxs)(n.p,{children:["Event handlers can be attached to a ",(0,r.jsx)(n.code,{children:"client"})," or to the global API object, for any ",(0,r.jsx)(n.a,{href:"/specification/types#provider-events",children:"provider event type"}),".\nHandlers attached to the global API object run when ",(0,r.jsx)(n.em,{children:"any"})," provider emits the associated events; this makes global handlers ideal for configuration troubleshooting, monitoring and other cross-cutting concerns.\nHandlers attached to a client will run only when the provider bound with that client emits the associated event; this makes ",(0,r.jsx)(n.code,{children:"client"})," handlers ideal for reacting to flag-state changes in an application domain logic."]}),"\n",(0,r.jsxs)(n.p,{children:["Handlers are passed an ",(0,r.jsx)(n.a,{href:"/specification/types#event-details",children:"event details"})," structure, which contains data about the event, including a list of keys that have changed (if applicable and available)."]}),"\n",(0,r.jsxs)(i.A,{groupId:"code",children:[(0,r.jsx)(o.A,{value:"js",label:"TypeScript",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-ts",children:"// add an event handler to a client\nconst client = OpenFeature.getClient();\nclient.addHandler(ProviderEvents.ConfigurationChanged, (eventDetails) => {\n  // do something when the provider's flag settings change\n});\n\n// add an event handler to the global API\nOpenFeature.addHandler(ProviderEvents.Error, (eventDetails) => {\n  // do something when the provider goes into an error state\n});\n"})})}),(0,r.jsx)(o.A,{value:"java",label:"Java",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-java",children:"// add an event handler to a client\nClient client = OpenFeatureAPI.getInstance().getClient();\nclient.onProviderConfigurationChanged((EventDetails eventDetails) ->
1 {\n    // do something when the provider's flag settings change\n});\n\n// add an event handler to the global API\nOpenFeatureAPI.getInstance().onProviderError((EventDetails eventDetails) -> {\n  // do something when the provider goes into an error state\n});\n"})})}),(0,r.jsx)(o.A,{value:"csharp",label:"C#",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-csharp",children:"// add an event handler to a client\nvar client = Api.Instance.GetClient();\nclient.AddHandler(ProviderEventTypes.ProviderConfigurationChanged, (ProviderEventPayload eventDetails) =>\n{\n  // do something when the provider's flag settings change\n});\n\n// add an event handler to the global API\nApi.Instance.AddHandler(ProviderEventTypes.ProviderError, (ProviderEventPayload eventDetails) =>\n{\n  // do something when the provider goes into an error state\n});\n"})})}),(0,r.jsx)(o.A,{value:"go",label:"Go",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-go",children:'var changedCallback = func(details openfeature.EventDetails) {\n  // do something when the provider\'s flag settings change\n}\n\n// add an event handler to a client\nclient := openfeature.NewClient("clientName")\n\n// add an event handler to a client\nclient.AddHandler(openfeature.ProviderError, &changedCallback)\n\nvar errorCallback = func(details openfeature.EventDetails) {\n  // do something when the provider goes into an error state\n}\n\n// add an event handler to the global API\nopenfeature.AddHandler(openfeature.ProviderError, &errorCallback)\n'})})}),(0,r.jsx)(o.A,{value:"python",label:"Python",children:(0,r.jsx)(n.pre,{children:(0,r.jsx)(n.code,{className:"language-python",children:"client = api.get_client()\n\ndef on_changed(event_details: EventDetails):\n  # do something when the provider's flag settings change\n\n# add an event handler to a client\nclient.add_handler(ProviderEvent.PROVIDER_CONFIGURATION_CHANGED, on_changed)\n\ndef on_error(event_details: EventDetails):\n  # do something when the provider goes into an error state\n\n# add an event handler to the global API\napi.add_handler(ProviderEvent.PROVIDER_ERROR, on_error)\n"})})})]}),"\n",(0,r.jsx)(n.h2,{id:"event-types",children:"Event Types"}),"\n",(0,r.jsxs)(n.p,{children:["See ",(0,r.jsx)(n.a,{href:"/specification/types#provider-events",children:"event types"})," specification."]}),"\n",(0,r.jsx)(n.h3,{id:"provider_ready",children:"PROVIDER_READY"}),"\n",(0,r.jsx)(n.p,{children:"The provider is ready to perform flag evaluations."}),"\n",(0,r.jsxs)(n.p,{children:[(0,r.jsx)(n.em,{children:"Application authors"})," may wish to wait to evaluate flags until the provider has fully started.\nIn that case, they can await the ",(0,r.jsx)(n.code,{children:"PROVIDER_READY"})," event before evaluating any flags.\nThis can be especially useful for hiding UI elements or otherwise deferring evaluation until associated flags can be resolved accurately."]}),"\n",(0,r.jsx)(n.h3,{id:"provider_configuration_changed",children:"PROVIDER_CONFIGURATION_CHANGED"}),"\n",(0,r.jsx)(n.p,{children:"A change was made to the backend flag configuration."}),"\n",(0,r.jsxs)(n.p,{children:["If the flag management system supports it, providers may emit a ",(0,r.jsx)(n.code,{children:"PROVIDER_CONFIGURATION_CHANGED"})," event signifying that flag configurations have changed.\nThis might mean the value of a flag (or flags) have changed, or that a rule has been updated.\nGenerally, the associated event details will indicate which flag (or flags) have been changed with the ",(0,r.jsx)(n.code,{children:"flags changed"})," field."]}),"\n",(0,r.jsx)(n.h3,{id:"provider_error",children:"PROVIDER_ERROR"}),"\n",(0,r.jsx)(n.p,{children:"The provider signaled an error."}
1),"\n",(0,r.jsxs)(n.p,{children:["If a provider becomes unavailable, evaluations will typically default.\nHandlers associated with ",(0,r.jsx)(n.code,{children:"PROVIDER_ERROR"})," events can be used to alert monitoring systems about the provider failure, or enable custom fallback mechanisms."]}),"\n",(0,r.jsx)(n.h3,{id:"provider_stale",children:"PROVIDER_STALE"}),"\n",(0,r.jsx)(n.p,{children:"The provider's cached state is no longer valid and may not be up-to-date with the source of truth."}),"\n",(0,r.jsxs)(n.p,{children:["Some providers maintain a connection to a management system, but are also tolerant of disconnection.\nThe ",(0,r.jsx)(n.code,{children:"PROVIDER_STALE"})," indicates this situation."]}),"\n",(0,r.jsx)(n.h3,{id:"provider_reconciling-static-contextclient-side-only",children:"PROVIDER_RECONCILING (Static-context/Client-side only)"}),"\n",(0,r.jsxs)(n.p,{children:["The context associated with the provider has changed, and the provider has not yet ",(0,r.jsx)(n.a,{href:"/docs/reference/concepts/sdk-paradigms#static-context-paradigms-client-side-sdks",children:"reconciled"})," its associated state."]}),"\n",(0,r.jsx)(n.h3,{id:"provider_context_changed-static-contextclient-side-only",children:"PROVIDER_CONTEXT_CHANGED (Static-context/Client-side only)"}),"\n",(0,r.jsxs)(n.p,{children:["The context associated with the provider has changed, and the provider has ",(0,r.jsx)(n.a,{href:"/docs/reference/concepts/sdk-paradigms#static-context-paradigms-client-side-sdks",children:"reconciled"})," its associated state."]})]})}function v(e={}){const{wrapper:n}={...(0,s.R)(),...e.components};return n?(0,r.jsx)(n,{...e,children:(0,r.jsx)(u,{...e})}):u(e)}},19365(e,n,t){t.d(n,{A:()=>l});t(96540);var a=t(34164),r=t(47751);const s="tabItem_Ymn6";var i=t(74848);function o({children:e,className:n,hidden:t}){return(0,i.jsx)("div",{role:"tabpanel",className:(0,a.A)(s,n),hidden:t,children:e})}function l({children:e,className:n,value:t}){const{selectedValue:a,lazy:s}=(0,r.uc)(),l=t===a;return!l&&s?null:(0,i.jsx)(o,{className:n,hidden:!l,children:e})}},4865(e,n,t){t.d(n,{A:()=>p});t(96540);var a=t(34164),r=t(17559),s=t(47751),i=t(23104),o=t(92303);const l="tabList__CuJ",c="tabItem_LNqP";var d=t(74848);function h({className:e}){const{selectedValue:n,selectValue:t,tabValues:r,block:o}=(0,s.uc)(),l=[],{blockElementScrollPositionUntilNextRender:h}=(0,i.a_)(),u=e=>{const a=e.currentTarget,s=l.indexOf(a),i=r[s].value;i!==n&&(h(a),t(i))},v=e=>{let n=null;switch(e.key){case"Enter":u(e);break;case"ArrowRight":{const t=l.indexOf(e.currentTarget)+1;n=l[t]??l[0];break}case"ArrowLeft":{const t=l.indexOf(e.currentTarget)-1;n=l[t]??l[l.length-1];break}}n?.focus()};return(0,d.jsx)("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,a.A)("tabs",{"tabs--block":o},e),children:r.map((({value:e,label:t,attributes:r})=>(0,d.jsx)("li",{role:"tab",tabIndex:n===e?0:-1,"aria-selected":n===e,ref:e=>{l.push(e)},onKeyDown:v,onClick:u,...r,className:(0,a.A)("tabs__item",c,r?.className,{"tabs__item--active":n===e}),children:t??e},e)))})}function u({children:e}){return(0,d.jsx)("div",{className:"margin-top--md",children:e})}function v({className:e,children:n}){return(0,d.jsxs)("div",{className:(0,a.A)(r.G.tabs.container,"tabs-container",l),children:[(0,d.jsx)(h,{className:e}),(0,d.jsx)(u,{children:n})]})}function p(e){const n=(0,o.A)(),t=(0,s.OC)(e);return(0,d.jsx)(s.O_,{value:t,children:(0,d.jsx)(v,{className:e.className,children:(0,s.vT)(e.children)})},String(n))}},47751(e,n,t){t.d(n,{OC:()=>p,O_:()=>m,uc:()=>f,vT:()=>d});var a=t(96540),r=t(90612),s=t(205),i=t(57485),o=t(70679),l=t(31682),c=t(74848);function d(e){return a.Children.toArray(e).filter((e=>"\n"!==e))}function h(e){const{values:n,children:t}=e;return(0,a.useMemo)((()=>{const e=n??function(e){return a.Children.toArray(e).flatMap((e=>{if(!e)return[];if((0,a.isValidElement)(e)&&function(e){const{props:n}=e;return!!n&&"object"==typeof n&&"value"in n}(e))return[e];const n="string"==typeof e.type?e.type:e.type.name;throw new Error(`Docusaurus error: Bad <Tabs> child <${n}>: all children of the <Tabs> component should be <TabItem>, and every <TabItem> should have a unique "value" prop.\nIf you do not want to pass on a "value" prop to the direct children of <Tabs>, you can also pass an explicit <Tabs values={...}> prop.`)})).map((({props:{value:e,label:n,attributes:t,default:a}})=>({value:e,label:n,attributes:t,default:a})))}(t);return function(e){const n=(0,l.XI)(e,((e,n)=>e.value===n.value));if(n.length>0)throw new Error(`Docusaurus error: Duplicate values "${n.map((e=>`'${e.value}'`)).join(", ")}" found in <Tabs>. Every value needs to be unique.`)}(e),e}),[n,t])}function u({value:e,tabValues:n}){return n.some((n=>n.value===e))}function v({queryString:e=!1,groupId:n}){const t=(0,r.W6)(),s=function({queryString:e=!1,groupId:n}){if("string"==typeof e)return e;if(!1===e)return null;if(!0===e&&!n)throw new Error('Docusaurus error: The <Tabs> component groupId prop is required if queryString=true, because this value is used as the search param name. You can also provide an explicit value such as queryString="my-search-param".');return n??null}({queryString:e,groupId:n});return[(0,i.aZ)(s),(0,a.useCallback)((e=>{if(!s)return;const n=new URLSearchParams(t.location.search);n.set(s,e),t.replace({...t.location,search:n.toString()})}),[s,t])]}function p(e){const{defaultValue:n,queryString:t=!1,groupId:r}=e,i=h(e),[l,c]=(0,a.useState)((()=>function({defaultValue:e,tabValues:n}){if(0===n.length)throw new Error("Docusaurus error: the <Tabs> component requires at least one <TabItem> children component");if(e){if(!u({value:e,tabValues:n}))throw new Error(`Docusaurus error: The <Tabs> has a defaultValue "${e}" but none of its children has the corresponding value. Available values are: ${n.map((e=>e.value)).join(", ")}. If you intend to show no default tab, use defaultValue={null} instead.`);return e}const t=n.find((e=>e.default))??n[0];if(!t)throw new Error("Unexpected error: 0 tabValues");return t.value}({defaultValue:n,tabValues:i}))),[d,p]=v({queryString:t,groupId:r}),[g,f]=function({groupId:e}){const n=function(e){return e?`docusaurus.tab.${e}`:null}(e),[t,r]=(0,o.Dv)(n);return[t,(0,a.useCallback)((e=>{n&&r.set(e)}),[n,r])]}({groupId:r}),m=(()=>{const e=d??g;return u({value:e,tabValues:i})?e:null})();(0,s.A)((()=>{m&&c(m)}),[m]);return{selectedValue:l,selectValue:(0,a.useCallback)((e=>{if(!u({value:e,tabValues:i}))throw new Error(`Can't select invalid tab value=${e}`);c(e),p(e),f(e)}),[p,f,i]),tabValues:i,lazy:e.lazy??!1,block:e.block??!1}}const g=(0,a.createContext)(null);function f(){const e=a.useContext(g);if(!e)throw new Error("useTabsContext() must be used within a Tabs component");return e}function m(e){return(0,c.jsx)(g.Provider,{value:e.value,children:e.children})}},28453(e,n,t){t.d(n,{R:()=>i,x:()=>o});var a=t(96540);const r={},s=a.createContext(r);function i(e){const n=a.useContext(s);return a.useMemo((function(){return"function"==typeof e?e(n):{...n,...e}}),[n,e])}function o(e){let n;return n=e.disableParentContext?"function"==typeof e.components?e.components(r):e.components||r:i(e.components),a.createElement(s.Provider,{value:n},e.children)}}}]);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.