UNPKG

unleash-server

Version:

Unleash is an enterprise ready feature flag service. It provides different strategies for handling feature flags.

689 lines (589 loc) 49.7 kB
import{n as e,s as t,t as n}from"./jsx-runtime-ButemYzH.js";import{$l as r,$r as i,$u as a,Ac as o,Al as s,Br as c,Cd as l,Co as u,Dd as d,Ea as f,Gr as p,Gu as m,H as h,Hr as g,Iu as _,Ji as v,Kn as y,La as b,Ls as x,Ml as S,Mo as ee,Ms as te,Na as ne,Nc as re,Ns as C,Nu as w,Ol as T,Ot as ie,Ps as E,Qu as D,Rl as ae,Ss as oe,Ts as se,Ua as ce,Ur as le,Vr as ue,Wa as O,Wo as de,Xr as fe,Xu as k,Yi as pe,Yl as me,Yr as he,Yu as A,Zi as ge,Zr as _e,_a as ve,_d as j,_o as M,aa as ye,ad as be,ca as xe,da as Se,fa as Ce,fr as we,fs as Te,ga as Ee,gs as De,ha as Oe,ia as ke,ja as Ae,ji as je,kc as N,kl as P,la as Me,lc as Ne,ll as Pe,ma as Fe,nd as Ie,nf as Le,oa as Re,oi as ze,pa as Be,qi as Ve,qn as He,qu as Ue,rd as F,ru as We,sa as Ge,ua as Ke,us as qe,uu as Je,va as Ye,xd as I,yi as Xe,yu as Ze,zi as L,zr as Qe}from"./index-B0RbDPtk.js";var R=n(),$e=j((0,R.jsx)(`path`,{d:`M19 3H5c-1.1 0-2 .9-2 2v14c0 1.1.9 2 2 2h14c1.1 0 2-.9 2-2V5c0-1.1-.9-2-2-2m-2 10H7v-2h10z`}),`IndeterminateCheckBox`),et=Ie(),tt=({options:e,selectedOptions:t,indeterminateOptions:n,tagType:i,existingTags:o,disabled:s=!1,onChange:c})=>{let l=(0,R.jsx)(He,{fontSize:`small`});return(0,R.jsx)(a,{multiple:!0,id:`checkboxes-tag`,sx:{marginTop:e=>e.spacing(2),width:500},disableCloseOnSelect:!0,options:e,value:t,isOptionEqualToValue:(e,t)=>t.inputValue&&t.inputValue!==``?e.title===t.inputValue:e.title===t.title,getOptionLabel:e=>e.inputValue?e.inputValue:e.title,renderOption:({key:e,...t},i,{selected:a})=>{let o=n?.some(e=>e.title===i.title)??!1;return(0,R.jsxs)(`li`,{...t,children:[(0,R.jsx)(r,{condition:!!i.inputValue,show:(0,R.jsx)(b,{sx:{mr:e=>e.spacing(.5)}}),elseShow:(0,R.jsx)(Ue,{icon:l,checkedIcon:(0,R.jsx)(y,{fontSize:`small`}),indeterminateIcon:(0,R.jsx)($e,{fontSize:`small`}),sx:{mr:e=>e.spacing(.5)},checked:a&&!o,indeterminate:o})}),i.title]},e)},filterOptions:(e,t)=>{let n=t.inputValue.trim(),r=et(e,{...t,inputValue:n}),i=e.some(e=>n===e.title);return n.length>=2&&!i&&r.push({inputValue:n,title:`Create new value "${n}"`}),r},ListboxProps:{style:{maxHeight:200,overflow:`auto`}},onChange:c,renderInput:e=>(0,R.jsx)(Je,{...e,label:`Select values`,placeholder:`Select values`}),disabled:s})},nt=I(`li`)({flexDirection:`column`}),rt=({options:e,value:t,disabled:n=!1,onChange:r})=>{let i=l();return(0,R.jsx)(a,{disablePortal:!0,disabled:n,id:`tag-type-select`,sx:{marginTop:e=>e.spacing(2),width:500},options:e,disableClearable:!0,value:t,getOptionLabel:e=>e.name,renderOption:({key:e,...t},n)=>(0,R.jsxs)(nt,{...t,style:{alignItems:`flex-start`,gap:i.spacing(.5)},children:[(0,R.jsx)(F,{variant:`body1`,children:n.name}),(0,R.jsx)(F,{variant:`caption`,children:n.description})]},e),renderInput:e=>(0,R.jsx)(Je,{...e,label:`Tag type`,value:t}),onChange:r,ListboxProps:{style:{maxHeight:200,overflow:`auto`}}})},it=()=>{let{makeRequest:e,createRequest:t,errors:n,loading:r}=te({propagateErrors:!0});return{createTag:async n=>{let r=t(`api/admin/tags`,{method:`POST`,body:JSON.stringify(n)});return e(r.caller,r.id)},bulkUpdateTags:async(n,r)=>{let i=t(`api/admin/projects/${r}/tags`,{method:`PUT`,body:JSON.stringify(n)});return e(i.caller,i.id)},errors:n,loading:r}},z=t(e()),at=(e,t={})=>{let n=async()=>{let t=T(`api/admin/tags/${e}`);return(await fetch(t,{method:`GET`}).then(o(`Tags`))).json()},r=`api/admin/tags/${e}`,{data:i,error:a}=oe(!!e,{tags:[]},r,n,t),[s,c]=(0,z.useState)(!a&&!i);return(0,z.useEffect)(()=>{c(!a&&!i)},[i,a]),{tags:i?.tags||[],error:a,loading:s,refetch:()=>{S(r)}}},ot=(e,t)=>{let{project:n,refetch:r}=u(e);return{defaultStrategyFallback:{name:`flexibleRollout`,constraints:[],parameters:{rollout:`100`,stickiness:n.defaultStickiness||`default`,groupId:``}},strategy:n.environments?.find(e=>e.environment===t)?.defaultStrategy,refetch:r}},st=()=>{let e=ne(`projectId`),t=i(`environmentId`),{refetch:n}=u(e),{defaultStrategyFallback:r,strategy:a,refetch:o}=ot(e,t),[s,c]=(0,z.useState)(a||r),{updateDefaultStrategy:l,loading:d}=Xe(),{strategyDefinition:f}=fe(s.name),{setToastData:p,setToastApiError:m}=E(),h=_e(),{uiConfig:g}=N(),{unleashUrl:_}=g,v=Le(),{trackEvent:y}=x(),b=ue(s.constraints);if(!f)return null;let S=ct(s);return(0,R.jsx)(Ve,{modal:!0,disablePadding:!0,description:ut,documentationLink:dt,documentationLinkLabel:ft,formatApiCode:()=>lt(e,t,S,f,_),children:(0,R.jsxs)(we,{strategy:s,setStrategy:c,strategyDefinition:f,errors:h,groupIdTooltip:`Defaults to the feature flag name if not set.`,onSubmit:async()=>{let r=`/projects/${e}/settings/default-strategy`;try{await l(e,t,S),y(`default_strategy`,{props:{action:`edit`,hasTitle:!!S.title}}),n(),p({text:`Default Strategy updated`,type:`success`}),await o(),v(r)}catch(e){m(C(e))}},children:[(0,R.jsx)(M,{permission:[ee,de],projectId:e,environmentId:t,variant:`contained`,color:`primary`,type:`submit`,disabled:d||!b||h.hasFormErrors(),"data-testid":Pe,children:`Save strategy`}),(0,R.jsx)(A,{type:`button`,onClick:()=>v(`/projects/${e}/settings/default-strategy`),children:`Cancel`})]})})},ct=e=>({name:e.name,title:e.title,constraints:e.constraints??[],parameters:Object.fromEntries(Object.entries(e.parameters??{}).filter(([,e])=>e!==void 0).map(([e,t])=>[e,String(t)])),variants:e.variants??[],segments:e.segments??[],disabled:e.disabled??!1}),lt=(e,t,n,r,i)=>{if(!i)return``;let a={...n,parameters:he(n.parameters??{},r)};return`curl --location --request POST '${`${i}/api/admin/projects/${e}/environments/${t}/default-strategy`}' \\ --header 'Authorization: INSERT_API_KEY' \\ --header 'Content-Type: application/json' \\ --data-raw '${JSON.stringify(a,void 0,2)}'`},ut=` An activation strategy will only run when a feature flag is enabled and provides a way to control who will get access to the feature. If any of a feature flag's activation strategies returns true, the user will get access. `,dt=`https://docs.getunleash.io/concepts/projects#project-default-strategy`,ft=`Default strategy documentation`,pt=e=>{let{data:t,error:n,mutate:r}=s(T(`api/admin/environments/project/${e}`),mt);return{environments:(0,z.useMemo)(()=>t||[],[t]),refetchEnvironments:(0,z.useCallback)(async()=>{await r()},[r]),loading:!n&&!t,error:n}},mt=async e=>(await fetch(e).then(o(`Environments`)).then(e=>e.json())).environments.sort((e,t)=>e.sortOrder-t.sortOrder),ht=e=>{let{setToastData:t,setToastApiError:n}=E(),{addChange:r}=p(),{refetch:i}=le(e),[a,o]=(0,z.useState)(!1),[s,c]=(0,z.useState)({isOpen:!1});return{pending:a,onChangeRequestToggle:(0,z.useCallback)((e,t,n,r)=>{c({featureName:e,environment:t,enabled:n,shouldActivateDisabledStrategies:r,isOpen:!0})},[]),onChangeRequestToggleClose:(0,z.useCallback)(()=>{c(e=>({...e,isOpen:!1}))},[]),onChangeRequestToggleConfirm:(0,z.useCallback)(async()=>{try{o(!0),await r(e,s.environment,{feature:s.featureName,action:`updateEnabled`,payload:{enabled:!!s.enabled,shouldActivateDisabledStrategies:!!s.shouldActivateDisabledStrategies}}),i(),c(e=>({...e,isOpen:!1})),t({type:`success`,text:`Changes added to draft`})}catch(e){n(C(e)),c(e=>({...e,isOpen:!1}))}finally{o(!1)}},[r]),changeRequestDialogDetails:s}},gt=({enabled:e,featureName:t,environment:n})=>(0,R.jsxs)(F,{"data-testid":`update-enabled-message`,children:[(0,R.jsx)(`strong`,{children:e?`Enable`:`Disable`}),` feature flag`,` `,(0,R.jsx)(`strong`,{children:t}),` in `,(0,R.jsx)(`strong`,{children:n})]}),_t=I(`ul`)(({theme:e})=>({margin:e.spacing(1),paddingLeft:e.spacing(2)})),vt=({isOpen:e,onAddDefaultStrategy:t,onActivateDisabledStrategies:n,onClose:r,environment:i,featureId:a})=>{let o=ne(`projectId`),{feature:s}=ce(o,a),c=s.environments?.find(({name:e})=>e===i)?.strategies?.filter(({disabled:e})=>e).length,l=c?c===1?`1 disabled strategy`:`${c} disabled strategies`:`disabled strategies`;return(0,R.jsxs)(se,{open:e,secondaryButtonText:`Cancel`,permissionButton:(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(M,{type:`button`,variant:`outlined`,permission:qe,projectId:o,environmentId:i,onClick:t,children:`Add default strategy`}),(0,R.jsx)(M,{type:`button`,variant:`outlined`,permission:qe,projectId:o,environmentId:i,onClick:n,children:`Enable all strategies`})]}),onClose:r,title:`Enable feature flag in ${i}`,fullWidth:!0,children:[(0,R.jsx)(F,{sx:{mb:e=>e.spacing(3)},children:`A feature flag cannot be enabled without an enabled strategy.`}),(0,R.jsx)(F,{children:`To enable this feature flag you can choose to:`}),(0,R.jsxs)(_t,{children:[(0,R.jsx)(`li`,{children:(0,R.jsx)(F,{children:(0,R.jsx)(`strong`,{children:`Add the default strategy`})})}),(0,R.jsx)(`li`,{children:(0,R.jsxs)(F,{children:[(0,R.jsx)(`strong`,{children:`Enable all the disabled strategies`}),` `,`(this feature flag has `,l,`)`]})})]})]})},yt=e=>{let t=n=>{n<e.length&&e[n](()=>t(n+1))};t(0)},bt=e=>{let{toggleFeatureEnvironmentOn:t,toggleFeatureEnvironmentOff:n}=g(),{setToastData:i,setToastApiError:a}=E(),[o,s]=(0,z.useState)({open:!1,label:``,loading:!1,onClose:()=>{},onClick:()=>{}}),[l,u]=(0,z.useState)({isOpen:!1,environment:``,featureId:``,onClose:()=>{},onActivateDisabledStrategies:()=>{},onAddDefaultStrategy:()=>{}}),{pending:d,onChangeRequestToggle:f,onChangeRequestToggleClose:p,onChangeRequestToggleConfirm:m,changeRequestDialogDetails:_}=ht(e),[v,y]=(0,z.useState)(),b=(0,z.useCallback)(async(e,r)=>{let o=!1;return yt([t=>{if(r.isChangeRequestEnabled||!c(r.environmentType||``))return t();s({open:!0,label:`${e?`Enable`:`Disable`} Environment`,loading:!1,onClose:()=>{s(e=>({...e,open:!1})),r.onRollback?.()},onClick:()=>{s(e=>({...e,open:!1,loading:!0})),t()}})},t=>{if(e===!1||!r.hasStrategies||r.hasEnabledStrategies||r.hasReleasePlans)return t();u({isOpen:!0,environment:r.environmentName,featureId:r.featureId,onClose:()=>{u(e=>({...e,isOpen:!1})),r.onRollback?.()},onActivateDisabledStrategies:()=>{u(e=>({...e,isOpen:!1})),o=!0,t()},onAddDefaultStrategy:()=>{u(e=>({...e,isOpen:!1})),t()}})},t=>{if(!r.isChangeRequestEnabled)return t();y(()=>{y(void 0),r.onRollback?.()}),f(r.featureId,r.environmentName,e,o)},async t=>{if(e!==!1)return t();try{await n(r.projectId,r.featureId,r.environmentName),i({type:`success`,text:`Disabled in ${r.environmentName}`}),r.onSuccess?.()}catch(e){a(C(e)),r.onRollback?.()}},async n=>{if(e!==!0)return n();try{await t(r.projectId,r.featureId,r.environmentName,o),i({type:`success`,text:`Enabled in ${r.environmentName}`}),r.onSuccess?.()}catch(e){a(C(e)),r.onRollback?.()}}])},[s]),x=l.featureId.length!==0;return{onToggle:b,modals:(0,R.jsxs)(R.Fragment,{children:[(0,R.jsx)(Qe,{...o}),(0,R.jsx)(r,{condition:x,show:(0,R.jsx)(vt,{...l})}),(0,R.jsx)(h,{isOpen:_.isOpen,onClose:()=>{v?.(),p()},environment:_?.environment,disabled:d,onConfirm:()=>{v?.(),m()},messageComponent:(0,R.jsx)(gt,{enabled:_?.enabled,featureName:_?.featureName,environment:_.environment})})]})}},xt=e=>{let[t,n]=(0,z.useState)(e),r=(0,z.useCallback)(()=>n(e),[e]);return(0,z.useEffect)(()=>{n(e)},[e]),[t,n,r]},St=I(k)(()=>({mx:`auto`,...We})),Ct=({projectId:e,featureId:t,environmentName:n,value:r,onToggle:i})=>{let[a,o,s]=xt(r),c=()=>{o(!a),requestAnimationFrame(()=>{i(!a,s)})},l=`${t}-${n}`;return(0,R.jsx)(R.Fragment,{children:(0,R.jsx)(St,{"data-testid":`TOGGLE-${l}`,children:(0,R.jsx)(f,{tooltip:a?`Disable flag in ${n}`:`Enable flag in ${n}`,checked:r,environmentId:n,projectId:e,permission:Te,inputProps:{"aria-label":n},onClick:c,"data-testid":`permission-switch`,disabled:r!==a})},l)})},wt=j((0,R.jsx)(`path`,{d:`M16 11h-1V3c0-1.1-.9-2-2-2h-2c-1.1 0-2 .9-2 2v8H8c-2.76 0-5 2.24-5 5v7h18v-7c0-2.76-2.24-5-5-5m3 10h-2v-3c0-.55-.45-1-1-1s-1 .45-1 1v3h-2v-3c0-.55-.45-1-1-1s-1 .45-1 1v3H9v-3c0-.55-.45-1-1-1s-1 .45-1 1v3H5v-5c0-1.65 1.35-3 3-3h8c1.65 0 3 1.35 3 3z`}),`CleaningServices`),Tt=`flag-reminders:v1`,Et=50,Dt=7,Ot=({days:e=Dt,maxReminders:t=Et}={})=>{let[n,r]=Ne(Tt,{});return{shouldShowReminder:e=>{let t=n[e];return!t||ae(new Date,new Date(t))},snoozeReminder:(n,i=e)=>{let a=me(new Date,i).getTime();r(e=>{let r={...e,[n]:a},i=Object.entries(r);if(i.length>t){i.sort((e,t)=>e[1]-t[1]);let e=i.slice(i.length-t);return Object.fromEntries(e)}return r})}}},kt=(e,t={})=>{let{data:n,error:r,mutate:i}=s(T(`api/admin/projects/${e}/api-tokens`),At,t),a=(0,z.useMemo)(()=>n??[],[n]),o=(0,z.useCallback)(()=>{i().catch(console.warn)},[i]);return{tokens:a,error:r,loading:!r&&!n,refetch:o}},At=async e=>(await(await fetch(e).then(o(`Project Api tokens`))).json()).tokens,jt=()=>{let{makeRequest:e,createRequest:t,errors:n,loading:r}=te({propagateErrors:!0});return{deleteToken:async(n,r)=>{let i=t(`api/admin/projects/${r}/api-tokens/${n}`,{method:`DELETE`});return e(i.caller,i.id)},createToken:async(n,r)=>{let i=t(`api/admin/projects/${r}/api-tokens`,{method:`POST`,body:JSON.stringify(n)});return e(i.caller,i.id)},errors:n,loading:r}},Mt=e=>{if(!e)return null;let[t,n]=e.split(`:`,2);if(!n)return null;let[r,i,...a]=n.split(`.`);return t&&r&&i&&a.length===0?{project:t,environment:r,secret:i}:null},B=ie(),V=I(`div`)(({theme:e})=>({fontWeight:e.typography.fontWeightBold,marginBottom:e.spacing(1),fontSize:e.typography.body1.fontSize})),H=I(`div`)(({theme:e})=>({display:`flex`,alignItems:`center`,justifyContent:`space-between`,marginTop:e.spacing(2)})),Nt=I(`div`)(({theme:e})=>({display:`flex`,gap:e.spacing(1)})),Pt=I(`div`)(({theme:e})=>({background:e.palette.background.application,borderRadius:e.shape.borderRadius,width:e.spacing(7),height:e.spacing(1)})),Ft=I(`div`)(({theme:e})=>({background:e.palette.background.sidebar,borderRadius:e.shape.borderRadius,width:e.spacing(7),height:e.spacing(1)})),U=({active:e,steps:t})=>(0,R.jsx)(Nt,{children:Array.from({length:t},(t,n)=>n===e?(0,R.jsx)(Ft,{},n):(0,R.jsx)(Pt,{},n))}),It=({environments:e,onSelect:t,currentEnvironment:n})=>{let r=Math.max(...e.map(e=>e.length));return(0,R.jsx)(ze,{tooltip:{header:``},description:`Select the environment where the API key will be created`,options:e.map(e=>({label:e,value:e})),onChange:e=>{t(e)},button:{label:n,icon:(0,R.jsx)(De,{}),labelWidth:`${r+5}ch`},search:{label:`Filter project mode options`,placeholder:`Select project mode`}})},Lt=I(`div`)(({theme:e})=>({backgroundColor:e.palette.background.elevation1,borderRadius:e.shape.borderRadius,padding:e.spacing(3),display:`flex`,flexDirection:`column`,alignItems:`center`})),W=I(`div`)(({theme:e})=>({backgroundColor:e.palette.background.paper,borderRadius:e.shape.borderRadius,padding:e.spacing(2),flex:1,color:e.palette.text.secondary,fontSize:e.typography.body2.fontSize})),Rt=I(k)(({theme:e})=>({display:`flex`,gap:e.spacing(2),alignItems:`flex-start`,marginTop:e.spacing(8),flexWrap:`wrap`})),G=I(`p`)(({theme:e})=>({color:e.palette.text.secondary,fontSize:e.typography.body2.fontSize,marginBottom:e.spacing(2)})),zt=I(`div`)(({theme:e})=>({padding:e.spacing(5,8,3,8),display:`flex`,flexDirection:`column`,gap:e.spacing(3)})),Bt=({project:e,environment:t,secret:n})=>{let r=l(),i=_(r.breakpoints.up(`lg`));return(0,R.jsx)(B.ArcherContainer,{strokeColor:r.palette.secondary.border,endMarker:!1,lineStyle:`curve`,children:(0,R.jsxs)(Lt,{children:[(0,R.jsxs)(k,{sx:{wordBreak:`break-all`},children:[(0,R.jsx)(B.ArcherElement,{id:`project`,children:(0,R.jsx)(`span`,{children:e})}),`:`,(0,R.jsx)(B.ArcherElement,{id:`environment`,children:(0,R.jsx)(`span`,{children:t})}),`.`,(0,R.jsx)(B.ArcherElement,{id:`secret`,children:(0,R.jsx)(`span`,{children:n})})]}),i?(0,R.jsxs)(Rt,{children:[(0,R.jsx)(B.ArcherElement,{id:`project-description`,relations:[{targetId:`project`,targetAnchor:`bottom`,sourceAnchor:`top`}],children:(0,R.jsx)(W,{children:`The project this API key can retrieve feature flags from`})}),(0,R.jsx)(B.ArcherElement,{id:`environment-description`,relations:[{targetId:`environment`,targetAnchor:`bottom`,sourceAnchor:`top`}],children:(0,R.jsx)(W,{children:`The environment this API key can retrieve feature flag configuration from`})}),(0,R.jsx)(B.ArcherElement,{id:`secret-description`,relations:[{targetId:`secret`,targetAnchor:`right`,sourceAnchor:`top`}],children:(0,R.jsx)(W,{children:`The API key secret`})})]}):null]})})},Vt=({environments:e,environment:t,project:n,sdkType:r,onEnvSelect:i,onApiKey:a})=>{let{trackEvent:o}=x(),{tokens:s,refetch:c}=kt(n),{createToken:l,loading:u}=jt(),d=s.find(e=>e.environment===t&&e.type===r);(0,z.useEffect)(()=>{a(d?.secret||null)},[d]);let f=Mt(d?.secret),{setToastApiError:p}=E(),m=async()=>{try{await l({environment:t,type:r,projects:[n],tokenName:`api-key-${n}-${t}`},n),c(),h()}catch(e){p(C(e))}},h=()=>{o(`onboarding`,{props:{eventType:`api-key-generated`}})};return(0,R.jsxs)(zt,{children:[(0,R.jsx)(F,{variant:`h2`,children:`Connect an SDK to Unleash`}),(0,R.jsxs)(H,{children:[(0,R.jsx)(U,{active:1,steps:3}),(0,R.jsx)(O,{color:`secondary`,children:`2/3 - Generate API Key`})]}),(0,R.jsxs)(k,{sx:{mt:2},children:[(0,R.jsx)(V,{children:`Environment`}),(0,R.jsx)(G,{children:`The environment the SDK connects to to retrieve configuration.`}),e.length>0?(0,R.jsx)(It,{environments:e,currentEnvironment:t,onSelect:i}):null]}),(0,R.jsxs)(k,{sx:{mt:3},children:[(0,R.jsx)(V,{children:`API Key`}),f?(0,R.jsxs)(G,{children:[`Here is your generated API key. We will use it to connect to the `,(0,R.jsx)(`b`,{children:f.project}),` project in the `,(0,R.jsx)(`b`,{children:f.environment}),` environment.`]}):(0,R.jsx)(G,{children:`You currently have no active API keys for this project/environment combination. Generate an API key to proceed with connecting your SDK.`}),f?(0,R.jsx)(Bt,{project:f.project,environment:f.environment,secret:f.secret}):(0,R.jsx)(A,{variant:`contained`,disabled:u,onClick:m,children:`Generate API Key`})]})]})},Ht=[{name:`Node.js`,icon:Se},{name:`Go`,icon:Oe},{name:`Ruby`,icon:Ge},{name:`PHP`,icon:Ke},{name:`Rust`,icon:Re},{name:`.NET`,icon:ve},{name:`Java`,icon:Be},{name:`Python`,icon:Me}],K=[{name:`JavaScript`,icon:Ce},{name:`React`,icon:xe},{name:`Vue`,icon:ke},{name:`Svelte`,icon:ye},{name:`Swift`,icon:Fe},{name:`Android`,icon:Ye},{name:`Flutter`,icon:Ee}],Ut=[...Ht,...K],Wt=I(`div`)(({theme:e})=>({padding:e.spacing(5,8,8,8),display:`flex`,flexDirection:`column`,gap:e.spacing(3)})),q=I(`div`)(({theme:e})=>({marginTop:e.spacing(4),marginBottom:e.spacing(2),fontSize:e.typography.body1.fontSize})),Gt=I(`div`)(({theme:e})=>({backgroundColor:e.palette.background.elevation1,borderRadius:e.shape.borderRadius,padding:e.spacing(6),display:`flex`,columnGap:e.spacing(2),rowGap:e.spacing(5),alignItems:`center`,justifyContent:`flex-start`,flexWrap:`wrap`})),Kt=I(`div`)(({theme:e})=>({fontSize:e.typography.body2.fontSize,backgroundColor:e.palette.background.default,borderRadius:e.shape.borderRadius,padding:e.spacing(2,3),width:`170px`,position:`relative`})),qt=I(`div`)(()=>({display:`flex`,justifyContent:`space-between`})),Jt=I(D)(({theme:e})=>({position:`absolute`,width:e.spacing(4),height:e.spacing(4),top:e.spacing(-2.75),left:e.spacing(2),boxShadow:e.shadows[2]})),Yt=({onSelect:e})=>(0,R.jsxs)(Wt,{children:[(0,R.jsx)(F,{variant:`h2`,children:`Connect an SDK to Unleash`}),(0,R.jsxs)(H,{children:[(0,R.jsx)(U,{active:0,steps:3}),(0,R.jsx)(O,{color:`secondary`,children:`1/3 - Choose SDK`})]}),(0,R.jsxs)(k,{sx:{mt:2},children:[(0,R.jsx)(V,{children:`Select SDK`}),(0,R.jsx)(q,{children:`Server side SDKs`}),(0,R.jsx)(Gt,{children:Ht.map(t=>(0,R.jsxs)(Kt,{children:[(0,R.jsx)(Jt,{src:P(t.icon)}),(0,R.jsxs)(qt,{children:[(0,R.jsx)(`b`,{children:t.name}),` `,(0,R.jsx)(w,{onClick:()=>e({name:t.name,type:`client`}),component:`button`,children:`Select`})]})]},t.name))}),(0,R.jsx)(q,{children:`Client side SDKs`}),(0,R.jsx)(Gt,{children:K.map(t=>(0,R.jsxs)(Kt,{children:[(0,R.jsx)(Jt,{src:P(t.icon)}),(0,R.jsxs)(qt,{children:[(0,R.jsx)(`b`,{children:t.name}),` `,(0,R.jsx)(w,{onClick:()=>e({name:t.name,type:`frontend`}),component:`button`,children:`Select`})]})]},t.name))})]})]}),Xt=I(`div`)(({theme:e})=>({backgroundColor:e.palette.background.sidebar,padding:e.spacing(12,6,6,6),flex:0,minWidth:`400px`})),J=I(`p`)(({theme:e})=>({color:e.palette.primary.contrastText,fontSize:e.typography.caption.fontSize,marginBottom:e.spacing(2)})),Y=({theme:e})=>({color:e.palette.primary.contrastText,fontSize:e.typography.body2.fontSize,marginTop:e.spacing(.5)}),Zt=I(re)(Y),Qt=I(De)(Y),$t=I(Ae)(Y),X=I(`div`)(({theme:e})=>({display:`flex`,gap:e.spacing(1.5),alignItems:`flex-start`,marginTop:e.spacing(3)})),Z=I(`div`)(({theme:e})=>({color:e.palette.primary.contrastText,fontSize:e.typography.body2.fontSize,fontWeight:e.typography.fontWeightBold,marginBottom:e.spacing(2)})),en=()=>(0,R.jsxs)(Xt,{children:[(0,R.jsxs)(X,{children:[(0,R.jsx)(Zt,{}),(0,R.jsxs)(k,{children:[(0,R.jsx)(Z,{children:`Flags live in projects`}),(0,R.jsx)(J,{children:`Projects are containers for feature flags. Each flag belongs to the project where you create it.`})]})]}),(0,R.jsxs)(X,{children:[(0,R.jsx)(Qt,{}),(0,R.jsxs)(k,{children:[(0,R.jsx)(Z,{children:`Flags have configuration in environments`}),(0,R.jsx)(J,{children:`You can have multiple environments, and each feature flag has a different configuration per environment.`})]})]}),(0,R.jsxs)(X,{children:[(0,R.jsx)($t,{}),(0,R.jsxs)(k,{children:[(0,R.jsx)(Z,{children:`SDKs connect to Unleash to retrieve configuration`}),(0,R.jsx)(J,{children:`When you connect an SDK to Unleash, it uses the API key to identify which feature flags and configuration to retrieve from each environment.`})]})]})]}),tn=()=>(0,R.jsx)(Xt,{children:(0,R.jsxs)(X,{children:[(0,R.jsx)($t,{}),(0,R.jsxs)(k,{children:[(0,R.jsx)(Z,{children:`SDKs and Unleash`}),(0,R.jsx)(J,{children:`SDKs serve to bind your application to Unleash. The SDK can connect to Unleash via HTTP and retrieve feature flag configuration that is then cached in your application. Making sure none of your application data ever leaves your servers.`})]})]})}),nn=I(D,{shouldForwardProp:e=>e!==`active`})(({theme:e,active:t})=>({transition:`background-color 0.5s ease`,color:e.palette.common.white,backgroundColor:t?e.palette.primary.main:e.palette.divider,"@keyframes pulse":{"0%":{boxShadow:`0 0 0 0px ${d(e.palette.primary.main,.7)}`},"100%":{boxShadow:`0 0 0 20px ${d(e.palette.primary.main,0)}`}},animation:t?`pulse 2s infinite`:``})),rn=I(D,{shouldForwardProp:e=>e!==`active`})(({theme:e,active:t})=>({transition:`background-color 0.5s ease`,color:e.palette.primary.main,backgroundColor:t?e.palette.background.default:e.palette.divider,"@keyframes pulse":{"0%":{boxShadow:`0 0 0 0px ${d(e.palette.background.default,.7)}`},"100%":{boxShadow:`0 0 0 20px ${d(e.palette.background.default,0)}`}},animation:t?`pulse 2s infinite`:``})),an=j((0,R.jsx)(`path`,{d:`M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2M7 13.5c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5m5 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5m5 0c-.83 0-1.5-.67-1.5-1.5s.67-1.5 1.5-1.5 1.5.67 1.5 1.5-.67 1.5-1.5 1.5`}),`Pending`),on=I(`div`)(({theme:e})=>({backgroundColor:e.palette.background.sidebar,padding:e.spacing(6,9,6,9),minWidth:`400px`,display:`flex`,flexDirection:`column`,color:e.palette.primary.contrastText})),sn=I(F)(({theme:e})=>({display:`flex`,alignItems:`center`,justifyContent:`center`,fontWeight:e.typography.fontWeightBold})),cn=I(`div`)(({theme:e})=>({display:`flex`,flexDirection:`row`,padding:e.spacing(4,0,12,0),justifyContent:`space-between`,fontSize:e.spacing(1)})),ln=I(`div`)(({theme:e})=>({display:`flex`,gap:e.spacing(1),flexDirection:`column`})),un=I(`div`)(({theme:e})=>({alignItems:`center`,justifyContent:`center`,display:`flex`,gap:e.spacing(2),flexDirection:`column`,fontSize:e.fontSizes.smallBody})),dn=I(je)(({theme:e})=>({color:e.palette.primary.main,backgroundColor:e.palette.background.paper,borderRadius:`50%`,padding:e.spacing(1),width:`80px`,height:`80px`})),fn=({projectId:e,sdk:t,environment:n})=>{let r=l(),{project:i}=u(e,{refreshInterval:1e3}),a=i.onboardingStatus.status===`onboarded`;return(0,R.jsxs)(on,{children:[(0,R.jsx)(sn,{children:`Connection information`}),(0,R.jsxs)(cn,{children:[(0,R.jsxs)(ln,{children:[(0,R.jsx)(F,{fontWeight:`bold`,variant:`body2`,children:`Environment`}),(0,R.jsx)(F,{variant:`body2`,children:n})]}),(0,R.jsxs)(ln,{children:[(0,R.jsx)(F,{fontWeight:`bold`,variant:`body2`,children:`SDK`}),(0,R.jsx)(F,{variant:`body2`,children:t})]})]}),a?(0,R.jsxs)(un,{children:[(0,R.jsx)(F,{fontWeight:`bold`,variant:`body2`,children:`Connection status`}),(0,R.jsx)(F,{sx:{mb:r.spacing(4)},variant:`body2`,children:`Connected`}),(0,R.jsx)(dn,{}),(0,R.jsx)(F,{sx:{mb:r.spacing(4)},variant:`body2`,children:`We received metrics from your application!`})]}):(0,R.jsxs)(un,{children:[(0,R.jsx)(F,{fontWeight:`bold`,variant:`body2`,children:`Connection status`}),(0,R.jsx)(F,{sx:{mb:r.spacing(4)},variant:`body2`,children:`Waiting for SDK data...`}),(0,R.jsx)(rn,{sx:{width:80,height:80},active:!0,children:(0,R.jsx)(an,{fontSize:`large`})})]})]})},pn=t(v()),Q={Android:`1\\. Install the SDK \`\`\`gradle implementation("io.getunleash:unleash-android:1") \`\`\` 2\\. Enable required [permissions](https://developer.android.com/guide/topics/manifest/uses-permission-element) \`\`\`xml <uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> \`\`\` 2\\. Initialize Unleash in your application \`\`\`kotlin class MyApplication: Application() { val unleash: Unleash by lazy { val instance = DefaultUnleash( androidContext = this, unleashConfig = UnleashConfig.newBuilder(appName = "unleash-onboarding-android") .proxyUrl("<YOUR_API_URL>") .clientKey("<YOUR_API_TOKEN>") .build() ) instance.start() instance } override fun onTerminate() { super.onTerminate() unleash.close() } } \`\`\` 3\\. Check flag status \`\`\`kotlin class MainActivity : ComponentActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val unleashInstance = (application as MyApplication).unleash setContent { var flagStatus by remember { mutableStateOf("loading") } LaunchedEffect(Unit) { while (isActive) { val isFlagEnabled = unleashInstance.isEnabled("<YOUR_FLAG>") flagStatus = if (isFlagEnabled) "enabled" else "disabled" delay(3000L) } } Text(text = "Flag is $flagStatus!") } } } \`\`\` ℹ️ **Info:** The Android SDK takes at least 60 seconds to post metrics to Unleash. --- --- - [SDK repository with documentation and example](https://github.com/Unleash/unleash-android) - [Android SDK basic example](hhttps://github.com/Unleash/unleash-sdk-examples/tree/main/Android) `,Go:`1\\. Install the SDK \`\`\`sh go get github.com/Unleash/unleash-client-go/v4 \`\`\` 2\\. Run Unleash \`\`\`go package main import ( "github.com/Unleash/unleash-client-go/v4" "net/http" "time" ) func init() { unleash.Initialize( unleash.WithListener(&unleash.DebugListener{}), unleash.WithAppName("unleash-onboarding-golang"), unleash.WithUrl("<YOUR_API_URL>"), unleash.WithCustomHeaders(http.Header{"Authorization": {"<YOUR_API_TOKEN>"}}), // in production use environment variable ) } func main() { for { unleash.IsEnabled("<YOUR_FLAG>") time.Sleep(time.Second) } } \`\`\` --- \`\`\`go func init() { unleash.Initialize( unleash.WithListener(&unleash.DebugListener{}), unleash.WithAppName("unleash-onboarding-golang"), unleash.WithUrl("<YOUR_API_URL>"), unleash.WithCustomHeaders(http.Header{ "Authorization": {os.Getenv("UNLEASH_API_KEY")}, }) ) } \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash-client-go) - [Go SDK example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Go) `,JavaScript:`1\\. Install the SDK \`\`\`sh npm install unleash-proxy-client \`\`\` 2\\. Run Unleash \`\`\`js const { UnleashClient } = require('unleash-proxy-client'); const unleash = new UnleashClient({ url: '<YOUR_API_URL>', clientKey: '<YOUR_API_TOKEN>', // in production use environment variable appName: 'unleash-onboarding-javascript', }); unleash.start(); setInterval(() => { console.log('Is enabled', unleash.isEnabled('<YOUR_FLAG>')); }, 1000); \`\`\` --- \`\`\`js const unleash = new UnleashClient({ url: '<YOUR_API_URL>', clientKey: process.env.UNLEASH_API_TOKEN, appName: 'unleash-onboarding-javascript', }); unleash.start(); \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash-proxy-client-js) - [JavaScript SDK example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/JavaScript) `,"Node.js":`1\\. Install the SDK \`\`\`sh npm install unleash-client \`\`\` 2\\. Run Unleash \`\`\`js const { initialize } = require('unleash-client'); const unleash = initialize({ url: '<YOUR_API_URL>', appName: 'unleash-onboarding-node', customHeaders: { Authorization: '<YOUR_API_TOKEN>' // in production use environment variable }, }); setInterval(() => { console.log('Is enabled', unleash.isEnabled('<YOUR_FLAG>')); }, 1000); \`\`\` --- \`\`\`js const { initialize } = require('unleash-client'); const unleash = initialize({ url: '<YOUR_API_URL>', appName: 'unleash-onboarding-node', customHeaders: { Authorization: process.env.UNLEASH_API_KEY }, }); \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash-client-node) - [Node.js SDK example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Node.js) - [Node.js SDK tutorial](https://dev.to/reeshee/how-to-implement-feature-flags-in-nodejs-using-unleash-3907) `,Python:`1\\. Install the SDK \`\`\`sh pip install UnleashClient \`\`\` 2\\. Run Unleash \`\`\`python from UnleashClient import UnleashClient import asyncio client = UnleashClient( url="<YOUR_API_URL>", app_name="unleash-onboarding-python", custom_headers={'Authorization': '<YOUR_API_TOKEN>'}) # in production use environment variable client.initialize_client() while True: print(client.is_enabled("<YOUR_FLAG>")) asyncio.run(asyncio.sleep(1)) \`\`\` --- \`\`\`python from UnleashClient import UnleashClient import asyncio import os client = UnleashClient( url="<YOUR_API_URL>", app_name="unleash-onboarding-python", custom_headers={'Authorization': os.getenv('UNLEASH_API_TOKEN')} \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash-client-python) - [Python SDK example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Python) - [How to Implement Feature Flags in Python](https://docs.getunleash.io/feature-flag-tutorials/python) `,Ruby:`1\\. Install the SDK \`\`\`sh gem install unleash \`\`\` 2\\. Run Unleash \`\`\`rb require 'unleash' @unleash = Unleash::Client.new( url: "<YOUR_API_URL>", custom_http_headers: { 'Authorization': "<YOUR_API_TOKEN>" }, # in production use environment variable app_name: 'unleash-onboarding-ruby', instance_id: 'unleash-onboarding-ruby', ) while true if @unleash.is_enabled?("<YOUR_FLAG>") puts "Flag is enabled" else puts "Flag is not enabled" end sleep 3 end \`\`\` --- \`\`\`rb @unleash = Unleash::Client.new( url: "<YOUR_API_URL>", custom_http_headers: { 'Authorization': ENV['UNLEASH_API_TOKEN'] }, app_name: 'unleash-onboarding-ruby', instance_id: 'unleash-onboarding-ruby', ) \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash-client-ruby) - [Ruby example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Ruby) - [How to Implement Feature Flags in Ruby](https://docs.getunleash.io/feature-flag-tutorials/ruby) `,Svelte:`1\\. Install the SDK \`\`\`sh npm install @unleash/proxy-client-svelte \`\`\` 2\\. Initialize Unleash \`\`\`svelte <script> import { FlagProvider } from '@unleash/proxy-client-svelte'; const config = { url: '<YOUR_API_URL>', clientKey: '<YOUR_API_TOKEN>', // in production use environment variable appName: 'unleash-onboarding-svelte', }; <\/script> <div class="app"> <FlagProvider {config}> <main> <slot /> </main> </FlagProvider> </div> \`\`\` 3\\. Check feature flag status \`\`\`svelte <script lang="ts"> import { useFlag } from '@unleash/proxy-client-svelte'; const enabled = useFlag('<YOUR_FLAG>'); <\/script> <section> <p> {$enabled ? 'Feature is enabled!' : 'Feature is disabled!'} </p> </section> \`\`\` --- \`\`\`svelte const config = { url: '<YOUR_API_URL>', clientKey: import.meta.env.VITE_UNLEASH_API_TOKEN, appName: 'unleash-onboarding-svelte', }; \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/proxy-client-svelte) - [Svelte example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Svelte) - [How to Implement Feature Flags in SvelteKit](https://docs.getunleash.io/feature-flag-tutorials/sveltekit) `,Vue:`1\\. Install the SDK \`\`\`sh npm install @unleash/proxy-client-vue \`\`\` 2\\. Initialize Unleash \`\`\`vue <script setup lang="ts"> import { FlagProvider } from '@unleash/proxy-client-vue' const config = { url: '<YOUR_API_URL>', clientKey: '<YOUR_API_TOKEN>', // in production use environment variable appName: 'unleash-onboarding-vue', } <\/script> <template> <FlagProvider :config="config"> <!-- <YourComponent /> --> </FlagProvider> </template> \`\`\` 3\\. Check feature flag status \`\`\`vue <script setup lang="ts"> import { useFlag } from '@unleash/proxy-client-vue' const enabled = useFlag('<YOUR_FLAG>') <\/script> <template> <div> {{ enabled ? 'Feature is enabled!' : 'Feature is disabled!' }} </div> </template> \`\`\` --- \`\`\`svelte const config = { url: '<YOUR_API_URL>', clientKey: import.meta.env.VITE_UNLEASH_API_TOKEN, appName: 'unleash-onboarding-vue', } \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/proxy-client-vue) - [Vue example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Vue) `,Flutter:`1\\. Install the SDK \`\`\`sh flutter pub add unleash_proxy_client_flutter \`\`\` 2\\. Run Unleash \`\`\`dart import 'package:unleash_proxy_client_flutter/unleash_proxy_client_flutter.dart'; import 'dart:async'; final unleash = UnleashClient( url: Uri.parse('<YOUR_API_URL>'), clientKey: '<YOUR_API_TOKEN>', // in production use environment variable appName: 'unleash-onboarding-flutter'); unleash.start(); Timer.periodic(Duration(seconds: 1), (Timer timer) { final flagStatus = unleash.isEnabled('<YOUR_FLAG>'); print('Flag is \${unleash.isEnabled("<YOUR_FLAG>") ? "enabled" : "disabled"}'); }); \`\`\` --- \`\`\`dart import 'package:unleash_proxy_client_flutter/unleash_proxy_client_flutter.dart'; import 'dart:async'; import 'dart:io'; final unleash = UnleashClient( url: Uri.parse('<YOUR_API_URL>'), clientKey: Platform.environment['UNLEASH_CLIENT_KEY']!, appName: 'unleash-onboarding-flutter'); unleash.start(); \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash_proxy_client_flutter) - [Flutter example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Flutter) - [A/B Testing in Flutter using Unleash and Mixpanel](https://docs.getunleash.io/feature-flag-tutorials/flutter/a-b-testing) `,Java:`1\\. Install the SDK \`\`\`xml <dependency> <groupId>io.getunleash</groupId> <artifactId>unleash-client-java</artifactId> <version>LATEST</version> </dependency> \`\`\` 2\\. Run Unleash \`\`\`java UnleashConfig config = UnleashConfig.builder() .appName("unleash-onboarding-java") .instanceId("unleash-onboarding-instance") .unleashAPI("<YOUR_API_URL>") .apiKey("<YOUR_API_TOKEN>") // in production use environment variable .build(); Unleash unleash = new DefaultUnleash(config); while (true) { boolean featureEnabled = unleash.isEnabled("<YOUR_FLAG>"); System.out.println("Feature enabled: " + featureEnabled); Thread.sleep(1000); } \`\`\` --- \`\`\`java UnleashConfig config = UnleashConfig.builder() .appName("unleash-onboarding-java") .instanceId("unleash-onboarding-instance") .unleashAPI("<YOUR_API_URL>") .apiKey(System.getenv("UNLEASH_API_KEY")) .build(); \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash-client-java) - [Java SDK example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Java) - [How to Implement Feature Flags in Java](https://docs.getunleash.io/feature-flag-tutorials/java) `,".NET":`1\\. Install the SDK \`\`\`sh dotnet add package unleash.client // If you do not have a json library in your project: dotnet add package Newtonsoft.Json \`\`\` 2\\. Initialize Unleash \`\`\`csharp using Unleash; using Unleash.ClientFactory; public class Program { public static async Task Main() { var settings = new UnleashSettings() { AppName = "unleash-onboarding-dotnet", UnleashApi = new Uri("<YOUR_API_URL>"), CustomHttpHeaders = new Dictionary<string, string>() { {"Authorization","<YOUR_API_TOKEN>"} // in production use environment variable } }; var unleash = new DefaultUnleash(settings); while (true) { Console.WriteLine($"Flag is enabled: {unleash.IsEnabled("<YOUR_FLAG>")}"); await Task.Delay(1000); } } } \`\`\` --- \`\`\`csharp var settings = new UnleashSettings() { AppName = "unleash-onboarding-dotnet", UnleashApi = new Uri("<YOUR_API_URL>"), CustomHttpHeaders = new Dictionary<string, string>() { {"Authorization",Environment.GetEnvironmentVariable("UNLEASH_API_KEY")} } }; \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash-client-dotnet) - [.NET/C# SDK example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Csharp) `,PHP:`1\\. Install the SDK \`\`\`sh composer require unleash/client \`\`\` 2\\. Initialize Unleash \`\`\`php <?php use Unleash\\\\Client\\\\UnleashBuilder; require 'vendor/autoload.php'; $unleash = UnleashBuilder::create() ->withAppName('unleash-onboarding-php') ->withAppUrl('<YOUR_API_URL>') ->withHeader('Authorization', '<YOUR_API_TOKEN>') // in production use environment variable ->withInstanceId('unleash-onboarding-instance') ->build(); while (true) { echo 'Feature flag is: ' . $unleash->isEnabled('<YOUR_FLAG>') . PHP_EOL; sleep(1); } \`\`\` --- \`\`\`php $unleash = UnleashBuilder::create() ->withAppName('unleash-onboarding-php') ->withAppUrl('<YOUR_API_URL>') ->withHeader('Authorization', getenv('UNLEASH_API_TOKEN')) ->withInstanceId('unleash-onboarding-instance') ->build(); \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash-client-php) - [PHP SDK example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/PHP) `,React:`1\\. Install the SDK \`\`\`sh npm install @unleash/proxy-client-react unleash-proxy-client \`\`\` 2\\. Initialize Unleash \`\`\`jsx import { createRoot } from 'react-dom/client'; import { FlagProvider } from '@unleash/proxy-client-react'; const config = { url: '<YOUR_API_URL>', clientKey: '<YOUR_API_TOKEN>', // in production use environment variable appName: 'unleash-onboarding-react', }; const root = createRoot(document.getElementById('root')); root.render( <React.StrictMode> <FlagProvider config={config}> <App /> </FlagProvider> </React.StrictMode> ); \`\`\` 3\\. Check feature flag status \`\`\`jsx import { useFlag } from '@unleash/proxy-client-react'; const TestComponent = () => { const enabled = useFlag('<YOUR_FLAG>'); return enabled ? 'Flag is enabled' : 'Flag is disabled' }; \`\`\` --- \`\`\`jsx const config = { url: '<YOUR_API_URL>', clientKey: process.env.UNLEASH_API_TOKEN, appName: 'unleash-onboarding-react', }; \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/proxy-client-react) - [React SDK example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/React) - [https://docs.getunleash.io/feature-flag-tutorials/react](https://docs.getunleash.io/feature-flag-tutorials/react) `,Rust:`1\\. Install the SDK \`\`\`sh cargo add unleash-api-client --features async-std,reqwest,surf cargo add serde --features derive cargo add serde reqwest --features json cargo add serde tokio --features full cargo add serde anyhow cfg cfg-if enum-map@~2.0.0 surf \`\`\` 2\\. Run Unleash \`\`\`rust use enum_map::Enum; use serde::{Deserialize, Serialize}; use std::error::Error; use std::time::Duration; use tokio::time::sleep; use unleash_api_client::client::ClientBuilder; use unleash_api_client::Client; #[derive(Debug, Deserialize, Serialize, Enum, Clone)] enum Flags { #[serde(rename = "<YOUR_FLAG>")] TestFlag, } #[tokio::main] async fn main() -> Result<(), Box<dyn Error + Send + Sync>> { let client: Client<Flags, reqwest::Client> = ClientBuilder::default() .into_client( "<YOUR_API_URL>", "unleash-onboarding-rust", "unleash-onboarding-instance", Some("<YOUR_API_TOKEN>".to_owned()), // in production use environment variable )?; client.register().await?; let (_, _) = tokio::join!(client.poll_for_updates(), async { sleep(Duration::from_millis(1000)).await; let is_enabled = client.is_enabled(Flags::TestFlag, None, true); println!("\\nIs flag enabled: {}\\n", is_enabled); sleep(Duration::from_millis(5000)).await; client.stop_poll().await; Ok::<(), Box<dyn Error + Send + Sync>>(()) }); Ok(()) } \`\`\` --- \`\`\`rust let api_token = env::var("UNLEASH_API_TOKEN").expect("UNLEASH_API_TOKEN environment variable not set"); let client: Client<Flags, reqwest::Client> = ClientBuilder::default() .into_client( "<YOUR_API_URL>", "unleash-onboarding-rust", "unleash-onboarding-instance", Some(api_token.to_owned()), )?; client.register().await?; \`\`\` --- - [SDK repository with documentation](https://github.com/Unleash/unleash-client-rust) - [Rust example with CodeSandbox](https://github.com/Unleash/unleash-sdk-examples/tree/main/Rust) - [How to Implement Feature Flags in Rust](https://docs.getunleash.io/feature-flag-tutorials/rust) `,Swift:`1\\. Install the SDK \`\`\`sh // Instructions to add the Swift SDK can be found at the provided URL: https://github.com/Unleash/unleash-proxy-client-swift.git \`\`\` 2\\. Run Unleash \`\`\`swift import Foundation import UnleashProxyClientSwift var unleash = UnleashProxyClientSwift.UnleashClient( unleashUrl: "<YOUR_API_URL>", clientKey: "<YOUR_API_TOKEN>", // in production use environment variable appName: "unleash-onboarding-swift", context: [:]) unleash.start() Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in print("Is enabled", unleash.isEnabled(name: "<YOUR_FLAG>")) } \`\`\` ℹ️ **Info:** The Swift SDK takes at least 60 seconds to post metrics to Unleash. `},mn=I(`pre`)(({theme:e})=>({backgroundColor:e.palette.background.elevation1,padding:e.spacing(2),borderRadius:e.shape.borderRadius,overflow:`auto`,fontSize:e.typography.body2.fontSize,wordBreak:`break-all`,whiteSpace:`pre-wrap`,position:`relative`,maxHeight:e.spacing(34)})),hn=I(Ze)(({theme:e})=>({position:`absolute`,top:e.spacing(1),right:e.spacing(1)})),gn=({title:e,code:t})=>{let n=e=>()=>{(0,pn.default)(e),r({type:`success`,text:`Copied to clipboard`})},{setToastData:r}=E();return(0,R.jsxs)(mn,{children:[t,(0,R.jsx)(hn,{title:e,arrow:!0,children:(0,R.jsx)(be,{onClick:n(t),size:`small`,children:(0,R.jsx)(ge,{})})})]})},$=({inline:e=!1,children:t})=>!e&&typeof t?.[0]==`string`?(0,R.jsx)(gn,{code:t[0],title:`Copy code`}):(0,R.jsx)(`code`,{children:t}),_n=(e,t)=>`${e}/api${K.some(e=>e.name===t)?`/frontend`:``}/`,vn=I(`div`)(({theme:e})=>({padding:e.spacing(5,8,2,8),display:`flex`,flexDirection:`column`,gap:e.spacing(3)})),yn=I(`div`)(({theme:e})=>({display:`inline-flex`,gap:e.spacing(3),padding:e.spacing(1,2),border:`1px solid ${e.palette.divider}`,borderRadius:e.shape.borderRadius,marginBottom:e.spacing(3)})),bn=({sdk:e,apiKey:t,feature:n,onSdkChange:r})=>{let{uiConfig:i}=N(),a=Ut.find(t=>t.name===e.name)?.icon,o=_n(i.unleashUrl,e.name),[s,c,l]=(Q[e.name]||``).replace(`<YOUR_API_TOKEN>`,t).replace(`<YOUR_API_URL>`,o).replaceAll(`<YOUR_FLAG>`,n).split(`--- `);return(0,R.jsxs)(vn,{children:[(0,R.jsx)(F,{variant:`h2`,children:`Connect an SDK to Unleash`}),(0,R.jsxs)(H,{children:[(0,R.jsx)(U,{active:2,steps:3}),(0,R.jsx)(O,{color:`secondary`,children:`3/3 - Test connection`})]}),(0,R.jsxs)(k,{sx:{mt:2},children:[(0,R.jsxs)(yn,{children:[a?(0,R.jsx)(D,{variant:`circular`,src:P(a),alt:e.name}):null,(0,R.jsx)(w,{onClick:r,component:`button`,children:`Change SDK`})]}),(0,R.jsx)(V,{children:`Setup the SDK`}),(0,R.jsx)(L,{components:{code:$},children:s})]})]})},xn=I(`div`)(({theme:e})=>({padding:e.spacing(5,8,2,8),display:`flex`,flexDirection:`column`,gap:e.spacing(3),fontSize:e.typography.body2.fontSize})),Sn=({sdk:e})=>{let{uiConfig:t}=N(),n=`${t.unleashUrl}/api/`,r=`${t.unleashUrl}/api/frontend/`,i=e.type===`client`?n:r,[a,o,s]=(Q[e.name]||``).replaceAll(`<YOUR_API_URL>`,i).split(`--- `);return(0,R.jsxs)(xn,{children:[(0,R.jsx)(F,{variant:`h2`,children:`Connect an SDK to Unleash`}),(0,R.jsxs)(H,{children:[(0,R.jsx)(U,{active:2,steps:3}),(0,R.jsx)(O,{color:`secondary`,children:`3/3 - Test connection`})]}),o?.trim()?(0,R.jsxs)(k,{sx:{mt:2},children:[(0,R.jsx)(V,{children:`Production settings`}),(0,R.jsx)(F,{variant:`body2`,children:`You have successfully connected your SDK. In the previous code example, the settings were optimized for development. We recommend the following setup for production.`}),(0,R.jsx)(L,{components:{code:$},children:o})]}):null,s?.trim()?(0,R.jsxs)(k,{children:[(0,R.jsx)(V,{children:`Additional resources`}),(0,R.jsx)(F,{variant:`body2`,children:`Now that we’ve validated the connection, you might want to look into more advanced use cases and examples:`}),(0,R.jsx)(L,{components:{code:$},children:s})]}):null]})},Cn=({sdk:e,apiKey:t,feature:n,onSdkChange:r})=>(0,R.jsx)(z.Suspense,{fallback:(0,R.jsx)(pe,{}),children:n?(0,R.jsx)(bn,{sdk:e,apiKey:t,feature:n,onSdkChange:r}):(0,R.jsx)(Sn,{sdk:e})}),wn=I(`main`)(({theme:e})=>({backgroundColor:e.palette.background.paper,display:`flex`,flexDirection:`column`,flex:1})),Tn=I(m)(({theme:e})=>({"& .MuiDialog-paper":{borderRadius:e.shape.borderRadiusLarge,maxWidth:e.spacing(170),width:`100%`,backgroundColor:`transparent`},padding:0,"& .MuiPaper-root > section":{overflowX:`hidden`}})),En=I(`div`)(({theme:e})=>({borderTop:`1px solid ${e.palette.divider}}`,display:`flex`,justifyContent:`flex-end`,gap:e.spacing(4),alignItems:`center`,padding:e.spacing(3,8,3,8)})),Dn=I(`div`)(({theme:e})=>({display:`flex`,justifyContent:`flex-end`,gap:e.spacing(4),alignItems:`center`,padding:e.spacing(3,8,3,8)})),On=({onClose:e,onFinish:t,environments:n,project:r,feature:i})=>{let a=_(l().breakpoints.up(`lg`)),[o,s]=(0,z.useState)(null),[c,d]=(0,z.useState)(null),[f,p]=(0,z.useState)(null),[m,h]=(0,z.useState)(`select-sdk`),{project:g}=u(r,{refreshInterval:1e3}),v=m===`select-sdk`,y=m===`generate-api-key`&&o&&c,b=m===`test-connection`&&o&&c&&f,x=g.onboardingStatus.status===`onboarded`;return(0,z.useEffect)(()=>{n.length>0&&d(n[0])},[JSON.stringify(n)]),(0,R.jsx)(Tn,{open:!0,onClose:e,children:(0,R.jsxs)(k,{sx:{display:`flex`},children:[(0,R.jsxs)(wn,{children:[v?(0,R.jsx)(Yt,{onSelect:e=>{s(e),h(`generate-api-key`)}}):null,y?(0,R.jsx)(Vt,{environments:n,environment:c,project:r,sdkType:o.type,onEnvSelect:d,onApiKey:p}):null,b?(0,R.jsx)(Cn,{apiKey:f,sdk:o,feature:i,onSdkChange:()=>{h(`select-sdk`)}}):null,m===`generate-api-key`?(0,R.jsx)(En,{children:(0,R.jsxs)(Dn,{children:[(0,R.jsx)(A,{variant:`text`,color:`inherit`,onClick:()=>{h(`select-sdk`)},children:`Back`}),(0,R.jsx)(A,{variant:`contained`,disabled:!f,onClick:()=>{h(`test-connection`)},children:`Next`})]})}):null,b?(0,R.jsx)(En,{children:(0,R.jsxs)(Dn,{children:[x?null:(0,R.jsx)(A,{variant:`text`,color:`inherit`,onClick:()=>{h(`generate-api-key`)},children:`Back`}),(0,R.jsx)(A,{variant:`contained`,onClick:()=>{t(o.name)},children:`Complete`})]})}):null]}),a&&v?(0,R.jsx)(tn,{}):null,a&&y?(0,R.jsx)(en,{}):null,a&&b?(0,R.jsx)(fn,{projectId:r,sdk:o.name,environment:c}):null]})})},kn=({open:e,...t})=>e?(0,R.jsx)(On,{...t}):null,An=({isStale:e,isOpen:t,projectId:n,featureId:i,onClose:a})=>{let{setToastData:o,setToastApiError:s}=E(),{patchFeatureToggle:c}=g(),l=(0,R.jsx)(F,{children:`Setting a flag to stale marks it for cleanup`}),u=(0,R.jsx)(F,{children:`Setting a flag to active marks it as in active use`}),d=e?`active`:`stale`;return(0,R.jsx)(se,{open:t,secondaryButtonText:`Cancel`,primaryButtonText:`Flip to ${d}`,title:`Set feature state to ${d}`,onClick:async t=>{t.stopPropagation();try{await c(n,i,[{op:`replace`,path:`/stale`,value:!e}]),a()}catch(e){s(C(e))}o(e?{type:`success`,text:`The flag is no longer marked as stale`}:{type:`success`,text:`The flag has been marked as stale`})},onClose:a,children:(0,R.jsx)(r,{condition:e,show:u,elseShow:l})})};export{ot as _,Q as a,rt as b,Ut as c,Ot as d,wt as f,st as g,pt as h,$ as i,jt as l,bt as m,kn as n,an as o,Ct as p,_n as r,nn as s,An as t,kt as u,at as v,tt as x,it as y};