[{"data":1,"prerenderedAt":4394},["ShallowReactive",2],{"navigation_docs":3,"-sdks-server-go":239,"-sdks-server-go-surround":4388},[4,51,73,224],{"title":5,"icon":6,"modifiedAt":7,"path":8,"stem":9,"children":10,"page":6},"Getting Started",false,"2026-04-30T19:15:33.000Z","\u002Fgetting-started","1.getting-started",[11,16,21,26,31,36,41,46],{"title":12,"path":13,"stem":14,"icon":15},"Quickstart","\u002Fgetting-started\u002Fquickstart","1.getting-started\u002F1.quickstart","i-lucide-fast-forward",{"title":17,"path":18,"stem":19,"icon":20},"Core Concepts","\u002Fgetting-started\u002Fcore-concepts","1.getting-started\u002F2.core-concepts","i-lucide-workflow",{"title":22,"path":23,"stem":24,"icon":25},"Configs Anatomy","\u002Fgetting-started\u002Fconfigs","1.getting-started\u002F3.configs","i-lucide-file-cog",{"title":27,"path":28,"stem":29,"icon":30},"Architecture","\u002Fgetting-started\u002Farchitecture","1.getting-started\u002F4.architecture","i-lucide-layers",{"title":32,"path":33,"stem":34,"icon":35},"JSON Schema Validation","\u002Fgetting-started\u002Fjson-schema-validation","1.getting-started\u002F5.json-schema-validation","i-lucide-braces",{"title":37,"path":38,"stem":39,"icon":40},"Testing Rules","\u002Fgetting-started\u002Ftesting-targeting-rules","1.getting-started\u002F6.testing-targeting-rules","i-lucide-flask-conical",{"title":42,"path":43,"stem":44,"icon":45},"Segments","\u002Fgetting-started\u002Fsegments","1.getting-started\u002F7.segments","i-lucide-squares-intersect",{"title":47,"path":48,"stem":49,"icon":50},"Pricing & Billing","\u002Fgetting-started\u002Fpricing-and-billing","1.getting-started\u002F8.pricing-and-billing","i-lucide-credit-card",{"title":52,"modifiedAt":53,"defaultOpen":54,"path":55,"stem":56,"children":57,"page":6},"Monitoring","2026-09-24T23:33:20.000Z",true,"\u002Fmonitoring","2.monitoring",[58,63,68],{"title":59,"path":60,"stem":61,"icon":62},"Activity","\u002Fmonitoring\u002Factivity","2.monitoring\u002F1.activity","i-lucide-activity",{"title":64,"path":65,"stem":66,"icon":67},"Lifecycle","\u002Fmonitoring\u002Flifecycle","2.monitoring\u002F2.lifecycle","i-lucide-recycle",{"title":69,"path":70,"stem":71,"icon":72},"Alerts","\u002Fmonitoring\u002Falerts","2.monitoring\u002F3.alerts","i-lucide-bell",{"title":74,"modifiedAt":53,"defaultOpen":54,"path":75,"stem":76,"children":77,"page":6},"SDKs","\u002Fsdks","3.sdks",[78,83,104,129,145,176,219],{"title":79,"path":80,"stem":81,"icon":82},"Available SDKs","\u002Fsdks\u002Favailable-sdks","3.sdks\u002F1.available-sdks","i-lucide-list",{"title":84,"modifiedAt":53,"icon":85,"defaultOpen":6,"path":86,"stem":87,"children":88,"page":6},"Browser SDKs","i-lucide-monitor","\u002Fsdks\u002Fbrowser","3.sdks\u002F2.browser",[89,94,99],{"title":90,"path":91,"stem":92,"icon":93},"Javascript","\u002Fsdks\u002Fbrowser\u002Fjavascript","3.sdks\u002F2.browser\u002F1.javascript","cd-logos-javascript",{"title":95,"path":96,"stem":97,"icon":98},"React","\u002Fsdks\u002Fbrowser\u002Freact","3.sdks\u002F2.browser\u002F2.react","cd-logos-react-light",{"title":100,"path":101,"stem":102,"icon":103},"Vue","\u002Fsdks\u002Fbrowser\u002Fvue","3.sdks\u002F2.browser\u002F3.vue","cd-logos-vue",{"title":105,"modifiedAt":53,"icon":106,"defaultOpen":6,"path":107,"stem":108,"children":109,"page":6},"Mobile SDKs","i-lucide-smartphone","\u002Fsdks\u002Fmobile","3.sdks\u002F3.mobile",[110,114,119,124],{"title":111,"path":112,"stem":113,"icon":98},"React Native","\u002Fsdks\u002Fmobile\u002Freact-native","3.sdks\u002F3.mobile\u002F1.react-native",{"title":115,"path":116,"stem":117,"icon":118},"Flutter","\u002Fsdks\u002Fmobile\u002Fflutter","3.sdks\u002F3.mobile\u002F2.flutter","cd-logos-flutter",{"title":120,"path":121,"stem":122,"icon":123},"Swift","\u002Fsdks\u002Fmobile\u002Fswift","3.sdks\u002F3.mobile\u002F3.swift","cd-logos-swift",{"title":125,"path":126,"stem":127,"icon":128},"Android","\u002Fsdks\u002Fmobile\u002Fandroid","3.sdks\u002F3.mobile\u002F4.android","cd-logos-android",{"title":130,"modifiedAt":53,"icon":131,"defaultOpen":6,"path":132,"stem":133,"children":134,"page":6},"Meta-framework SDKs","i-lucide-monitor-smartphone","\u002Fsdks\u002Fmeta","3.sdks\u002F4.meta",[135,140],{"title":136,"path":137,"stem":138,"icon":139},"Next.js","\u002Fsdks\u002Fmeta\u002Fnextjs","3.sdks\u002F4.meta\u002F1.nextjs","cd-logos-nextjs",{"title":141,"path":142,"stem":143,"icon":144},"Nuxt","\u002Fsdks\u002Fmeta\u002Fnuxt","3.sdks\u002F4.meta\u002F2.nuxt","cd-logos-nuxt",{"title":146,"modifiedAt":53,"icon":147,"defaultOpen":6,"path":148,"stem":149,"children":150,"page":6},"Server SDKs","i-lucide-server","\u002Fsdks\u002Fserver","3.sdks\u002F5.server",[151,156,161,166,171],{"title":152,"path":153,"stem":154,"icon":155},"Node.js","\u002Fsdks\u002Fserver\u002Fnode-js","3.sdks\u002F5.server\u002F1.node-js","cd-logos-nodejs",{"title":157,"path":158,"stem":159,"icon":160},"Python","\u002Fsdks\u002Fserver\u002Fpython","3.sdks\u002F5.server\u002F2.python","cd-logos-python",{"title":162,"path":163,"stem":164,"icon":165},"Java","\u002Fsdks\u002Fserver\u002Fjava","3.sdks\u002F5.server\u002F3.java","cd-logos-java",{"title":167,"path":168,"stem":169,"icon":170},".NET","\u002Fsdks\u002Fserver\u002Fdotnet","3.sdks\u002F5.server\u002F4.dotnet","cd-logos-dotnet",{"title":172,"path":173,"stem":174,"icon":175},"Go","\u002Fsdks\u002Fserver\u002Fgo","3.sdks\u002F5.server\u002F5.go","cd-logos-go",{"title":177,"modifiedAt":53,"icon":178,"defaultOpen":6,"path":179,"stem":180,"children":181,"page":6},"OpenFeature Providers","cd-logos-openfeature","\u002Fsdks\u002Fopenfeature","3.sdks\u002F6.openfeature",[182,187,191,195,199,203,207,211,215],{"title":183,"path":184,"stem":185,"icon":178,"icons":186},"Web","\u002Fsdks\u002Fopenfeature\u002Fweb","3.sdks\u002F6.openfeature\u002F1.web",[178,93],{"title":120,"path":188,"stem":189,"icon":178,"icons":190},"\u002Fsdks\u002Fopenfeature\u002Fswift","3.sdks\u002F6.openfeature\u002F2.swift",[178,123],{"title":125,"path":192,"stem":193,"icon":178,"icons":194},"\u002Fsdks\u002Fopenfeature\u002Fandroid","3.sdks\u002F6.openfeature\u002F3.android",[178,128],{"title":115,"path":196,"stem":197,"icon":178,"icons":198},"\u002Fsdks\u002Fopenfeature\u002Fflutter","3.sdks\u002F6.openfeature\u002F4.flutter",[178,118],{"title":152,"path":200,"stem":201,"icon":178,"icons":202},"\u002Fsdks\u002Fopenfeature\u002Fnode-js","3.sdks\u002F6.openfeature\u002F5.node-js",[178,155],{"title":157,"path":204,"stem":205,"icon":178,"icons":206},"\u002Fsdks\u002Fopenfeature\u002Fpython","3.sdks\u002F6.openfeature\u002F6.python",[178,160],{"title":162,"path":208,"stem":209,"icon":178,"icons":210},"\u002Fsdks\u002Fopenfeature\u002Fjava","3.sdks\u002F6.openfeature\u002F7.java",[178,165],{"title":167,"path":212,"stem":213,"icon":178,"icons":214},"\u002Fsdks\u002Fopenfeature\u002Fdotnet","3.sdks\u002F6.openfeature\u002F8.dotnet",[178,170],{"title":172,"path":216,"stem":217,"icon":178,"icons":218},"\u002Fsdks\u002Fopenfeature\u002Fgo","3.sdks\u002F6.openfeature\u002F9.go",[178,175],{"title":220,"path":221,"stem":222,"icon":223},"Maven Repository","\u002Fsdks\u002Fmaven-repository","3.sdks\u002F7.maven-repository","i-lucide-package",{"title":225,"modifiedAt":53,"defaultOpen":54,"path":226,"stem":227,"children":228,"page":6},"Integrations","\u002Fintegrations","4.integrations",[229,234],{"title":230,"path":231,"stem":232,"icon":233},"Terraform","\u002Fintegrations\u002Fterraform","4.integrations\u002F1.terraform","cd-logos-terraform",{"title":235,"path":236,"stem":237,"icon":238},"MCP","\u002Fintegrations\u002Fmcp","4.integrations\u002F2.mcp","cd-logos-mcp",{"id":240,"title":241,"body":242,"description":4380,"extension":4381,"links":4382,"meta":4383,"navigation":4385,"path":173,"seo":4386,"stem":174,"__hash__":4387},"docs\u002F3.sdks\u002F5.server\u002F5.go.md","Go Server SDK",{"type":243,"value":244,"toc":4354},"minimark",[245,249,254,259,263,271,274,278,291,318,322,336,627,633,637,658,663,678,685,691,788,794,809,819,978,981,987,996,1099,1244,1250,1253,1256,1263,1299,1409,1413,1419,1511,1654,1657,1814,1824,1836,1840,1843,2209,2212,2239,2242,2277,2302,2390,2399,2403,2416,2538,2547,2551,2554,2564,2567,2714,2717,2792,2797,2858,3001,3005,3008,3035,3038,3193,3199,3345,3348,3352,3355,3360,3369,3375,3378,3384,3404,3410,3416,3426,3431,3440,3443,3446,3450,3462,3777,3805,3809,3857,3894,3898,4009,4242,4245,4281,4285,4350],[246,247],"div",{"hidden":54,"dataTitleIcon":248},"go",[250,251],"external-badges",{"package-url":252,"registry":248,"repo":253},"https:\u002F\u002Fpkg.go.dev\u002Fgithub.com\u002FConfigDirector\u002Fgo-sdks\u002Fconfigdirector","go-sdks\u002Ftree\u002Fmain\u002Fconfigdirector",[255,256,258],"h2",{"id":257},"introduction","Introduction",[260,261,262],"p",{},"The Go Server SDK is a server-side SDK that evaluates configs and their targeting rules. Upon initialization, it retrieves configs and targeting rules from ConfigDirector services. From that point on, it evaluates configs locally for any user context, and receives updates via server sent events (SSE) when configs are updated on the dashboard or via the admin API.",[260,264,265,266,270],{},"The minimum Go version supported is ",[267,268,269],"code",{},"1.26",".",[260,272,273],{},"The client is safe to share between goroutines. Create one when your application starts, share it for the lifetime of the process, and close it on shutdown. Evaluations read config state the client already holds in memory, so they make no network calls on the request path.",[255,275,277],{"id":276},"installation","Installation",[260,279,280,281,284,285],{},"The SDK can be installed with ",[267,282,283],{},"go get",". Its package reference is on pkg.go.dev: ",[286,287,252],"a",{"href":252,"rel":288,"target":290},[289],"noopener","_blank",[292,293,294],"code-group",{},[295,296,301],"pre",{"className":297,"code":298,"filename":283,"language":299,"meta":300,"style":300},"language-bash shiki shiki-themes material-theme-lighter github-light github-dark","go get github.com\u002FConfigDirector\u002Fgo-sdks\u002Fconfigdirector@latest\n","bash","",[267,302,303],{"__ignoreMap":300},[304,305,308,311,315],"span",{"class":306,"line":307},"line",1,[304,309,248],{"class":310},"sbgvK",[304,312,314],{"class":313},"s_sjI"," get",[304,316,317],{"class":313}," github.com\u002FConfigDirector\u002Fgo-sdks\u002Fconfigdirector@latest\n",[255,319,321],{"id":320},"configure-and-initialize-the-client","Configure and initialize the client",[323,324,325,333],"ol",{},[326,327,328,329,332],"li",{},"Create a client providing your server SDK key. You can retrieve a server SDK key for each environment under ",[267,330,331],{},"SDK Keys"," in the dashboard's navigation panel. The client starts connecting as soon as it is created.",[326,334,335],{},"Wait for the client to be ready before serving requests.",[295,337,341],{"className":338,"code":339,"filename":340,"language":248,"meta":300,"style":300},"language-go shiki shiki-themes material-theme-lighter github-light github-dark","import (\n    \"context\"\n    \"log\"\n\n    \"github.com\u002FConfigDirector\u002Fgo-sdks\u002Fconfigdirector\"\n)\n\n\u002F\u002F IMPORTANT: Do not commit the server SDK key to your source code, it is a secret value.\n\u002F\u002F Creating the client starts its connection in the background.\nclient, err := configdirector.NewClient(configdirector.Options{SDKKey: \"YOUR-SERVER-SDK-KEY\"})\nif err != nil {\n    log.Fatal(err)\n}\ndefer client.Close()\n\n\u002F\u002F Blocks until the first config state arrives or the connection timeout elapses.\n\u002F\u002F If the wait times out, the client keeps connecting in the background and\n\u002F\u002F reads return their in-code default value until config state arrives.\nif err := client.WaitForReady(context.Background()); err != nil {\n    log.Printf(\"ConfigDirector is not ready yet: %v\", err)\n}\n","main.go",[267,342,343,353,366,376,382,392,398,403,410,416,474,492,510,516,533,538,544,550,556,592,622],{"__ignoreMap":300},[304,344,345,349],{"class":306,"line":307},[304,346,348],{"class":347},"sVHd0","import",[304,350,352],{"class":351},"sP7_E"," (\n",[304,354,356,360,363],{"class":306,"line":355},2,[304,357,359],{"class":358},"sjJ54","    \"",[304,361,362],{"class":310},"context",[304,364,365],{"class":358},"\"\n",[304,367,369,371,374],{"class":306,"line":368},3,[304,370,359],{"class":358},[304,372,373],{"class":310},"log",[304,375,365],{"class":358},[304,377,379],{"class":306,"line":378},4,[304,380,381],{"emptyLinePlaceholder":54},"\n",[304,383,385,387,390],{"class":306,"line":384},5,[304,386,359],{"class":358},[304,388,389],{"class":310},"github.com\u002FConfigDirector\u002Fgo-sdks\u002Fconfigdirector",[304,391,365],{"class":358},[304,393,395],{"class":306,"line":394},6,[304,396,397],{"class":351},")\n",[304,399,401],{"class":306,"line":400},7,[304,402,381],{"emptyLinePlaceholder":54},[304,404,406],{"class":306,"line":405},8,[304,407,409],{"class":408},"sutJx","\u002F\u002F IMPORTANT: Do not commit the server SDK key to your source code, it is a secret value.\n",[304,411,413],{"class":306,"line":412},9,[304,414,415],{"class":408},"\u002F\u002F Creating the client starts its connection in the background.\n",[304,417,419,423,426,429,433,436,438,442,445,448,450,453,456,459,462,465,468,471],{"class":306,"line":418},10,[304,420,422],{"class":421},"su5hD","client",[304,424,425],{"class":351},",",[304,427,428],{"class":421}," err ",[304,430,432],{"class":431},"smGrS",":=",[304,434,435],{"class":421}," configdirector",[304,437,270],{"class":351},[304,439,441],{"class":440},"sGLFI","NewClient",[304,443,444],{"class":351},"(",[304,446,447],{"class":310},"configdirector",[304,449,270],{"class":351},[304,451,452],{"class":310},"Options",[304,454,455],{"class":351},"{",[304,457,458],{"class":421},"SDKKey",[304,460,461],{"class":351},":",[304,463,464],{"class":358}," \"",[304,466,467],{"class":313},"YOUR-SERVER-SDK-KEY",[304,469,470],{"class":358},"\"",[304,472,473],{"class":351},"})\n",[304,475,477,480,482,485,489],{"class":306,"line":476},11,[304,478,479],{"class":347},"if",[304,481,428],{"class":421},[304,483,484],{"class":431},"!=",[304,486,488],{"class":487},"s39Yj"," nil",[304,490,491],{"class":351}," {\n",[304,493,495,498,500,503,505,508],{"class":306,"line":494},12,[304,496,497],{"class":421},"    log",[304,499,270],{"class":351},[304,501,502],{"class":440},"Fatal",[304,504,444],{"class":351},[304,506,507],{"class":421},"err",[304,509,397],{"class":351},[304,511,513],{"class":306,"line":512},13,[304,514,515],{"class":351},"}\n",[304,517,519,522,525,527,530],{"class":306,"line":518},14,[304,520,521],{"class":347},"defer",[304,523,524],{"class":421}," client",[304,526,270],{"class":351},[304,528,529],{"class":440},"Close",[304,531,532],{"class":351},"()\n",[304,534,536],{"class":306,"line":535},15,[304,537,381],{"emptyLinePlaceholder":54},[304,539,541],{"class":306,"line":540},16,[304,542,543],{"class":408},"\u002F\u002F Blocks until the first config state arrives or the connection timeout elapses.\n",[304,545,547],{"class":306,"line":546},17,[304,548,549],{"class":408},"\u002F\u002F If the wait times out, the client keeps connecting in the background and\n",[304,551,553],{"class":306,"line":552},18,[304,554,555],{"class":408},"\u002F\u002F reads return their in-code default value until config state arrives.\n",[304,557,559,561,563,565,567,569,572,574,576,578,581,584,586,588,590],{"class":306,"line":558},19,[304,560,479],{"class":347},[304,562,428],{"class":421},[304,564,432],{"class":431},[304,566,524],{"class":421},[304,568,270],{"class":351},[304,570,571],{"class":440},"WaitForReady",[304,573,444],{"class":351},[304,575,362],{"class":421},[304,577,270],{"class":351},[304,579,580],{"class":440},"Background",[304,582,583],{"class":351},"());",[304,585,428],{"class":421},[304,587,484],{"class":431},[304,589,488],{"class":487},[304,591,491],{"class":351},[304,593,595,597,599,602,604,606,609,613,615,617,620],{"class":306,"line":594},20,[304,596,497],{"class":421},[304,598,270],{"class":351},[304,600,601],{"class":440},"Printf",[304,603,444],{"class":351},[304,605,470],{"class":358},[304,607,608],{"class":313},"ConfigDirector is not ready yet: ",[304,610,612],{"class":611},"swQdS","%v",[304,614,470],{"class":358},[304,616,425],{"class":351},[304,618,619],{"class":421}," err",[304,621,397],{"class":351},[304,623,625],{"class":306,"line":624},21,[304,626,515],{"class":351},[260,628,629,630,632],{},"The client is safe to share between goroutines. Create one at startup, share it for the lifetime of the process, and call ",[267,631,529],{}," on shutdown.",[634,635,636],"warning",{},"Server SDK keys are secret values. Do not commit them to your source code repository. Provide them at runtime via environment variables instead.",[260,638,639,641,642,645,646,649,650,653,654,657],{},[267,640,571],{}," blocks until the first config state arrives, the connection timeout elapses, or the context you pass ends, whichever comes first. It returns ",[267,643,644],{},"nil"," once the client is ready, ",[267,647,648],{},"context.DeadlineExceeded"," when the connection timeout elapsed first, the context's own error when it ended first, and ",[267,651,652],{},"ErrClientClosed"," when the client was closed. An error only says that the wait ended: the client keeps connecting in the background and becomes ready when config state arrives. Until then, every read returns its in-code default value. ",[267,655,656],{},"Ready"," reports whether config state has arrived.",[659,660,662],"h3",{"id":661},"additional-configuration-options","Additional configuration options",[260,664,665,666,668,669,671,672,674,675,677],{},"Every option is a field of the ",[267,667,452],{}," struct passed to ",[267,670,441],{},". Only ",[267,673,458],{}," is required; a zero value in any other field means the default described below. An option the client cannot use, such as a blank SDK key, an unknown connection mode, or a relative URL, is returned as an error from ",[267,676,441],{}," rather than as a client that quietly never updates.",[679,680,682],"h4",{"id":681},"metadata",[267,683,684],{},"Metadata",[260,686,687,688,690],{},"The ",[267,689,684],{}," option allows you to provide your application's name and version. These values can be used in targeting rules conditionals. For example, if a certain feature should only be enabled starting with a certain version of your application.",[295,692,694],{"className":338,"code":693,"filename":340,"language":248,"meta":300,"style":300},"client, err := configdirector.NewClient(configdirector.Options{\n    SDKKey:   \"YOUR-SERVER-SDK-KEY\",\n    Metadata: configdirector.Metadata{AppName: \"YOUR-APP-NAME\", AppVersion: \"1.0.2\"},\n})\n",[267,695,696,723,740,784],{"__ignoreMap":300},[304,697,698,700,702,704,706,708,710,712,714,716,718,720],{"class":306,"line":307},[304,699,422],{"class":421},[304,701,425],{"class":351},[304,703,428],{"class":421},[304,705,432],{"class":431},[304,707,435],{"class":421},[304,709,270],{"class":351},[304,711,441],{"class":440},[304,713,444],{"class":351},[304,715,447],{"class":310},[304,717,270],{"class":351},[304,719,452],{"class":310},[304,721,722],{"class":351},"{\n",[304,724,725,728,730,733,735,737],{"class":306,"line":355},[304,726,727],{"class":421},"    SDKKey",[304,729,461],{"class":351},[304,731,732],{"class":358},"   \"",[304,734,467],{"class":313},[304,736,470],{"class":358},[304,738,739],{"class":351},",\n",[304,741,742,745,747,749,751,753,755,758,760,762,765,767,769,772,774,776,779,781],{"class":306,"line":368},[304,743,744],{"class":421},"    Metadata",[304,746,461],{"class":351},[304,748,435],{"class":310},[304,750,270],{"class":351},[304,752,684],{"class":310},[304,754,455],{"class":351},[304,756,757],{"class":421},"AppName",[304,759,461],{"class":351},[304,761,464],{"class":358},[304,763,764],{"class":313},"YOUR-APP-NAME",[304,766,470],{"class":358},[304,768,425],{"class":351},[304,770,771],{"class":421}," AppVersion",[304,773,461],{"class":351},[304,775,464],{"class":358},[304,777,778],{"class":313},"1.0.2",[304,780,470],{"class":358},[304,782,783],{"class":351},"},\n",[304,785,786],{"class":306,"line":378},[304,787,473],{"class":351},[679,789,791],{"id":790},"logger",[267,792,793],{},"Logger",[260,795,796,797,800,801,804,805,808],{},"The SDK logs through the standard library's ",[267,798,799],{},"log\u002Fslog"," package. By default it writes to ",[267,802,803],{},"slog.Default()",", so it follows whatever handler and level your application configures. Every record carries the attribute ",[267,806,807],{},"component=configdirector",", which makes the SDK's records easy to tell apart from your application's own.",[260,810,811,812,815,816,818],{},"Pass a ",[267,813,814],{},"*slog.Logger"," in the ",[267,817,793],{}," option to send the SDK's records somewhere else, or to give them their own level:",[295,820,822],{"className":338,"code":821,"filename":340,"language":248,"meta":300,"style":300},"import (\n    \"log\u002Fslog\"\n    \"os\"\n)\n\nlogger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelDebug}))\n\nclient, err := configdirector.NewClient(configdirector.Options{\n    SDKKey: \"YOUR-SERVER-SDK-KEY\",\n    Logger: logger,\n})\n",[267,823,824,830,838,847,851,855,918,922,948,962,974],{"__ignoreMap":300},[304,825,826,828],{"class":306,"line":307},[304,827,348],{"class":347},[304,829,352],{"class":351},[304,831,832,834,836],{"class":306,"line":355},[304,833,359],{"class":358},[304,835,799],{"class":310},[304,837,365],{"class":358},[304,839,840,842,845],{"class":306,"line":368},[304,841,359],{"class":358},[304,843,844],{"class":310},"os",[304,846,365],{"class":358},[304,848,849],{"class":306,"line":378},[304,850,397],{"class":351},[304,852,853],{"class":306,"line":384},[304,854,381],{"emptyLinePlaceholder":54},[304,856,857,860,862,865,867,870,872,875,877,880,882,884,886,889,891,894,896,898,901,903,906,908,910,912,915],{"class":306,"line":394},[304,858,859],{"class":421},"logger ",[304,861,432],{"class":431},[304,863,864],{"class":421}," slog",[304,866,270],{"class":351},[304,868,869],{"class":440},"New",[304,871,444],{"class":351},[304,873,874],{"class":421},"slog",[304,876,270],{"class":351},[304,878,879],{"class":440},"NewTextHandler",[304,881,444],{"class":351},[304,883,844],{"class":421},[304,885,270],{"class":351},[304,887,888],{"class":421},"Stderr",[304,890,425],{"class":351},[304,892,893],{"class":431}," &",[304,895,874],{"class":310},[304,897,270],{"class":351},[304,899,900],{"class":310},"HandlerOptions",[304,902,455],{"class":351},[304,904,905],{"class":421},"Level",[304,907,461],{"class":351},[304,909,864],{"class":421},[304,911,270],{"class":351},[304,913,914],{"class":421},"LevelDebug",[304,916,917],{"class":351},"}))\n",[304,919,920],{"class":306,"line":400},[304,921,381],{"emptyLinePlaceholder":54},[304,923,924,926,928,930,932,934,936,938,940,942,944,946],{"class":306,"line":405},[304,925,422],{"class":421},[304,927,425],{"class":351},[304,929,428],{"class":421},[304,931,432],{"class":431},[304,933,435],{"class":421},[304,935,270],{"class":351},[304,937,441],{"class":440},[304,939,444],{"class":351},[304,941,447],{"class":310},[304,943,270],{"class":351},[304,945,452],{"class":310},[304,947,722],{"class":351},[304,949,950,952,954,956,958,960],{"class":306,"line":412},[304,951,727],{"class":421},[304,953,461],{"class":351},[304,955,464],{"class":358},[304,957,467],{"class":313},[304,959,470],{"class":358},[304,961,739],{"class":351},[304,963,964,967,969,972],{"class":306,"line":418},[304,965,966],{"class":421},"    Logger",[304,968,461],{"class":351},[304,970,971],{"class":421}," logger",[304,973,739],{"class":351},[304,975,976],{"class":306,"line":476},[304,977,473],{"class":351},[260,979,980],{},"At the debug level the SDK logs every config update it receives.",[679,982,984],{"id":983},"connection",[267,985,986],{},"Connection",[260,988,687,989,991,992,995],{},[267,990,986],{}," option takes a ",[267,993,994],{},"ConnectionOptions"," struct with the following optional fields:",[997,998,999,1022,1043,1068,1081],"ul",{},[326,1000,1001,1004],{},[267,1002,1003],{},"Mode",[997,1005,1006,1017],{},[326,1007,1008,1009,1012,1013,1016],{},"The connection mode, which can be either ",[267,1010,1011],{},"ConnectionModeStreaming"," or ",[267,1014,1015],{},"ConnectionModePolling",". It is recommended to use the default of streaming unless you have a specific need to use polling instead.",[326,1018,1019,1020],{},"Defaults to ",[267,1021,1011],{},[326,1023,1024,1027],{},[267,1025,1026],{},"PollingInterval",[997,1028,1029,1040],{},[326,1030,1031,1032,1035,1036,270],{},"Only used in polling mode. How long the client waits between polls of ConfigDirector services for updates, as a ",[267,1033,1034],{},"time.Duration",". A value below the minimum of 1 minute is raised to 1 minute and a warning is logged. See ",[286,1037,1039],{"href":1038},"\u002Fgetting-started\u002Farchitecture#polling-intervals","Polling intervals",[326,1041,1042],{},"Defaults to 5 minutes",[326,1044,1045,1048],{},[267,1046,1047],{},"Timeout",[997,1049,1050,1065],{},[326,1051,1052,1053,1055,1056,1058,1059,1061,1062,1064],{},"How long ",[267,1054,571],{}," waits for the first config state, as a ",[267,1057,1034],{},". If the timeout is reached, ",[267,1060,571],{}," returns ",[267,1063,648],{}," but the client is still not ready and returns in-code default values. The client will continue to attempt to connect and retrieve config values in the background.",[326,1066,1067],{},"Defaults to 3 seconds",[326,1069,1070,1073],{},[267,1071,1072],{},"URL",[997,1074,1075,1078],{},[326,1076,1077],{},"The base URL used to connect to ConfigDirector services",[326,1079,1080],{},"This should only be provided if your environment requires you to configure a proxy server in order to connect to ConfigDirector services",[326,1082,1083,1086],{},[267,1084,1085],{},"HTTPClient",[997,1087,1088,1094],{},[326,1089,687,1090,1093],{},[267,1091,1092],{},"*http.Client"," that sends every request. Provide one to control transport settings such as a corporate proxy or custom TLS.",[326,1095,1019,1096],{},[267,1097,1098],{},"http.DefaultClient",[295,1100,1102],{"className":338,"code":1101,"filename":340,"language":248,"meta":300,"style":300},"import \"time\"\n\nclient, err := configdirector.NewClient(configdirector.Options{\n    SDKKey: \"YOUR-SERVER-SDK-KEY\",\n    Connection: configdirector.ConnectionOptions{\n        Mode:            configdirector.ConnectionModePolling,\n        PollingInterval: 10 * time.Minute,\n        Timeout:         5 * time.Second,\n    },\n})\n",[267,1103,1104,1115,1119,1145,1159,1174,1190,1214,1235,1240],{"__ignoreMap":300},[304,1105,1106,1108,1110,1113],{"class":306,"line":307},[304,1107,348],{"class":347},[304,1109,464],{"class":358},[304,1111,1112],{"class":310},"time",[304,1114,365],{"class":358},[304,1116,1117],{"class":306,"line":355},[304,1118,381],{"emptyLinePlaceholder":54},[304,1120,1121,1123,1125,1127,1129,1131,1133,1135,1137,1139,1141,1143],{"class":306,"line":368},[304,1122,422],{"class":421},[304,1124,425],{"class":351},[304,1126,428],{"class":421},[304,1128,432],{"class":431},[304,1130,435],{"class":421},[304,1132,270],{"class":351},[304,1134,441],{"class":440},[304,1136,444],{"class":351},[304,1138,447],{"class":310},[304,1140,270],{"class":351},[304,1142,452],{"class":310},[304,1144,722],{"class":351},[304,1146,1147,1149,1151,1153,1155,1157],{"class":306,"line":378},[304,1148,727],{"class":421},[304,1150,461],{"class":351},[304,1152,464],{"class":358},[304,1154,467],{"class":313},[304,1156,470],{"class":358},[304,1158,739],{"class":351},[304,1160,1161,1164,1166,1168,1170,1172],{"class":306,"line":384},[304,1162,1163],{"class":421},"    Connection",[304,1165,461],{"class":351},[304,1167,435],{"class":310},[304,1169,270],{"class":351},[304,1171,994],{"class":310},[304,1173,722],{"class":351},[304,1175,1176,1179,1181,1184,1186,1188],{"class":306,"line":394},[304,1177,1178],{"class":421},"        Mode",[304,1180,461],{"class":351},[304,1182,1183],{"class":421},"            configdirector",[304,1185,270],{"class":351},[304,1187,1015],{"class":421},[304,1189,739],{"class":351},[304,1191,1192,1195,1197,1201,1204,1207,1209,1212],{"class":306,"line":400},[304,1193,1194],{"class":421},"        PollingInterval",[304,1196,461],{"class":351},[304,1198,1200],{"class":1199},"srdBf"," 10",[304,1202,1203],{"class":431}," *",[304,1205,1206],{"class":421}," time",[304,1208,270],{"class":351},[304,1210,1211],{"class":421},"Minute",[304,1213,739],{"class":351},[304,1215,1216,1219,1221,1224,1226,1228,1230,1233],{"class":306,"line":405},[304,1217,1218],{"class":421},"        Timeout",[304,1220,461],{"class":351},[304,1222,1223],{"class":1199},"         5",[304,1225,1203],{"class":431},[304,1227,1206],{"class":421},[304,1229,270],{"class":351},[304,1231,1232],{"class":421},"Second",[304,1234,739],{"class":351},[304,1236,1237],{"class":306,"line":412},[304,1238,1239],{"class":351},"    },\n",[304,1241,1242],{"class":306,"line":418},[304,1243,473],{"class":351},[679,1245,1247],{"id":1246},"telemetry",[267,1248,1249],{},"Telemetry",[260,1251,1252],{},"Telemetry tuning. It is unlikely these settings need to be adjusted. However, in cases where your application has a large number of evaluations per second, you can adjust these settings to tune the memory footprint and frequency of telemetry requests.",[260,1254,1255],{},"Keep in mind that ConfigDirector relies on these telemetry events to provide insights and features related to the configs being used.",[260,1257,687,1258,991,1260,995],{},[267,1259,1249],{},[267,1261,1262],{},"TelemetryOptions",[997,1264,1265,1281],{},[326,1266,1267,1270],{},[267,1268,1269],{},"EventQueueLimit",[997,1271,1272,1275,1278],{},[326,1273,1274],{},"The size limit of telemetry event queues. If the size limit is reached before the events are flushed to the network, older events will be dropped.",[326,1276,1277],{},"ConfigDirector keeps a count of dropped events. If the number of dropped events is higher than 50% of the total events, ConfigDirector will issue an alert in the dashboard.",[326,1279,1280],{},"A number between 100 and 100,000. Defaults to 5,000.",[326,1282,1283,1286],{},[267,1284,1285],{},"FlushInterval",[997,1287,1288,1293,1296],{},[326,1289,1290,1291,270],{},"How often events are flushed and sent over the network, as a ",[267,1292,1034],{},[326,1294,1295],{},"Decrease this number if your application consistently captures a large number of events in short periods of time in order to reduce memory footprint from a large event queue.",[326,1297,1298],{},"Defaults to 30 seconds.",[295,1300,1302],{"className":338,"code":1301,"filename":340,"language":248,"meta":300,"style":300},"import \"time\"\n\nclient, err := configdirector.NewClient(configdirector.Options{\n    SDKKey:    \"YOUR-SERVER-SDK-KEY\",\n    Telemetry: configdirector.TelemetryOptions{EventQueueLimit: 10_000, FlushInterval: 15 * time.Second},\n})\n",[267,1303,1304,1314,1318,1344,1358,1405],{"__ignoreMap":300},[304,1305,1306,1308,1310,1312],{"class":306,"line":307},[304,1307,348],{"class":347},[304,1309,464],{"class":358},[304,1311,1112],{"class":310},[304,1313,365],{"class":358},[304,1315,1316],{"class":306,"line":355},[304,1317,381],{"emptyLinePlaceholder":54},[304,1319,1320,1322,1324,1326,1328,1330,1332,1334,1336,1338,1340,1342],{"class":306,"line":368},[304,1321,422],{"class":421},[304,1323,425],{"class":351},[304,1325,428],{"class":421},[304,1327,432],{"class":431},[304,1329,435],{"class":421},[304,1331,270],{"class":351},[304,1333,441],{"class":440},[304,1335,444],{"class":351},[304,1337,447],{"class":310},[304,1339,270],{"class":351},[304,1341,452],{"class":310},[304,1343,722],{"class":351},[304,1345,1346,1348,1350,1352,1354,1356],{"class":306,"line":378},[304,1347,727],{"class":421},[304,1349,461],{"class":351},[304,1351,359],{"class":358},[304,1353,467],{"class":313},[304,1355,470],{"class":358},[304,1357,739],{"class":351},[304,1359,1360,1363,1365,1367,1369,1371,1373,1375,1377,1379,1382,1385,1387,1390,1392,1395,1397,1399,1401,1403],{"class":306,"line":384},[304,1361,1362],{"class":421},"    Telemetry",[304,1364,461],{"class":351},[304,1366,435],{"class":310},[304,1368,270],{"class":351},[304,1370,1262],{"class":310},[304,1372,455],{"class":351},[304,1374,1269],{"class":421},[304,1376,461],{"class":351},[304,1378,1200],{"class":1199},[304,1380,1381],{"class":487},"_",[304,1383,1384],{"class":1199},"000",[304,1386,425],{"class":351},[304,1388,1389],{"class":421}," FlushInterval",[304,1391,461],{"class":351},[304,1393,1394],{"class":1199}," 15",[304,1396,1203],{"class":431},[304,1398,1206],{"class":421},[304,1400,270],{"class":351},[304,1402,1232],{"class":421},[304,1404,783],{"class":351},[304,1406,1407],{"class":306,"line":394},[304,1408,473],{"class":351},[659,1410,1412],{"id":1411},"hooks","Hooks",[260,1414,1415,1416,1418],{},"Hooks are events you can subscribe to in order to be notified of some key actions from the client. In order to subscribe to hooks you can provide handlers in the ",[267,1417,1412],{}," option, which attaches them before the client can emit any event, or call the matching method on the client, which returns a function that cancels the registration:",[295,1420,1422],{"className":338,"code":1421,"filename":340,"language":248,"meta":300,"style":300},"unsubscribe := client.OnConfigsUpdated(func(update configdirector.ConfigsUpdated) {\n    log.Printf(\"configs updated: %v\", update.Keys)\n})\n\nunsubscribe() \u002F\u002F Cancels the registration\n",[267,1423,1424,1461,1492,1496,1500],{"__ignoreMap":300},[304,1425,1426,1429,1431,1433,1435,1438,1440,1443,1445,1449,1451,1453,1456,1459],{"class":306,"line":307},[304,1427,1428],{"class":421},"unsubscribe ",[304,1430,432],{"class":431},[304,1432,524],{"class":421},[304,1434,270],{"class":351},[304,1436,1437],{"class":440},"OnConfigsUpdated",[304,1439,444],{"class":351},[304,1441,1442],{"class":431},"func",[304,1444,444],{"class":351},[304,1446,1448],{"class":1447},"s99_P","update",[304,1450,435],{"class":310},[304,1452,270],{"class":351},[304,1454,1455],{"class":310},"ConfigsUpdated",[304,1457,1458],{"class":351},")",[304,1460,491],{"class":351},[304,1462,1463,1465,1467,1469,1471,1473,1476,1478,1480,1482,1485,1487,1490],{"class":306,"line":355},[304,1464,497],{"class":421},[304,1466,270],{"class":351},[304,1468,601],{"class":440},[304,1470,444],{"class":351},[304,1472,470],{"class":358},[304,1474,1475],{"class":313},"configs updated: ",[304,1477,612],{"class":611},[304,1479,470],{"class":358},[304,1481,425],{"class":351},[304,1483,1484],{"class":421}," update",[304,1486,270],{"class":351},[304,1488,1489],{"class":421},"Keys",[304,1491,397],{"class":351},[304,1493,1494],{"class":306,"line":368},[304,1495,473],{"class":351},[304,1497,1498],{"class":306,"line":378},[304,1499,381],{"emptyLinePlaceholder":54},[304,1501,1502,1505,1508],{"class":306,"line":384},[304,1503,1504],{"class":440},"unsubscribe",[304,1506,1507],{"class":351},"()",[304,1509,1510],{"class":408}," \u002F\u002F Cancels the registration\n",[295,1512,1514],{"className":338,"code":1513,"filename":340,"language":248,"meta":300,"style":300},"client, err := configdirector.NewClient(configdirector.Options{\n    SDKKey: \"YOUR-SERVER-SDK-KEY\",\n    Hooks: configdirector.Hooks{\n        OnConfigEvaluated: func(evaluation configdirector.Evaluation) {\n            log.Printf(\"%s evaluated to %v\", evaluation.Key, evaluation.Value)\n        },\n    },\n})\n",[267,1515,1516,1542,1556,1571,1597,1641,1646,1650],{"__ignoreMap":300},[304,1517,1518,1520,1522,1524,1526,1528,1530,1532,1534,1536,1538,1540],{"class":306,"line":307},[304,1519,422],{"class":421},[304,1521,425],{"class":351},[304,1523,428],{"class":421},[304,1525,432],{"class":431},[304,1527,435],{"class":421},[304,1529,270],{"class":351},[304,1531,441],{"class":440},[304,1533,444],{"class":351},[304,1535,447],{"class":310},[304,1537,270],{"class":351},[304,1539,452],{"class":310},[304,1541,722],{"class":351},[304,1543,1544,1546,1548,1550,1552,1554],{"class":306,"line":355},[304,1545,727],{"class":421},[304,1547,461],{"class":351},[304,1549,464],{"class":358},[304,1551,467],{"class":313},[304,1553,470],{"class":358},[304,1555,739],{"class":351},[304,1557,1558,1561,1563,1565,1567,1569],{"class":306,"line":368},[304,1559,1560],{"class":421},"    Hooks",[304,1562,461],{"class":351},[304,1564,435],{"class":310},[304,1566,270],{"class":351},[304,1568,1412],{"class":310},[304,1570,722],{"class":351},[304,1572,1573,1576,1578,1581,1583,1586,1588,1590,1593,1595],{"class":306,"line":378},[304,1574,1575],{"class":421},"        OnConfigEvaluated",[304,1577,461],{"class":351},[304,1579,1580],{"class":431}," func",[304,1582,444],{"class":351},[304,1584,1585],{"class":1447},"evaluation",[304,1587,435],{"class":310},[304,1589,270],{"class":351},[304,1591,1592],{"class":310},"Evaluation",[304,1594,1458],{"class":351},[304,1596,491],{"class":351},[304,1598,1599,1602,1604,1606,1608,1610,1613,1616,1618,1620,1622,1625,1627,1630,1632,1634,1636,1639],{"class":306,"line":384},[304,1600,1601],{"class":421},"            log",[304,1603,270],{"class":351},[304,1605,601],{"class":440},[304,1607,444],{"class":351},[304,1609,470],{"class":358},[304,1611,1612],{"class":611},"%s",[304,1614,1615],{"class":313}," evaluated to ",[304,1617,612],{"class":611},[304,1619,470],{"class":358},[304,1621,425],{"class":351},[304,1623,1624],{"class":421}," evaluation",[304,1626,270],{"class":351},[304,1628,1629],{"class":421},"Key",[304,1631,425],{"class":351},[304,1633,1624],{"class":421},[304,1635,270],{"class":351},[304,1637,1638],{"class":421},"Value",[304,1640,397],{"class":351},[304,1642,1643],{"class":306,"line":394},[304,1644,1645],{"class":351},"        },\n",[304,1647,1648],{"class":306,"line":400},[304,1649,1239],{"class":351},[304,1651,1652],{"class":306,"line":405},[304,1653,473],{"class":351},[260,1655,1656],{},"The following hooks are available:",[997,1658,1659,1675,1698],{},[326,1660,1661,1666],{},[267,1662,1665],{"className":1663},[1664],"font-semibold","OnReady: func()",[997,1667,1668],{},[326,1669,1670,1671,1674],{},"Called when the client is initialized. When this event is emitted it means the client has received a payload from the ConfigDirector servers and is ready to evaluate configs. It is emitted once; a handler registered with ",[267,1672,1673],{},"OnReady"," after that point is called at once, on the calling goroutine.",[326,1676,1677,1681],{},[267,1678,1680],{"className":1679},[1664],"OnConfigsUpdated: func(ConfigsUpdated)",[997,1682,1683,1686,1692],{},[326,1684,1685],{},"Called when a payload is received from the ConfigDirector servers with config data. It is emitted during initialization when an entire config payload is received. After initialization, it is emitted when updates are pushed from the server (or discovered via polling if that connection mode is used).",[326,1687,1688,1691],{},[267,1689,1690],{},"Keys []string"," - The config keys that were included in the payload from the server, and the keys of configs whose targeting rules use a segment the payload included, sorted.",[326,1693,1694,1697],{},[267,1695,1696],{},"RemovedKeys []string"," - The keys of configs the payload removed, sorted. A config is removed when a full payload no longer includes it. Empty when nothing was removed.",[326,1699,1700,1704],{},[267,1701,1703],{"className":1702},[1664],"OnConfigEvaluated: func(Evaluation)",[997,1705,1706,1709],{},[326,1707,1708],{},"Called whenever a config is evaluated. This includes calls to the value methods and evaluations delivered to watches.",[326,1710,1711,1713,1714],{},[267,1712,1592],{}," - A struct containing the details of the evaluation:\n",[997,1715,1716,1722,1728,1737,1743,1808],{},[326,1717,1718,1721],{},[267,1719,1720],{},"Key string"," - The config key that was evaluated",[326,1723,1724,1727],{},[267,1725,1726],{},"Value any"," - The value the config evaluated to, in the type the read asked for. It can be the in-code default value provided to the value method or to the watch. For example, if the config was evaluated before the client was initialized.",[326,1729,1730,1733,1734,1736],{},[267,1731,1732],{},"ValueID string"," - The value ID, which is a stable hash of the value that can be used for analytics or other third parties rather than sending the ",[267,1735,1638],{},". This can be useful for large values, like a JSON config, or to avoid disclosing the values themselves to third parties. It is empty when the evaluation fell back to the in-code default value.",[326,1738,1739,1742],{},[267,1740,1741],{},"UsedDefault bool"," - Whether or not the evaluation fell back to the in-code default value.",[326,1744,1745,1748,1749,1752,1753,1756,1757],{},[267,1746,1747],{},"Reason EvaluationReason"," - The reason for the evaluation resolution. In the case the config successfully evaluated based on server-provided targeting rules, it will be ",[267,1750,1751],{},"EvaluationReasonFoundMatch",". If the evaluation had to fall back to the in-code default value, the ",[267,1754,1755],{},"Reason"," will encode why the fallback was required:\n",[997,1758,1759,1765,1771,1777,1790,1796,1802],{},[326,1760,1761,1764],{},[267,1762,1763],{},"EvaluationReasonClientNotReady"," - The evaluation happened before the client finished initialization, or after it was closed",[326,1766,1767,1770],{},[267,1768,1769],{},"EvaluationReasonConfigStateMissing"," - The requested config key was not present in the payload received from the server, usually because of an incorrect config key",[326,1772,1773,1776],{},[267,1774,1775],{},"EvaluationReasonInvalidNumber"," - The config was requested as a number but the value received from the server could not be read as that number type",[326,1778,1779,1782,1783,1786,1787],{},[267,1780,1781],{},"EvaluationReasonInvalidBoolean"," - The config was requested as a boolean but the value received from the server was neither ",[267,1784,1785],{},"true"," nor ",[267,1788,1789],{},"false",[326,1791,1792,1795],{},[267,1793,1794],{},"EvaluationReasonInvalidJSON"," - The config was requested as JSON but the value received from the server could not be decoded into the target",[326,1797,1798,1801],{},[267,1799,1800],{},"EvaluationReasonTypeMismatch"," - The config was requested as a string but it is a boolean or number config",[326,1803,1804,1807],{},[267,1805,1806],{},"EvaluationReasonValueMissing"," - The config key was present in the payload received from the server, but it carried no value",[326,1809,1810,1813],{},[267,1811,1812],{},"UserContext UserContext"," - The user context that was provided to the evaluation",[260,1815,1816,1817,1820,1821,1823],{},"Each reason constant's value is its name in kebab case, such as ",[267,1818,1819],{},"type-mismatch"," for ",[267,1822,1800],{},", which is how logs and the test client's documentation spell them.",[260,1825,1826,1828,1829,1831,1832,1835],{},[267,1827,1673],{}," and ",[267,1830,1437],{}," handlers run on the connection's goroutine, so one that blocks delays later updates. ",[267,1833,1834],{},"OnConfigEvaluated"," handlers run on the goroutine that read the value, or on the connection's goroutine when a watch fires. A panic in a handler is recovered and logged at error level; the other handlers and the connection continue.",[255,1837,1839],{"id":1838},"retrieve-config-values","Retrieve config values",[260,1841,1842],{},"Retrieve config values via the typed value methods, and subscribe to updates via the typed watches. The first argument is the config key, the second is the in-code default value, which is returned if the config is not available or cannot be read as the given type, and the third is the user context targeting rules are evaluated for. The method decides the type the config is read as:",[295,1844,1846],{"className":338,"code":1845,"filename":340,"language":248,"meta":300,"style":300},"user := configdirector.UserContext{ID: \"user-id\"}\n\n\u002F\u002F Retrieve config values\nretries := client.IntValue(\"max-retries\", 3, user)\ntheme := client.StringValue(\"theme\", \"light\", user)\nnewCheckout := client.BoolValue(\"new-checkout\", false, user)\n\n\u002F\u002F A JSON config is decoded into a value of your own, which holds the in-code default value\nlimits := map[string]any{\"per_minute\": 60}\nif err := client.JSONValue(\"rate-limits\", &limits, user); err != nil {\n    log.Fatal(err)\n}\n\n\u002F\u002F Watch for config updates\nunwatch := client.WatchBool(\"new-checkout\", false, user, func(value bool) {\n    log.Printf(\"new-checkout is now %t\", value)\n})\n\n\u002F\u002F Call unwatch when the watch is no longer needed\nunwatch()\n",[267,1847,1848,1878,1882,1887,1922,1960,1995,1999,2004,2043,2089,2103,2107,2111,2116,2162,2189,2193,2197,2202],{"__ignoreMap":300},[304,1849,1850,1853,1855,1857,1859,1862,1864,1867,1869,1871,1874,1876],{"class":306,"line":307},[304,1851,1852],{"class":421},"user ",[304,1854,432],{"class":431},[304,1856,435],{"class":310},[304,1858,270],{"class":351},[304,1860,1861],{"class":310},"UserContext",[304,1863,455],{"class":351},[304,1865,1866],{"class":421},"ID",[304,1868,461],{"class":351},[304,1870,464],{"class":358},[304,1872,1873],{"class":313},"user-id",[304,1875,470],{"class":358},[304,1877,515],{"class":351},[304,1879,1880],{"class":306,"line":355},[304,1881,381],{"emptyLinePlaceholder":54},[304,1883,1884],{"class":306,"line":368},[304,1885,1886],{"class":408},"\u002F\u002F Retrieve config values\n",[304,1888,1889,1892,1894,1896,1898,1901,1903,1905,1908,1910,1912,1915,1917,1920],{"class":306,"line":378},[304,1890,1891],{"class":421},"retries ",[304,1893,432],{"class":431},[304,1895,524],{"class":421},[304,1897,270],{"class":351},[304,1899,1900],{"class":440},"IntValue",[304,1902,444],{"class":351},[304,1904,470],{"class":358},[304,1906,1907],{"class":313},"max-retries",[304,1909,470],{"class":358},[304,1911,425],{"class":351},[304,1913,1914],{"class":1199}," 3",[304,1916,425],{"class":351},[304,1918,1919],{"class":421}," user",[304,1921,397],{"class":351},[304,1923,1924,1927,1929,1931,1933,1936,1938,1940,1943,1945,1947,1949,1952,1954,1956,1958],{"class":306,"line":384},[304,1925,1926],{"class":421},"theme ",[304,1928,432],{"class":431},[304,1930,524],{"class":421},[304,1932,270],{"class":351},[304,1934,1935],{"class":440},"StringValue",[304,1937,444],{"class":351},[304,1939,470],{"class":358},[304,1941,1942],{"class":313},"theme",[304,1944,470],{"class":358},[304,1946,425],{"class":351},[304,1948,464],{"class":358},[304,1950,1951],{"class":313},"light",[304,1953,470],{"class":358},[304,1955,425],{"class":351},[304,1957,1919],{"class":421},[304,1959,397],{"class":351},[304,1961,1962,1965,1967,1969,1971,1974,1976,1978,1981,1983,1985,1989,1991,1993],{"class":306,"line":394},[304,1963,1964],{"class":421},"newCheckout ",[304,1966,432],{"class":431},[304,1968,524],{"class":421},[304,1970,270],{"class":351},[304,1972,1973],{"class":440},"BoolValue",[304,1975,444],{"class":351},[304,1977,470],{"class":358},[304,1979,1980],{"class":313},"new-checkout",[304,1982,470],{"class":358},[304,1984,425],{"class":351},[304,1986,1988],{"class":1987},"syTEX"," false",[304,1990,425],{"class":351},[304,1992,1919],{"class":421},[304,1994,397],{"class":351},[304,1996,1997],{"class":306,"line":400},[304,1998,381],{"emptyLinePlaceholder":54},[304,2000,2001],{"class":306,"line":405},[304,2002,2003],{"class":408},"\u002F\u002F A JSON config is decoded into a value of your own, which holds the in-code default value\n",[304,2005,2006,2009,2011,2014,2017,2021,2024,2027,2029,2031,2034,2036,2038,2041],{"class":306,"line":412},[304,2007,2008],{"class":421},"limits ",[304,2010,432],{"class":431},[304,2012,2013],{"class":431}," map",[304,2015,2016],{"class":351},"[",[304,2018,2020],{"class":2019},"sbsja","string",[304,2022,2023],{"class":351},"]",[304,2025,2026],{"class":310},"any",[304,2028,455],{"class":351},[304,2030,470],{"class":358},[304,2032,2033],{"class":313},"per_minute",[304,2035,470],{"class":358},[304,2037,461],{"class":351},[304,2039,2040],{"class":1199}," 60",[304,2042,515],{"class":351},[304,2044,2045,2047,2049,2051,2053,2055,2058,2060,2062,2065,2067,2069,2071,2074,2076,2078,2081,2083,2085,2087],{"class":306,"line":418},[304,2046,479],{"class":347},[304,2048,428],{"class":421},[304,2050,432],{"class":431},[304,2052,524],{"class":421},[304,2054,270],{"class":351},[304,2056,2057],{"class":440},"JSONValue",[304,2059,444],{"class":351},[304,2061,470],{"class":358},[304,2063,2064],{"class":313},"rate-limits",[304,2066,470],{"class":358},[304,2068,425],{"class":351},[304,2070,893],{"class":431},[304,2072,2073],{"class":421},"limits",[304,2075,425],{"class":351},[304,2077,1919],{"class":421},[304,2079,2080],{"class":351},");",[304,2082,428],{"class":421},[304,2084,484],{"class":431},[304,2086,488],{"class":487},[304,2088,491],{"class":351},[304,2090,2091,2093,2095,2097,2099,2101],{"class":306,"line":476},[304,2092,497],{"class":421},[304,2094,270],{"class":351},[304,2096,502],{"class":440},[304,2098,444],{"class":351},[304,2100,507],{"class":421},[304,2102,397],{"class":351},[304,2104,2105],{"class":306,"line":494},[304,2106,515],{"class":351},[304,2108,2109],{"class":306,"line":512},[304,2110,381],{"emptyLinePlaceholder":54},[304,2112,2113],{"class":306,"line":518},[304,2114,2115],{"class":408},"\u002F\u002F Watch for config updates\n",[304,2117,2118,2121,2123,2125,2127,2130,2132,2134,2136,2138,2140,2142,2144,2146,2148,2150,2152,2155,2158,2160],{"class":306,"line":535},[304,2119,2120],{"class":421},"unwatch ",[304,2122,432],{"class":431},[304,2124,524],{"class":421},[304,2126,270],{"class":351},[304,2128,2129],{"class":440},"WatchBool",[304,2131,444],{"class":351},[304,2133,470],{"class":358},[304,2135,1980],{"class":313},[304,2137,470],{"class":358},[304,2139,425],{"class":351},[304,2141,1988],{"class":1987},[304,2143,425],{"class":351},[304,2145,1919],{"class":421},[304,2147,425],{"class":351},[304,2149,1580],{"class":431},[304,2151,444],{"class":351},[304,2153,2154],{"class":1447},"value",[304,2156,2157],{"class":2019}," bool",[304,2159,1458],{"class":351},[304,2161,491],{"class":351},[304,2163,2164,2166,2168,2170,2172,2174,2177,2180,2182,2184,2187],{"class":306,"line":540},[304,2165,497],{"class":421},[304,2167,270],{"class":351},[304,2169,601],{"class":440},[304,2171,444],{"class":351},[304,2173,470],{"class":358},[304,2175,2176],{"class":313},"new-checkout is now ",[304,2178,2179],{"class":611},"%t",[304,2181,470],{"class":358},[304,2183,425],{"class":351},[304,2185,2186],{"class":421}," value",[304,2188,397],{"class":351},[304,2190,2191],{"class":306,"line":546},[304,2192,473],{"class":351},[304,2194,2195],{"class":306,"line":552},[304,2196,381],{"emptyLinePlaceholder":54},[304,2198,2199],{"class":306,"line":558},[304,2200,2201],{"class":408},"\u002F\u002F Call unwatch when the watch is no longer needed\n",[304,2203,2204,2207],{"class":306,"line":594},[304,2205,2206],{"class":440},"unwatch",[304,2208,532],{"class":351},[260,2210,2211],{},"The type a config is read as is decided by the method you call, not by how the config was declared in the dashboard. There is one method per type the SDK can read, so a type it cannot fill is a compile error rather than a surprise at runtime. Each one takes the value to return when the config is missing, the client is not ready, or the value will not read as the requested type, so the in-code default value should always be the safe choice:",[997,2213,2214,2219,2224,2229,2234],{},[326,2215,2216],{},[267,2217,2218],{},"BoolValue(key string, defaultValue bool, userContext UserContext) bool",[326,2220,2221],{},[267,2222,2223],{},"StringValue(key string, defaultValue string, userContext UserContext) string",[326,2225,2226],{},[267,2227,2228],{},"IntValue(key string, defaultValue int64, userContext UserContext) int64",[326,2230,2231],{},[267,2232,2233],{},"FloatValue(key string, defaultValue float64, userContext UserContext) float64",[326,2235,2236],{},[267,2237,2238],{},"JSONValue(key string, target any, userContext UserContext) error",[260,2240,2241],{},"A few of those are worth calling out:",[997,2243,2244,2247,2259],{},[326,2245,2246],{},"Reading a string, enum, URL or JSON config as a string returns the value as the server spelled it, with no parsing, so a JSON config gives its JSON. A boolean or number config read as a string gives the in-code default value instead.",[326,2248,2249,2250,1828,2252,2254,2255,2258],{},"Only ",[267,2251,1785],{},[267,2253,1789],{}," read as a boolean, in either casing. A config holding ",[267,2256,2257],{},"1"," yields the in-code default value.",[326,2260,2261,2262,1012,2265,2268,2269,2272,2273,2276],{},"A whole number the server wrote as ",[267,2263,2264],{},"26.0",[267,2266,2267],{},"2.6e1"," still reads as ",[267,2270,2271],{},"26",". A value an ",[267,2274,2275],{},"int64"," cannot hold yields the in-code default value rather than an overflow.",[260,2278,2279,2280,2283,2284,2287,2288,2291,2292,2294,2295,1828,2298,2301],{},"Every value method has a ",[267,2281,2282],{},"ValueDetails"," twin, such as ",[267,2285,2286],{},"BoolValueDetails",", that also returns an ",[267,2289,2290],{},"EvaluationDetails"," with the ",[267,2293,1755],{},", ",[267,2296,2297],{},"ValueID",[267,2299,2300],{},"UsedDefault"," of the read, so your code can tell a served value from a fallback:",[295,2303,2305],{"className":338,"code":2304,"filename":340,"language":248,"meta":300,"style":300},"newCheckout, details := client.BoolValueDetails(\"new-checkout\", false, user)\nif details.UsedDefault {\n    log.Printf(\"new-checkout fell back to its in-code default value: %s\", details.Reason)\n}\n",[267,2306,2307,2343,2357,2386],{"__ignoreMap":300},[304,2308,2309,2312,2314,2317,2319,2321,2323,2325,2327,2329,2331,2333,2335,2337,2339,2341],{"class":306,"line":307},[304,2310,2311],{"class":421},"newCheckout",[304,2313,425],{"class":351},[304,2315,2316],{"class":421}," details ",[304,2318,432],{"class":431},[304,2320,524],{"class":421},[304,2322,270],{"class":351},[304,2324,2286],{"class":440},[304,2326,444],{"class":351},[304,2328,470],{"class":358},[304,2330,1980],{"class":313},[304,2332,470],{"class":358},[304,2334,425],{"class":351},[304,2336,1988],{"class":1987},[304,2338,425],{"class":351},[304,2340,1919],{"class":421},[304,2342,397],{"class":351},[304,2344,2345,2347,2350,2352,2355],{"class":306,"line":355},[304,2346,479],{"class":347},[304,2348,2349],{"class":421}," details",[304,2351,270],{"class":351},[304,2353,2354],{"class":421},"UsedDefault ",[304,2356,722],{"class":351},[304,2358,2359,2361,2363,2365,2367,2369,2372,2374,2376,2378,2380,2382,2384],{"class":306,"line":368},[304,2360,497],{"class":421},[304,2362,270],{"class":351},[304,2364,601],{"class":440},[304,2366,444],{"class":351},[304,2368,470],{"class":358},[304,2370,2371],{"class":313},"new-checkout fell back to its in-code default value: ",[304,2373,1612],{"class":611},[304,2375,470],{"class":358},[304,2377,425],{"class":351},[304,2379,2349],{"class":421},[304,2381,270],{"class":351},[304,2383,1755],{"class":421},[304,2385,397],{"class":351},[304,2387,2388],{"class":306,"line":378},[304,2389,515],{"class":351},[2391,2392,2393,2394,2396,2397,270],"note",{},"Value methods never return an error when a config is missing or a value will not read as the requested type; they return your in-code default value instead. Which of those happened is reported by the ",[267,2395,2282],{}," twins and through ",[267,2398,1834],{},[659,2400,2402],{"id":2401},"json-configs","JSON configs",[260,2404,2405,2407,2408,2411,2412,2415],{},[267,2406,2057],{}," decodes the config's JSON document into ",[267,2409,2410],{},"target",", a non-nil pointer to a struct, map, slice or any other type ",[267,2413,2414],{},"encoding\u002Fjson"," can decode into. The value you put in the target beforehand is the in-code default value: the target is left as it was when the config is missing, the client is not ready, or the document cannot be decoded into it. The only error is for a target that is not a non-nil pointer.",[295,2417,2419],{"className":338,"code":2418,"filename":340,"language":248,"meta":300,"style":300},"type RateLimits struct {\n    PerMinute int `json:\"per_minute\"`\n}\n\nlimits := RateLimits{PerMinute: 60}\nif err := client.JSONValue(\"rate-limits\", &limits, user); err != nil {\n    log.Fatal(err)\n}\n",[267,2420,2421,2434,2451,2455,2459,2478,2520,2534],{"__ignoreMap":300},[304,2422,2423,2426,2429,2432],{"class":306,"line":307},[304,2424,2425],{"class":431},"type",[304,2427,2428],{"class":310}," RateLimits",[304,2430,2431],{"class":431}," struct",[304,2433,491],{"class":351},[304,2435,2436,2439,2442,2445,2448],{"class":306,"line":355},[304,2437,2438],{"class":421},"    PerMinute ",[304,2440,2441],{"class":2019},"int",[304,2443,2444],{"class":358}," `",[304,2446,2447],{"class":313},"json:\"per_minute\"",[304,2449,2450],{"class":358},"`\n",[304,2452,2453],{"class":306,"line":368},[304,2454,515],{"class":351},[304,2456,2457],{"class":306,"line":378},[304,2458,381],{"emptyLinePlaceholder":54},[304,2460,2461,2463,2465,2467,2469,2472,2474,2476],{"class":306,"line":384},[304,2462,2008],{"class":421},[304,2464,432],{"class":431},[304,2466,2428],{"class":310},[304,2468,455],{"class":351},[304,2470,2471],{"class":421},"PerMinute",[304,2473,461],{"class":351},[304,2475,2040],{"class":1199},[304,2477,515],{"class":351},[304,2479,2480,2482,2484,2486,2488,2490,2492,2494,2496,2498,2500,2502,2504,2506,2508,2510,2512,2514,2516,2518],{"class":306,"line":394},[304,2481,479],{"class":347},[304,2483,428],{"class":421},[304,2485,432],{"class":431},[304,2487,524],{"class":421},[304,2489,270],{"class":351},[304,2491,2057],{"class":440},[304,2493,444],{"class":351},[304,2495,470],{"class":358},[304,2497,2064],{"class":313},[304,2499,470],{"class":358},[304,2501,425],{"class":351},[304,2503,893],{"class":431},[304,2505,2073],{"class":421},[304,2507,425],{"class":351},[304,2509,1919],{"class":421},[304,2511,2080],{"class":351},[304,2513,428],{"class":421},[304,2515,484],{"class":431},[304,2517,488],{"class":487},[304,2519,491],{"class":351},[304,2521,2522,2524,2526,2528,2530,2532],{"class":306,"line":400},[304,2523,497],{"class":421},[304,2525,270],{"class":351},[304,2527,502],{"class":440},[304,2529,444],{"class":351},[304,2531,507],{"class":421},[304,2533,397],{"class":351},[304,2535,2536],{"class":306,"line":405},[304,2537,515],{"class":351},[2391,2539,2540,2542,2543,2546],{},[267,2541,2414],{}," ignores any member the target does not declare, and leaves a field the document does not carry as it was, so a config whose shape has moved on cannot be told apart from your in-code default value. Decode into a ",[267,2544,2545],{},"map[string]any"," to read the document as it stands.",[659,2548,2550],{"id":2549},"evaluate-config-values-with-a-user-context","Evaluate config values with a user context",[2391,2552,2553],{},"Unlike client SDKs, the server SDKs are able to evaluate targeting rules for the given user context locally without additional network calls.",[260,2555,2556,2557,2559,2560,2563],{},"Every value method takes a ",[267,2558,1861],{}," as its last argument, which is what targeting rules are evaluated against. The same key can therefore resolve differently per user. Pass an empty ",[267,2561,2562],{},"configdirector.UserContext{}"," when there is no user to evaluate for.",[260,2565,2566],{},"The user context is the last argument of every value method. Unlike client SDKs, the server SDKs are able to evaluate targeting rules for the given user context locally without additional network calls.",[295,2568,2570],{"className":338,"code":2569,"filename":340,"language":248,"meta":300,"style":300},"import \"github.com\u002FConfigDirector\u002Fgo-sdks\u002Fconfigdirector\"\n\nuser := configdirector.UserContext{\n    ID:   \"12345\",\n    Name: \"Example User\",\n    Traits: map[string]any{\n        \"region\": \"North America\", \u002F\u002F Any arbitrary traits which can be referenced in targeting rules\n    },\n}\n\nclient.BoolValue(\"my-boolean-config-key\", false, user)\n",[267,2571,2572,2582,2586,2600,2616,2632,2651,2675,2679,2683,2687],{"__ignoreMap":300},[304,2573,2574,2576,2578,2580],{"class":306,"line":307},[304,2575,348],{"class":347},[304,2577,464],{"class":358},[304,2579,389],{"class":310},[304,2581,365],{"class":358},[304,2583,2584],{"class":306,"line":355},[304,2585,381],{"emptyLinePlaceholder":54},[304,2587,2588,2590,2592,2594,2596,2598],{"class":306,"line":368},[304,2589,1852],{"class":421},[304,2591,432],{"class":431},[304,2593,435],{"class":310},[304,2595,270],{"class":351},[304,2597,1861],{"class":310},[304,2599,722],{"class":351},[304,2601,2602,2605,2607,2609,2612,2614],{"class":306,"line":378},[304,2603,2604],{"class":421},"    ID",[304,2606,461],{"class":351},[304,2608,732],{"class":358},[304,2610,2611],{"class":313},"12345",[304,2613,470],{"class":358},[304,2615,739],{"class":351},[304,2617,2618,2621,2623,2625,2628,2630],{"class":306,"line":384},[304,2619,2620],{"class":421},"    Name",[304,2622,461],{"class":351},[304,2624,464],{"class":358},[304,2626,2627],{"class":313},"Example User",[304,2629,470],{"class":358},[304,2631,739],{"class":351},[304,2633,2634,2637,2639,2641,2643,2645,2647,2649],{"class":306,"line":394},[304,2635,2636],{"class":421},"    Traits",[304,2638,461],{"class":351},[304,2640,2013],{"class":431},[304,2642,2016],{"class":351},[304,2644,2020],{"class":2019},[304,2646,2023],{"class":351},[304,2648,2026],{"class":310},[304,2650,722],{"class":351},[304,2652,2653,2656,2659,2661,2663,2665,2668,2670,2672],{"class":306,"line":400},[304,2654,2655],{"class":358},"        \"",[304,2657,2658],{"class":313},"region",[304,2660,470],{"class":358},[304,2662,461],{"class":351},[304,2664,464],{"class":358},[304,2666,2667],{"class":313},"North America",[304,2669,470],{"class":358},[304,2671,425],{"class":351},[304,2673,2674],{"class":408}," \u002F\u002F Any arbitrary traits which can be referenced in targeting rules\n",[304,2676,2677],{"class":306,"line":405},[304,2678,1239],{"class":351},[304,2680,2681],{"class":306,"line":412},[304,2682,515],{"class":351},[304,2684,2685],{"class":306,"line":418},[304,2686,381],{"emptyLinePlaceholder":54},[304,2688,2689,2691,2693,2695,2697,2699,2702,2704,2706,2708,2710,2712],{"class":306,"line":476},[304,2690,422],{"class":421},[304,2692,270],{"class":351},[304,2694,1973],{"class":440},[304,2696,444],{"class":351},[304,2698,470],{"class":358},[304,2700,2701],{"class":313},"my-boolean-config-key",[304,2703,470],{"class":358},[304,2705,425],{"class":351},[304,2707,1988],{"class":1987},[304,2709,425],{"class":351},[304,2711,1919],{"class":421},[304,2713,397],{"class":351},[260,2715,2716],{},"It is also the third argument of every watch:",[295,2718,2720],{"className":338,"code":2719,"filename":340,"language":248,"meta":300,"style":300},"unwatch := client.WatchBool(\"new-checkout\", false, user, func(value bool) {\n    log.Printf(\"new-checkout is now %t\", value)\n})\n",[267,2721,2722,2764,2788],{"__ignoreMap":300},[304,2723,2724,2726,2728,2730,2732,2734,2736,2738,2740,2742,2744,2746,2748,2750,2752,2754,2756,2758,2760,2762],{"class":306,"line":307},[304,2725,2120],{"class":421},[304,2727,432],{"class":431},[304,2729,524],{"class":421},[304,2731,270],{"class":351},[304,2733,2129],{"class":440},[304,2735,444],{"class":351},[304,2737,470],{"class":358},[304,2739,1980],{"class":313},[304,2741,470],{"class":358},[304,2743,425],{"class":351},[304,2745,1988],{"class":1987},[304,2747,425],{"class":351},[304,2749,1919],{"class":421},[304,2751,425],{"class":351},[304,2753,1580],{"class":431},[304,2755,444],{"class":351},[304,2757,2154],{"class":1447},[304,2759,2157],{"class":2019},[304,2761,1458],{"class":351},[304,2763,491],{"class":351},[304,2765,2766,2768,2770,2772,2774,2776,2778,2780,2782,2784,2786],{"class":306,"line":355},[304,2767,497],{"class":421},[304,2769,270],{"class":351},[304,2771,601],{"class":440},[304,2773,444],{"class":351},[304,2775,470],{"class":358},[304,2777,2176],{"class":313},[304,2779,2179],{"class":611},[304,2781,470],{"class":358},[304,2783,425],{"class":351},[304,2785,2186],{"class":421},[304,2787,397],{"class":351},[304,2789,2790],{"class":306,"line":368},[304,2791,473],{"class":351},[260,2793,2794,2796],{},[267,2795,1861],{}," is a struct with the following fields:",[997,2798,2799,2808,2818,2848],{},[326,2800,2801,2803],{},[267,2802,1866],{},[997,2804,2805],{},[326,2806,2807],{},"The user's identifier. It decides their bucket in a percentage rollout, so changing it can move a user into a different bucket. Leave it empty for a user without a stable identity.",[326,2809,2810,2813],{},[267,2811,2812],{},"Name",[997,2814,2815],{},[326,2816,2817],{},"The user's display name.",[326,2819,2820,2823],{},[267,2821,2822],{},"Traits",[997,2824,2825],{},[326,2826,2827,2828,2830,2831,2833,2834,2836,2837,2840,2841,2844,2845,2847],{},"A ",[267,2829,2545],{}," of arbitrary traits which can be referenced in targeting rules, keyed by the name the rule references. Each trait is evaluated as the value ",[267,2832,2414],{}," encodes it to: an ",[267,2835,2441],{}," is a number, a ",[267,2838,2839],{},"[]string"," is an array of text, a struct is an object keyed by its JSON field names, and a ",[267,2842,2843],{},"time.Time"," is its RFC 3339 text. A trait ",[267,2846,2414],{}," cannot encode, such as a channel, matches no condition, and the other traits in the context still match.",[326,2849,2850,2853],{},[267,2851,2852],{},"Anonymous",[997,2854,2855],{},[326,2856,2857],{},"Keeps the context out of the dashboard: it is evaluated but never persisted, and telemetry reports neither the context nor its ID.",[295,2859,2861],{"className":338,"code":2860,"filename":340,"language":248,"meta":300,"style":300},"user := configdirector.UserContext{\n    ID:   \"user-id\",\n    Name: \"Example User\",\n    Traits: map[string]any{\n        \"region\": \"North America\",\n        \"age\":    26,\n        \"tags\":   []string{\"beta\", \"internal\"},\n    },\n}\n",[267,2862,2863,2877,2891,2905,2923,2941,2957,2993,2997],{"__ignoreMap":300},[304,2864,2865,2867,2869,2871,2873,2875],{"class":306,"line":307},[304,2866,1852],{"class":421},[304,2868,432],{"class":431},[304,2870,435],{"class":310},[304,2872,270],{"class":351},[304,2874,1861],{"class":310},[304,2876,722],{"class":351},[304,2878,2879,2881,2883,2885,2887,2889],{"class":306,"line":355},[304,2880,2604],{"class":421},[304,2882,461],{"class":351},[304,2884,732],{"class":358},[304,2886,1873],{"class":313},[304,2888,470],{"class":358},[304,2890,739],{"class":351},[304,2892,2893,2895,2897,2899,2901,2903],{"class":306,"line":368},[304,2894,2620],{"class":421},[304,2896,461],{"class":351},[304,2898,464],{"class":358},[304,2900,2627],{"class":313},[304,2902,470],{"class":358},[304,2904,739],{"class":351},[304,2906,2907,2909,2911,2913,2915,2917,2919,2921],{"class":306,"line":378},[304,2908,2636],{"class":421},[304,2910,461],{"class":351},[304,2912,2013],{"class":431},[304,2914,2016],{"class":351},[304,2916,2020],{"class":2019},[304,2918,2023],{"class":351},[304,2920,2026],{"class":310},[304,2922,722],{"class":351},[304,2924,2925,2927,2929,2931,2933,2935,2937,2939],{"class":306,"line":384},[304,2926,2655],{"class":358},[304,2928,2658],{"class":313},[304,2930,470],{"class":358},[304,2932,461],{"class":351},[304,2934,464],{"class":358},[304,2936,2667],{"class":313},[304,2938,470],{"class":358},[304,2940,739],{"class":351},[304,2942,2943,2945,2948,2950,2952,2955],{"class":306,"line":394},[304,2944,2655],{"class":358},[304,2946,2947],{"class":313},"age",[304,2949,470],{"class":358},[304,2951,461],{"class":351},[304,2953,2954],{"class":1199},"    26",[304,2956,739],{"class":351},[304,2958,2959,2961,2964,2966,2968,2971,2973,2975,2977,2980,2982,2984,2986,2989,2991],{"class":306,"line":400},[304,2960,2655],{"class":358},[304,2962,2963],{"class":313},"tags",[304,2965,470],{"class":358},[304,2967,461],{"class":351},[304,2969,2970],{"class":351},"   []",[304,2972,2020],{"class":2019},[304,2974,455],{"class":351},[304,2976,470],{"class":358},[304,2978,2979],{"class":313},"beta",[304,2981,470],{"class":358},[304,2983,425],{"class":351},[304,2985,464],{"class":358},[304,2987,2988],{"class":313},"internal",[304,2990,470],{"class":358},[304,2992,783],{"class":351},[304,2994,2995],{"class":306,"line":405},[304,2996,1239],{"class":351},[304,2998,2999],{"class":306,"line":412},[304,3000,515],{"class":351},[659,3002,3004],{"id":3003},"watch-for-updates","Watch for updates",[260,3006,3007],{},"The watches mirror the value methods, one per type. Each accepts the config key, the in-code default value used when an update will not read as that type, the user context, and a handler that runs with the newly evaluated value:",[997,3009,3010,3015,3020,3025,3030],{},[326,3011,3012],{},[267,3013,3014],{},"WatchBool(key string, defaultValue bool, userContext UserContext, handler func(bool))",[326,3016,3017],{},[267,3018,3019],{},"WatchString(key string, defaultValue string, userContext UserContext, handler func(string))",[326,3021,3022],{},[267,3023,3024],{},"WatchInt(key string, defaultValue int64, userContext UserContext, handler func(int64))",[326,3026,3027],{},[267,3028,3029],{},"WatchFloat(key string, defaultValue float64, userContext UserContext, handler func(float64))",[326,3031,3032],{},[267,3033,3034],{},"WatchJSON(key string, newTarget func() any, userContext UserContext, handler func(target any))",[260,3036,3037],{},"A watch fires whenever an update carries its key or a segment the config's targeting rules use, whether or not the value changed. Registering a watch before the first config state arrives means it is called for that first state as well; one registered afterwards only sees later updates. When a full payload no longer includes the watched config, the handler is called with the in-code default value, so it always agrees with what the value method would return. Handlers run on the connection's goroutine, so one that blocks delays later updates.",[295,3039,3041],{"className":338,"code":3040,"filename":340,"language":248,"meta":300,"style":300},"unwatch := client.WatchBool(\n    \"new-checkout\",\n    false,\n    configdirector.UserContext{ID: \"user-id\", Name: \"Example User\", Traits: map[string]any{\"region\": \"North America\"}},\n    func(value bool) {\n        log.Printf(\"new-checkout is now %t\", value)\n    },\n)\n",[267,3042,3043,3058,3068,3075,3145,3160,3185,3189],{"__ignoreMap":300},[304,3044,3045,3047,3049,3051,3053,3055],{"class":306,"line":307},[304,3046,2120],{"class":421},[304,3048,432],{"class":431},[304,3050,524],{"class":421},[304,3052,270],{"class":351},[304,3054,2129],{"class":440},[304,3056,3057],{"class":351},"(\n",[304,3059,3060,3062,3064,3066],{"class":306,"line":355},[304,3061,359],{"class":358},[304,3063,1980],{"class":313},[304,3065,470],{"class":358},[304,3067,739],{"class":351},[304,3069,3070,3073],{"class":306,"line":368},[304,3071,3072],{"class":1987},"    false",[304,3074,739],{"class":351},[304,3076,3077,3080,3082,3084,3086,3088,3090,3092,3094,3096,3098,3101,3103,3105,3107,3109,3111,3114,3116,3118,3120,3122,3124,3126,3128,3130,3132,3134,3136,3138,3140,3142],{"class":306,"line":378},[304,3078,3079],{"class":310},"    configdirector",[304,3081,270],{"class":351},[304,3083,1861],{"class":310},[304,3085,455],{"class":351},[304,3087,1866],{"class":421},[304,3089,461],{"class":351},[304,3091,464],{"class":358},[304,3093,1873],{"class":313},[304,3095,470],{"class":358},[304,3097,425],{"class":351},[304,3099,3100],{"class":421}," Name",[304,3102,461],{"class":351},[304,3104,464],{"class":358},[304,3106,2627],{"class":313},[304,3108,470],{"class":358},[304,3110,425],{"class":351},[304,3112,3113],{"class":421}," Traits",[304,3115,461],{"class":351},[304,3117,2013],{"class":431},[304,3119,2016],{"class":351},[304,3121,2020],{"class":2019},[304,3123,2023],{"class":351},[304,3125,2026],{"class":310},[304,3127,455],{"class":351},[304,3129,470],{"class":358},[304,3131,2658],{"class":313},[304,3133,470],{"class":358},[304,3135,461],{"class":351},[304,3137,464],{"class":358},[304,3139,2667],{"class":313},[304,3141,470],{"class":358},[304,3143,3144],{"class":351},"}},\n",[304,3146,3147,3150,3152,3154,3156,3158],{"class":306,"line":384},[304,3148,3149],{"class":431},"    func",[304,3151,444],{"class":351},[304,3153,2154],{"class":1447},[304,3155,2157],{"class":2019},[304,3157,1458],{"class":351},[304,3159,491],{"class":351},[304,3161,3162,3165,3167,3169,3171,3173,3175,3177,3179,3181,3183],{"class":306,"line":394},[304,3163,3164],{"class":421},"        log",[304,3166,270],{"class":351},[304,3168,601],{"class":440},[304,3170,444],{"class":351},[304,3172,470],{"class":358},[304,3174,2176],{"class":313},[304,3176,2179],{"class":611},[304,3178,470],{"class":358},[304,3180,425],{"class":351},[304,3182,2186],{"class":421},[304,3184,397],{"class":351},[304,3186,3187],{"class":306,"line":400},[304,3188,1239],{"class":351},[304,3190,3191],{"class":306,"line":405},[304,3192,397],{"class":351},[260,3194,3195,3198],{},[267,3196,3197],{},"WatchJSON"," takes a function that returns a fresh pointer holding the in-code default value, rather than the value itself, because every update decodes into a new target:",[295,3200,3202],{"className":338,"code":3201,"filename":340,"language":248,"meta":300,"style":300},"unwatch := client.WatchJSON(\n    \"rate-limits\",\n    func() any { return &RateLimits{PerMinute: 60} },\n    user,\n    func(target any) {\n        limits := target.(*RateLimits)\n        log.Printf(\"rate-limits is now %d per minute\", limits.PerMinute)\n    },\n)\n",[267,3203,3204,3218,3228,3262,3269,3283,3303,3337,3341],{"__ignoreMap":300},[304,3205,3206,3208,3210,3212,3214,3216],{"class":306,"line":307},[304,3207,2120],{"class":421},[304,3209,432],{"class":431},[304,3211,524],{"class":421},[304,3213,270],{"class":351},[304,3215,3197],{"class":440},[304,3217,3057],{"class":351},[304,3219,3220,3222,3224,3226],{"class":306,"line":355},[304,3221,359],{"class":358},[304,3223,2064],{"class":313},[304,3225,470],{"class":358},[304,3227,739],{"class":351},[304,3229,3230,3232,3234,3237,3240,3243,3245,3248,3250,3252,3254,3256,3259],{"class":306,"line":368},[304,3231,3149],{"class":431},[304,3233,1507],{"class":351},[304,3235,3236],{"class":310}," any",[304,3238,3239],{"class":351}," {",[304,3241,3242],{"class":347}," return",[304,3244,893],{"class":431},[304,3246,3247],{"class":310},"RateLimits",[304,3249,455],{"class":351},[304,3251,2471],{"class":421},[304,3253,461],{"class":351},[304,3255,2040],{"class":1199},[304,3257,3258],{"class":351},"}",[304,3260,3261],{"class":351}," },\n",[304,3263,3264,3267],{"class":306,"line":378},[304,3265,3266],{"class":421},"    user",[304,3268,739],{"class":351},[304,3270,3271,3273,3275,3277,3279,3281],{"class":306,"line":384},[304,3272,3149],{"class":431},[304,3274,444],{"class":351},[304,3276,2410],{"class":1447},[304,3278,3236],{"class":310},[304,3280,1458],{"class":351},[304,3282,491],{"class":351},[304,3284,3285,3288,3290,3293,3296,3299,3301],{"class":306,"line":394},[304,3286,3287],{"class":421},"        limits ",[304,3289,432],{"class":431},[304,3291,3292],{"class":421}," target",[304,3294,3295],{"class":351},".(",[304,3297,3298],{"class":431},"*",[304,3300,3247],{"class":310},[304,3302,397],{"class":351},[304,3304,3305,3307,3309,3311,3313,3315,3318,3321,3324,3326,3328,3331,3333,3335],{"class":306,"line":400},[304,3306,3164],{"class":421},[304,3308,270],{"class":351},[304,3310,601],{"class":440},[304,3312,444],{"class":351},[304,3314,470],{"class":358},[304,3316,3317],{"class":313},"rate-limits is now ",[304,3319,3320],{"class":611},"%d",[304,3322,3323],{"class":313}," per minute",[304,3325,470],{"class":358},[304,3327,425],{"class":351},[304,3329,3330],{"class":421}," limits",[304,3332,270],{"class":351},[304,3334,2471],{"class":421},[304,3336,397],{"class":351},[304,3338,3339],{"class":306,"line":405},[304,3340,1239],{"class":351},[304,3342,3343],{"class":306,"line":412},[304,3344,397],{"class":351},[260,3346,3347],{},"Each watch returns a function that cancels it. Calling it twice is harmless.",[255,3349,3351],{"id":3350},"other-useful-client-features","Other useful client features",[260,3353,3354],{},"The client provides additional methods.",[659,3356,3358],{"id":3357},"ready",[267,3359,656],{},[260,3361,3362,3363,3365,3366,3368],{},"Returns a boolean indicating if the client has received config state and is ready to evaluate configs. It is initially ",[267,3364,1789],{}," and becomes ",[267,3367,1785],{}," once the first config state arrives. Until it does, every read returns its in-code default value.",[659,3370,3372],{"id":3371},"closed",[267,3373,3374],{},"Closed",[260,3376,3377],{},"Returns a boolean indicating if the client has been closed. A closed client cannot be reopened.",[659,3379,3381],{"id":3380},"allconfigs",[267,3382,3383],{},"AllConfigs",[260,3385,3386,3387,3390,3391,2294,3393,2294,3395,2294,3398,3400,3401,3403],{},"Returns the evaluated state of every config the client holds as a ",[267,3388,3389],{},"[]ConfigState"," sorted by key, each with the config's ",[267,3392,1866],{},[267,3394,1629],{},[267,3396,3397],{},"Type",[267,3399,1638],{}," as the server spelled it, and ",[267,3402,2297],{},". It takes the user context to evaluate for and, optionally, the config keys to restrict the result to. It is empty until the client is ready.",[260,3405,3406,3407,3409],{},"This is intended for handing state to a client SDK to hydrate with. It records no telemetry and reaches no ",[267,3408,1834],{}," handler, since the SDK that receives the state reports its own evaluations.",[659,3411,3413],{"id":3412},"unwatchall",[267,3414,3415],{},"UnwatchAll",[260,3417,3418,3419,2294,3421,1828,3423,3425],{},"Cancels every watch on every config. Handlers registered with ",[267,3420,1673],{},[267,3422,1437],{},[267,3424,1834],{}," stay.",[659,3427,3429],{"id":3428},"close",[267,3430,529],{},[260,3432,3433,3434,3436,3437,3439],{},"Closes the connection to ConfigDirector services, reports whatever telemetry is pending, and cancels every watch and event handler. After ",[267,3435,529],{}," the client is not ready and every read returns its in-code default value. Calling ",[267,3438,529],{}," more than once is harmless and returns the first call's result.",[260,3441,3442],{},"Only close when your application shuts down and it will no longer make use of the client.",[634,3444,3445],{},"Never create a client inside a request handler. Each one opens its own connection to ConfigDirector, starts out not ready so it serves the in-code default values, and keeps connecting in the background for as long as it lives.",[255,3447,3449],{"id":3448},"test-your-code","Test your code",[260,3451,3452,3453,3456,3457,3461],{},"Use the SDK's ",[267,3454,3455],{},"configdirectortest"," package to test the code that reads your configs and flags. It creates a ",[3458,3459,3460],"strong",{},"test client",": the SDK's real client wired to an in-memory connection that your test controls. Only the connection to ConfigDirector and the telemetry are replaced. Everything else is the same code that runs in production, so the code under test behaves exactly as it does against ConfigDirector, and no network connection is opened, no telemetry is sent, and no goroutine is started. The package ships in the module, so there is nothing else to install.",[295,3463,3466],{"className":338,"code":3464,"filename":3465,"language":248,"meta":300,"style":300},"import (\n    \"testing\"\n\n    \"github.com\u002FConfigDirector\u002Fgo-sdks\u002Fconfigdirector\u002Fconfigdirectortest\"\n    \"github.com\u002Fstretchr\u002Ftestify\u002Frequire\"\n)\n\nfunc TestNewCheckout(t *testing.T) {\n    testClient := configdirectortest.NewTestClient(map[string]any{\"new-checkout\": true, \"max-items\": 20})\n    t.Cleanup(func() { require.NoError(t, testClient.Close()) })\n    service := NewCheckoutService(testClient.Client())\n\n    require.True(t, service.IsNewCheckoutEnabled(\"user-123\"))\n\n    require.NoError(t, testClient.SetValue(\"new-checkout\", false))\n    require.False(t, service.IsNewCheckoutEnabled(\"user-123\"))\n}\n","checkout_service_test.go",[267,3467,3468,3474,3483,3487,3496,3505,3509,3513,3538,3595,3640,3663,3667,3703,3707,3742,3773],{"__ignoreMap":300},[304,3469,3470,3472],{"class":306,"line":307},[304,3471,348],{"class":347},[304,3473,352],{"class":351},[304,3475,3476,3478,3481],{"class":306,"line":355},[304,3477,359],{"class":358},[304,3479,3480],{"class":310},"testing",[304,3482,365],{"class":358},[304,3484,3485],{"class":306,"line":368},[304,3486,381],{"emptyLinePlaceholder":54},[304,3488,3489,3491,3494],{"class":306,"line":378},[304,3490,359],{"class":358},[304,3492,3493],{"class":310},"github.com\u002FConfigDirector\u002Fgo-sdks\u002Fconfigdirector\u002Fconfigdirectortest",[304,3495,365],{"class":358},[304,3497,3498,3500,3503],{"class":306,"line":384},[304,3499,359],{"class":358},[304,3501,3502],{"class":310},"github.com\u002Fstretchr\u002Ftestify\u002Frequire",[304,3504,365],{"class":358},[304,3506,3507],{"class":306,"line":394},[304,3508,397],{"class":351},[304,3510,3511],{"class":306,"line":400},[304,3512,381],{"emptyLinePlaceholder":54},[304,3514,3515,3517,3520,3522,3525,3527,3529,3531,3534,3536],{"class":306,"line":405},[304,3516,1442],{"class":431},[304,3518,3519],{"class":440}," TestNewCheckout",[304,3521,444],{"class":351},[304,3523,3524],{"class":1447},"t",[304,3526,1203],{"class":431},[304,3528,3480],{"class":310},[304,3530,270],{"class":351},[304,3532,3533],{"class":310},"T",[304,3535,1458],{"class":351},[304,3537,491],{"class":351},[304,3539,3540,3543,3545,3548,3550,3553,3555,3558,3560,3562,3564,3566,3568,3570,3572,3574,3576,3579,3581,3583,3586,3588,3590,3593],{"class":306,"line":412},[304,3541,3542],{"class":421},"    testClient ",[304,3544,432],{"class":431},[304,3546,3547],{"class":421}," configdirectortest",[304,3549,270],{"class":351},[304,3551,3552],{"class":440},"NewTestClient",[304,3554,444],{"class":351},[304,3556,3557],{"class":431},"map",[304,3559,2016],{"class":351},[304,3561,2020],{"class":2019},[304,3563,2023],{"class":351},[304,3565,2026],{"class":310},[304,3567,455],{"class":351},[304,3569,470],{"class":358},[304,3571,1980],{"class":313},[304,3573,470],{"class":358},[304,3575,461],{"class":351},[304,3577,3578],{"class":1987}," true",[304,3580,425],{"class":351},[304,3582,464],{"class":358},[304,3584,3585],{"class":313},"max-items",[304,3587,470],{"class":358},[304,3589,461],{"class":351},[304,3591,3592],{"class":1199}," 20",[304,3594,473],{"class":351},[304,3596,3597,3600,3602,3605,3607,3609,3611,3613,3616,3618,3621,3623,3625,3627,3630,3632,3634,3637],{"class":306,"line":418},[304,3598,3599],{"class":421},"    t",[304,3601,270],{"class":351},[304,3603,3604],{"class":440},"Cleanup",[304,3606,444],{"class":351},[304,3608,1442],{"class":431},[304,3610,1507],{"class":351},[304,3612,3239],{"class":351},[304,3614,3615],{"class":421}," require",[304,3617,270],{"class":351},[304,3619,3620],{"class":440},"NoError",[304,3622,444],{"class":351},[304,3624,3524],{"class":421},[304,3626,425],{"class":351},[304,3628,3629],{"class":421}," testClient",[304,3631,270],{"class":351},[304,3633,529],{"class":440},[304,3635,3636],{"class":351},"())",[304,3638,3639],{"class":351}," })\n",[304,3641,3642,3645,3647,3650,3652,3655,3657,3660],{"class":306,"line":476},[304,3643,3644],{"class":421},"    service ",[304,3646,432],{"class":431},[304,3648,3649],{"class":440}," NewCheckoutService",[304,3651,444],{"class":351},[304,3653,3654],{"class":421},"testClient",[304,3656,270],{"class":351},[304,3658,3659],{"class":440},"Client",[304,3661,3662],{"class":351},"())\n",[304,3664,3665],{"class":306,"line":494},[304,3666,381],{"emptyLinePlaceholder":54},[304,3668,3669,3672,3674,3677,3679,3681,3683,3686,3688,3691,3693,3695,3698,3700],{"class":306,"line":512},[304,3670,3671],{"class":421},"    require",[304,3673,270],{"class":351},[304,3675,3676],{"class":440},"True",[304,3678,444],{"class":351},[304,3680,3524],{"class":421},[304,3682,425],{"class":351},[304,3684,3685],{"class":421}," service",[304,3687,270],{"class":351},[304,3689,3690],{"class":440},"IsNewCheckoutEnabled",[304,3692,444],{"class":351},[304,3694,470],{"class":358},[304,3696,3697],{"class":313},"user-123",[304,3699,470],{"class":358},[304,3701,3702],{"class":351},"))\n",[304,3704,3705],{"class":306,"line":518},[304,3706,381],{"emptyLinePlaceholder":54},[304,3708,3709,3711,3713,3715,3717,3719,3721,3723,3725,3728,3730,3732,3734,3736,3738,3740],{"class":306,"line":535},[304,3710,3671],{"class":421},[304,3712,270],{"class":351},[304,3714,3620],{"class":440},[304,3716,444],{"class":351},[304,3718,3524],{"class":421},[304,3720,425],{"class":351},[304,3722,3629],{"class":421},[304,3724,270],{"class":351},[304,3726,3727],{"class":440},"SetValue",[304,3729,444],{"class":351},[304,3731,470],{"class":358},[304,3733,1980],{"class":313},[304,3735,470],{"class":358},[304,3737,425],{"class":351},[304,3739,1988],{"class":1987},[304,3741,3702],{"class":351},[304,3743,3744,3746,3748,3751,3753,3755,3757,3759,3761,3763,3765,3767,3769,3771],{"class":306,"line":540},[304,3745,3671],{"class":421},[304,3747,270],{"class":351},[304,3749,3750],{"class":440},"False",[304,3752,444],{"class":351},[304,3754,3524],{"class":421},[304,3756,425],{"class":351},[304,3758,3685],{"class":421},[304,3760,270],{"class":351},[304,3762,3690],{"class":440},[304,3764,444],{"class":351},[304,3766,470],{"class":358},[304,3768,3697],{"class":313},[304,3770,470],{"class":358},[304,3772,3702],{"class":351},[304,3774,3775],{"class":306,"line":546},[304,3776,515],{"class":351},[260,3778,3779,3782,3783,3786,3787,3789,3790,3793,3794,3797,3798,3801,3802,3804],{},[267,3780,3781],{},"testClient.Client()"," is a ",[267,3784,3785],{},"*configdirector.Client",", so it goes anywhere your code accepts one. It is ready when ",[267,3788,3552],{}," returns, with the values delivered as its first update, so the code under test can read at once; ",[267,3791,3792],{},"HoldInitialization()"," keeps it not ready instead, for testing what your code does before config state arrives. Pass the client to the code under test as a parameter, as ",[267,3795,3796],{},"NewCheckoutService"," does above, rather than reaching for a package-level one: that is what lets the test hand it a client it controls. The examples use ",[267,3799,3800],{},"require"," from the testify module; the standard ",[267,3803,3480],{}," package works just as well.",[659,3806,3808],{"id":3807},"values","Values",[260,3810,3811,2294,3813,1828,3815,3818,3819,3822,3823,3825,3826,1012,3829,3832,3833,3835,3836,3838,3839,3841,3842,3845,3846,3848,3849,1828,3851,3853,3854,3856],{},[267,3812,3552],{},[267,3814,3727],{},[267,3816,3817],{},"ReplaceValues"," take Go values, and the config type follows from each value: a ",[267,3820,3821],{},"bool","; any integer kind, ",[267,3824,1034],{}," included (an integer config); a ",[267,3827,3828],{},"float32",[267,3830,3831],{},"float64"," (a float config); a ",[267,3834,2020],{},"; or a map, slice, array or struct (a JSON config holding what ",[267,3837,2414],{}," writes for it). A ",[267,3840,2020],{}," is always a string config, so JSON text meant as a JSON config is given as a ",[267,3843,3844],{},"json.RawMessage",". Every value is served as an unconditional config, so every user context receives the same value; a test that needs different values per context is written as one test per value. A ",[267,3847,644],{}," value, a blank key, a non-finite number, a value that does not encode as JSON, or a value of any other kind, such as a pointer or a channel, is an error from ",[267,3850,3727],{},[267,3852,3817],{}," and a panic from ",[267,3855,3552],{},"; in every case nothing changes.",[260,3858,3859,3860,3863,3864,3866,3867,3869,3870,3863,3873,3875,3876,3879,3880,3883,3884,3887,3888,3890,3891,3893],{},"Reads behave as they do in production: ",[267,3861,3862],{},"SetValue(\"k\", true)"," read with ",[267,3865,1935],{}," returns the in-code default value with the ",[267,3868,1819],{}," reason, ",[267,3871,3872],{},"SetValue(\"k\", 2.5)",[267,3874,1900],{}," returns the default with the ",[267,3877,3878],{},"invalid-number"," reason, and ",[267,3881,3882],{},"SetValue(\"k\", \"\")"," serves the in-code default value with the ",[267,3885,3886],{},"value-missing"," reason. The ",[267,3889,2282],{}," methods and ",[267,3892,1834],{}," handlers report the reason.",[659,3895,3897],{"id":3896},"controls","Controls",[3899,3900,3901,3914],"table",{},[3902,3903,3904],"thead",{},[3905,3906,3907,3911],"tr",{},[3908,3909,3910],"th",{},"Control",[3908,3912,3913],{},"Effect",[3915,3916,3917,3937,3963,3973,3989],"tbody",{},[3905,3918,3919,3925],{},[3920,3921,3922],"td",{},[267,3923,3924],{},"SetValue(key, value)",[3920,3926,3927,3928,3931,3932,2294,3934,3936],{},"Stores the value and, once the client has received its first update, delivers an update carrying only ",[267,3929,3930],{},"key",". Watches of ",[267,3933,3930],{},[267,3935,1437],{}," handlers and reads see it before the call returns.",[3905,3938,3939,3944],{},[3920,3940,3941],{},[267,3942,3943],{},"RemoveValue(key)",[3920,3945,3946,3947,3950,3951,3953,3954,3956,3957,3959,3960,270],{},"Removes the value and delivers a full update without it. Reads return the in-code default value with the ",[267,3948,3949],{},"config-state-missing"," reason, watches of ",[267,3952,3930],{}," receive the default, and ",[267,3955,1437],{}," lists ",[267,3958,3930],{}," in ",[267,3961,3962],{},"RemovedKeys",[3905,3964,3965,3970],{},[3920,3966,3967],{},[267,3968,3969],{},"ReplaceValues(values)",[3920,3971,3972],{},"Replaces every stored value and delivers a full update. Use it to reset a test client shared across tests.",[3905,3974,3975,3980],{},[3920,3976,3977],{},[267,3978,3979],{},"CompleteInitialization()",[3920,3981,3982,3983,3985,3986,3988],{},"Delivers the stored values to a client built with ",[267,3984,3792],{},", on the calling goroutine, so the client is ready and a blocked ",[267,3987,571],{}," has returned when the call returns. On a ready client it does nothing.",[3905,3990,3991,3996],{},[3920,3992,3993],{},[267,3994,3995],{},"FailInitialization()",[3920,3997,3998,3999,4002,4003,4005,4006,4008],{},"Fails the held initialization the way a refused SDK key does: the failure is logged at error level and the client stays not ready, so reads return the in-code default value with the ",[267,4000,4001],{},"client-not-ready"," reason and a ",[267,4004,571],{}," in flight ends with its timeout or its context. ",[267,4007,3979],{}," still delivers the stored values afterwards. On a ready client it does nothing.",[295,4010,4012],{"className":338,"code":4011,"filename":3465,"language":248,"meta":300,"style":300},"import \"time\"\n\ntestClient := configdirectortest.NewTestClient(\n    map[string]any{\"new-checkout\": true},\n    configdirectortest.HoldInitialization(),\n    configdirectortest.WithTimeout(500*time.Millisecond),\n)\nt.Cleanup(func() { require.NoError(t, testClient.Close()) })\nservice := NewCheckoutService(testClient.Client())\ntestClient.FailInitialization()\n\nrequire.False(t, testClient.Client().Ready())\nrequire.False(t, service.IsNewCheckoutEnabled(\"user-123\"))\n",[267,4013,4014,4024,4028,4043,4070,4083,4109,4113,4151,4170,4181,4185,4212],{"__ignoreMap":300},[304,4015,4016,4018,4020,4022],{"class":306,"line":307},[304,4017,348],{"class":347},[304,4019,464],{"class":358},[304,4021,1112],{"class":310},[304,4023,365],{"class":358},[304,4025,4026],{"class":306,"line":355},[304,4027,381],{"emptyLinePlaceholder":54},[304,4029,4030,4033,4035,4037,4039,4041],{"class":306,"line":368},[304,4031,4032],{"class":421},"testClient ",[304,4034,432],{"class":431},[304,4036,3547],{"class":421},[304,4038,270],{"class":351},[304,4040,3552],{"class":440},[304,4042,3057],{"class":351},[304,4044,4045,4048,4050,4052,4054,4056,4058,4060,4062,4064,4066,4068],{"class":306,"line":378},[304,4046,4047],{"class":431},"    map",[304,4049,2016],{"class":351},[304,4051,2020],{"class":2019},[304,4053,2023],{"class":351},[304,4055,2026],{"class":310},[304,4057,455],{"class":351},[304,4059,470],{"class":358},[304,4061,1980],{"class":313},[304,4063,470],{"class":358},[304,4065,461],{"class":351},[304,4067,3578],{"class":1987},[304,4069,783],{"class":351},[304,4071,4072,4075,4077,4080],{"class":306,"line":384},[304,4073,4074],{"class":421},"    configdirectortest",[304,4076,270],{"class":351},[304,4078,4079],{"class":440},"HoldInitialization",[304,4081,4082],{"class":351},"(),\n",[304,4084,4085,4087,4089,4092,4094,4097,4099,4101,4103,4106],{"class":306,"line":394},[304,4086,4074],{"class":421},[304,4088,270],{"class":351},[304,4090,4091],{"class":440},"WithTimeout",[304,4093,444],{"class":351},[304,4095,4096],{"class":1199},"500",[304,4098,3298],{"class":431},[304,4100,1112],{"class":421},[304,4102,270],{"class":351},[304,4104,4105],{"class":421},"Millisecond",[304,4107,4108],{"class":351},"),\n",[304,4110,4111],{"class":306,"line":400},[304,4112,397],{"class":351},[304,4114,4115,4117,4119,4121,4123,4125,4127,4129,4131,4133,4135,4137,4139,4141,4143,4145,4147,4149],{"class":306,"line":405},[304,4116,3524],{"class":421},[304,4118,270],{"class":351},[304,4120,3604],{"class":440},[304,4122,444],{"class":351},[304,4124,1442],{"class":431},[304,4126,1507],{"class":351},[304,4128,3239],{"class":351},[304,4130,3615],{"class":421},[304,4132,270],{"class":351},[304,4134,3620],{"class":440},[304,4136,444],{"class":351},[304,4138,3524],{"class":421},[304,4140,425],{"class":351},[304,4142,3629],{"class":421},[304,4144,270],{"class":351},[304,4146,529],{"class":440},[304,4148,3636],{"class":351},[304,4150,3639],{"class":351},[304,4152,4153,4156,4158,4160,4162,4164,4166,4168],{"class":306,"line":412},[304,4154,4155],{"class":421},"service ",[304,4157,432],{"class":431},[304,4159,3649],{"class":440},[304,4161,444],{"class":351},[304,4163,3654],{"class":421},[304,4165,270],{"class":351},[304,4167,3659],{"class":440},[304,4169,3662],{"class":351},[304,4171,4172,4174,4176,4179],{"class":306,"line":418},[304,4173,3654],{"class":421},[304,4175,270],{"class":351},[304,4177,4178],{"class":440},"FailInitialization",[304,4180,532],{"class":351},[304,4182,4183],{"class":306,"line":476},[304,4184,381],{"emptyLinePlaceholder":54},[304,4186,4187,4189,4191,4193,4195,4197,4199,4201,4203,4205,4208,4210],{"class":306,"line":494},[304,4188,3800],{"class":421},[304,4190,270],{"class":351},[304,4192,3750],{"class":440},[304,4194,444],{"class":351},[304,4196,3524],{"class":421},[304,4198,425],{"class":351},[304,4200,3629],{"class":421},[304,4202,270],{"class":351},[304,4204,3659],{"class":440},[304,4206,4207],{"class":351},"().",[304,4209,656],{"class":440},[304,4211,3662],{"class":351},[304,4213,4214,4216,4218,4220,4222,4224,4226,4228,4230,4232,4234,4236,4238,4240],{"class":306,"line":512},[304,4215,3800],{"class":421},[304,4217,270],{"class":351},[304,4219,3750],{"class":440},[304,4221,444],{"class":351},[304,4223,3524],{"class":421},[304,4225,425],{"class":351},[304,4227,3685],{"class":421},[304,4229,270],{"class":351},[304,4231,3690],{"class":440},[304,4233,444],{"class":351},[304,4235,470],{"class":358},[304,4237,3697],{"class":313},[304,4239,470],{"class":358},[304,4241,3702],{"class":351},[659,4243,452],{"id":4244},"options",[260,4246,4247,4250,4251,4253,4254,4256,4257,4259,4260,4263,4264,4266,4267,4270,4271,4274,4275,4277,4278,4280],{},[267,4248,4249],{},"NewTestClient(values, options...)"," takes ",[267,4252,3792],{},", described above; ",[267,4255,4091],{},", the client's connection timeout, which bounds how long a held ",[267,4258,571],{}," waits and defaults to the SDK's production timeout; ",[267,4261,4262],{},"WithLogger",", which defaults to ",[267,4265,803],{}," as for a production client; and ",[267,4268,4269],{},"WithHooks",", handlers attached before the first update as ",[267,4272,4273],{},"Options.Hooks"," attaches them, so an ",[267,4276,1437],{}," hook sees the values arrive and an ",[267,4279,1673],{}," hook sees the client become ready.",[659,4282,4284],{"id":4283},"what-to-expect","What to expect",[997,4286,4287,4303,4318,4327,4335,4341],{},[326,4288,4289,4290,2294,4292,2294,4294,2294,4297,1012,4299,4302],{},"Watches and handlers run on the goroutine that calls ",[267,4291,3552],{},[267,4293,3727],{},[267,4295,4296],{},"RemoveValue",[267,4298,3817],{},[267,4300,4301],{},"CompleteInitialization",", not on a connection goroutine, so an assertion can follow the call directly. A change made from inside a handler is delivered after the update being handled, as it would be by the single connection goroutine in production.",[326,4304,4305,4306,4308,4309,4311,4312,4314,4315,4317],{},"The first update fires ",[267,4307,1437],{}," before ",[267,4310,1673],{},", as in production. With ",[267,4313,3792],{},", handlers and watches registered before ",[267,4316,4301],{}," see it.",[326,4319,4320,4321,4323,4324,4326],{},"A held ",[267,4322,571],{}," that times out leaves the client not ready, and ",[267,4325,4301],{}," afterwards still makes it ready, as production does when config state arrives after the wait.",[326,4328,4329,4330,1828,4332,4334],{},"Watches fire on every update carrying their key, whether or not the value changed, as in production. ",[267,4331,4296],{},[267,4333,3817],{}," deliver a full update, so they also fire the watches of every remaining key.",[326,4336,4337,4338,4340],{},"A panic in a watch or handler is recovered and logged, as it is in production. A failed ",[267,4339,3800],{}," assertion inside a handler ends the test as it does anywhere else, because the handler runs on the test's goroutine.",[326,4342,4343,4344,4346,4347,4349],{},"After ",[267,4345,529],{},", reads return the in-code default value with the ",[267,4348,4001],{}," reason, every handler and watch is cancelled, the controls store without delivering, and no goroutine is left running, because none was started.",[4351,4352,4353],"style",{},"html pre.shiki code .sbgvK, html code.shiki .sbgvK{--shiki-light:#E2931D;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s_sjI, html code.shiki .s_sjI{--shiki-light:#91B859;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sVHd0, html code.shiki .sVHd0{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#D73A49;--shiki-default-font-style:inherit;--shiki-dark:#F97583;--shiki-dark-font-style:inherit}html pre.shiki code .sP7_E, html code.shiki .sP7_E{--shiki-light:#39ADB5;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sjJ54, html code.shiki .sjJ54{--shiki-light:#39ADB5;--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sutJx, html code.shiki .sutJx{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#6A737D;--shiki-default-font-style:inherit;--shiki-dark:#6A737D;--shiki-dark-font-style:inherit}html pre.shiki code .su5hD, html code.shiki .su5hD{--shiki-light:#90A4AE;--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .smGrS, html code.shiki .smGrS{--shiki-light:#39ADB5;--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sGLFI, html code.shiki .sGLFI{--shiki-light:#6182B8;--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .s39Yj, html code.shiki .s39Yj{--shiki-light:#39ADB5;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .swQdS, html code.shiki .swQdS{--shiki-light:#E53935;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .srdBf, html code.shiki .srdBf{--shiki-light:#F76D47;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s99_P, html code.shiki .s99_P{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#E36209;--shiki-default-font-style:inherit;--shiki-dark:#FFAB70;--shiki-dark-font-style:inherit}html pre.shiki code .syTEX, html code.shiki .syTEX{--shiki-light:#FF5370;--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sbsja, html code.shiki .sbsja{--shiki-light:#9C3EDA;--shiki-default:#D73A49;--shiki-dark:#F97583}",{"title":300,"searchDepth":355,"depth":355,"links":4355},[4356,4357,4358,4362,4367,4374],{"id":257,"depth":355,"text":258},{"id":276,"depth":355,"text":277},{"id":320,"depth":355,"text":321,"children":4359},[4360,4361],{"id":661,"depth":368,"text":662},{"id":1411,"depth":368,"text":1412},{"id":1838,"depth":355,"text":1839,"children":4363},[4364,4365,4366],{"id":2401,"depth":368,"text":2402},{"id":2549,"depth":368,"text":2550},{"id":3003,"depth":368,"text":3004},{"id":3350,"depth":355,"text":3351,"children":4368},[4369,4370,4371,4372,4373],{"id":3357,"depth":368,"text":656},{"id":3371,"depth":368,"text":3374},{"id":3380,"depth":368,"text":3383},{"id":3412,"depth":368,"text":3415},{"id":3428,"depth":368,"text":529},{"id":3448,"depth":355,"text":3449,"children":4375},[4376,4377,4378,4379],{"id":3807,"depth":368,"text":3808},{"id":3896,"depth":368,"text":3897},{"id":4244,"depth":368,"text":452},{"id":4283,"depth":368,"text":4284},"Evaluate configs and feature flags locally for any user context with the Go server SDK, with updates delivered over server sent events","md",null,{"modifiedAt":4384},"2026-10-11T00:51:13.000Z",{"title":172,"icon":175},{"title":241,"description":4380},"p1czVSQGPj6lmp8fQEet3Kis1CG5OgEYodlSOax7v68",[4389,4391],{"title":167,"path":168,"stem":169,"description":4390,"icon":170,"children":-1},"Evaluate configs and feature flags locally for any user context with the .NET server SDK, with updates delivered over server sent events",{"title":183,"path":184,"stem":185,"description":4392,"icon":178,"icons":4393,"children":-1},"ConfigDirector Provider for the OpenFeature Web SDK",[178,93],1791697530064]