1<!DOCTYPE html> 2<html lang="en-us" dir="ltr"> 3 <head><meta charset='utf-8'> 4<meta name='viewport' content='width=device-width, initial-scale=1'><meta name='description' content=""> 5<title>Search</title> 6 7<link rel='canonical' href='https://roosma.dev/search/'> 8 9<link rel="stylesheet" href="/scss/style.min.0304c6baf04e01a8fe70693791cb744d56a3578a3120a8796cefc66825aa39c7.css"><meta property='og:title' content="Search"> 10<meta property='og:description' content=""> 11<meta property='og:url' content='https://roosma.dev/search/'> 12<meta property='og:site_name' content='Insights in Coding with Duncan'> 13<meta property='og:type' content='article'><meta property='article:section' content='Page' /> 14<meta name="twitter:title" content="Search"> 15<meta name="twitter:description" content=""><link rel="alternate" type="application/json" href="https://roosma.dev/search/index.json"> 16 <link rel="shortcut icon" href="/favicon.png" /> 17 18 19 20<link rel="preload" href="/search/index.json" as="fetch" crossorigin="anonymous"> 21 </head> 22 <body class="template-search"> 23
23<script> 24 (function() { 25 const colorSchemeKey = 'StackColorScheme'; 26 if(!localStorage.getItem(colorSchemeKey)){ 27 localStorage.setItem(colorSchemeKey, "auto"); 28 } 29 })(); 30 </script>
30<script> 31 (function() { 32 const colorSchemeKey = 'StackColorScheme'; 33 const colorSchemeItem = localStorage.getItem(colorSchemeKey); 34 const supportDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches === true; 35 36 if (colorSchemeItem == 'dark' || colorSchemeItem === 'auto' && supportDarkMode) { 37 38 39 document.documentElement.dataset.scheme = 'dark'; 40 } else { 41 document.documentElement.dataset.scheme = 'light'; 42 } 43 })(); 44</script>
44 45<div class="container main-container flex on-phone--column extended"><aside class="sidebar left-sidebar sticky "> 46 <button class="hamburger hamburger--spin" type="button" id="toggle-menu" aria-label="Toggle Menu"> 47 <span class="hamburger-box"> 48 <span class="hamburger-inner"></span> 49 </span> 50 </button> 51 52 <header> 53 54 55 <figure class="site-avatar"> 56 <a href="/"> 57 58 59 60 61 62 <img src="/img/avatar_hubc40be1f072dad8c3a05446a61a4297a_42934_300x0_resize_box_3.png" width="300" 63 height="300" class="site-logo" loading="lazy" alt="Avatar"> 64 65 66 </a> 67 68 </figure> 69 70 71 72 <div class="site-meta"> 73 <h1 class="site-name"><a href="/">Insights in Coding with Duncan</a></h1> 74 <h2 class="site-description">Developer Opinions: Views from the Coding Trenches</h2> 75 </div> 76 </header><ol class="menu-social"> 77 78 <li> 79 <a 80 href='https://github.com/droosma' 81 target="_blank" 82 title="GitHub" 83 rel="me" 84 > 85 86 87 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-github" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> 88 <path stroke="none" d="M0 0h24v24H0z" fill="none"/> 89 <path d="M9 19c-4.3 1.4 -4.3 -2.5 -6 -3m12 5v-3.5c0 -1 .1 -1.4 -.5 -2c2.8 -.3 5.5 -1.4 5.5 -6a4.6 4.6 0 0 0 -1.3 -3.2a4.2 4.2 0 0 0 -.1 -3.2s-1.1 -.3 -3.5 1.3a12.3 12.3 0 0 0 -6.2 0c-2.4 -1.6 -3.5 -1.3 -3.5 -1.3a4.2 4.2 0 0 0 -.1 3.2a4.6 4.6 0 0 0 -1.3 3.2c0 4.6 2.7 5.7 5.5 6c-.6 .6 -.6 1.2 -.5 2v3.5" /> 90</svg> 91 92 93 94 95 </a> 96 </li> 97 98 <li> 99 <a 100 href='https://twitter.com/duncanroosma' 101 target="_blank" 102 title="Twitter" 103 rel="me" 104 > 105 106 107 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-brand-twitter" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> 108 <path stroke="none" d="M0 0h24v24H0z" fill="none"/> 109 <path d="M22 4.01c-1 .49 -1.98 .689 -3 .99c-1.121 -1.265 -2.783 -1.335 -4.38 -.737s-2.643 2.06 -2.62 3.737v1c-3.245 .083 -6.135 -1.395 -8 -4c0 0 -4.182 7.433 4 11c-1.872 1.247 -3.739 2.088 -6 2c3.308 1.803 6.913 2.423 10.034 1.517c3.58 -1.04 6.522 -3.723 7.651 -7.742a13.84 13.84 0 0 0 .497 -3.753c-.002 -.249 1.51 -2.772 1.818 -4.013z" /> 110</svg> 111 112 113 114 115 </a> 116 </li> 117 118 </ol><ol class="menu" id="main-menu"> 119 120 121 122 <li > 123 <a href='/' > 124 125 126 127 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-home" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> 128 <path stroke="none" d="M0 0h24v24H0z"/> 129 <polyline points="5 12 3 12 12 3 21 12 19 12" /> 130 <path d="M5 12v7a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-7" /> 131 <path d="M9 21v-6a2 2 0 0 1 2 -2h2a2 2 0 0 1 2 2v6" /> 132</svg> 133 134 135 136 137 <span>Home</span> 138 </a> 139 </li> 140 141 142 <li > 143 <a href='/archives/' > 144 145 146 147 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-archive" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> 148 <path stroke="none" d="M0 0h24v24H0z"/> 149 <rect x="3" y="4" width="18" height="4" rx="2" /> 150 <path d="M5 8v10a2 2 0 0 0 2 2h10a2 2 0 0 0 2 -2v-10" /> 151 <line x1="10" y1="12" x2="14" y2="12" /> 152</svg> 153 154 155 156
157 <span>Archives</span> 158 </a> 159 </li> 160 161 162 <li class='current' > 163 <a href='/search/' > 164 165 166 167 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-search" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> 168 <path stroke="none" d="M0 0h24v24H0z"/> 169 <circle cx="10" cy="10" r="7" /> 170 <line x1="21" y1="21" x2="15" y2="15" /> 171</svg> 172 173 174 175 176 <span>Search</span> 177 </a> 178 </li> 179 180 181 <li > 182 <a href='/manual/' > 183 184 185 186 <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> 187<!-- Uploaded to: SVG Repo, www.svgrepo.com, Transformed by: SVG Repo Mixer Tools --> 188<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 189�<g id="SVGRepo_bgCarrier" stroke-width="0"/> 190�<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"/> 191�<g id="SVGRepo_iconCarrier"> <path d="M3 10C3 6.22876 3 4.34315 4.17157 3.17157C5.34315 2 7.22876 2 11 2H13C16.7712 2 18.6569 2 19.8284 3.17157C21 4.34315 21 6.22876 21 10V14C21 17.7712 21 19.6569 19.8284 20.8284C18.6569 22 16.7712 22 13 22H11C7.22876 22 5.34315 22 4.17157 20.8284C3 19.6569 3 17.7712 3 14V10Z" stroke="#c0c0c0" stroke-width="1.5"/> <path d="M8 12H16" stroke="#c0c0c0" stroke-width="1.5" stroke-linecap="round"/> <path d="M8 8H16" stroke="#c0c0c0" stroke-width="1.5" stroke-linecap="round"/> <path d="M8 16H13" stroke="#c0c0c0" stroke-width="1.5" stroke-linecap="round"/> </g> 192�</svg> 193 194 <span>Manual</span> 195 </a> 196 </li> 197 198 <li class="menu-bottom-section"> 199 <ol class="menu"> 200 201 202 <li id="dark-mode-toggle"> 203 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-left" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> 204 <path stroke="none" d="M0 0h24v24H0z"/> 205 <circle cx="8" cy="12" r="2" /> 206 <rect x="2" y="6" width="20" height="12" rx="6" /> 207</svg> 208 209 210 211 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-toggle-right" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> 212 <path stroke="none" d="M0 0h24v24H0z"/> 213 <circle cx="16" cy="12" r="2" /> 214 <rect x="2" y="6" width="20" height="12" rx="6" /> 215</svg> 216 217 218 219 <span>Dark Mode</span> 220 </li> 221 222 </ol> 223 </li> 224 </ol> 225</aside> 226 227 <main class="main full-width"> 228<form action="/search/" class="search-form"data-json="/search/index.json"> 229 <p> 230 <label>Search</label> 231 <input name="keyword" placeholder="Type something..." /> 232 </p> 233 234 <button title="Search"> 235 <svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-search" width="24" height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none" stroke-linecap="round" stroke-linejoin="round"> 236 <path stroke="none" d="M0 0h24v24H0z"/> 237 <circle cx="10" cy="10" r="7" /> 238 <line x1="21" y1="21" x2="15" y2="15" /> 239</svg> 240 241 242 243 </button> 244</form> 245 246<div class="search-result"> 247 <h3 class="search-result--title section-title"></h3> 248 <div class="search-result--list article-list--compact"></div> 249</div> 250
251<script> 252 window.searchResultTitleTemplate = "#PAGES_COUNT pages (#TIME_SECONDS seconds)" 253</script>
253<script type="text/javascript" src="/ts/search.js" defer></script>
253 254 255<footer class="site-footer"> 256 <section class="copyright"> 257 © 258 259 2023 - 260 261 2025 Insights in Coding with Duncan 262 </section> 263 264 <section class="powerby"> 265 Theme <b><a href="https://github.com/CaiJimmy/hugo-theme-stack" target="_blank" rel="noopener" data-version="3.17.0">Stack</a></b> designed by <a href="https://jimmycai.com" target="_blank" rel="noopener">Jimmy</a> 266 </section> 267 268
268<script type="text/javascript"> 269 !(function (cfg){function e(){cfg.onInit&&cfg.onInit(i)}var S,u,D,t,n,i,C=window,x=document,w=C.location,I="script",b="ingestionendpoint",E="disableExceptionTracking",A="ai.device.";"instrumentationKey"[S="toLowerCase"](),u="crossOrigin",D="POST",t="appInsightsSDK",n=cfg.name||"appInsights",(cfg.name||C[t])&&(C[t]=n),i=C[n]||function(l){var d=!1,g=!1,f={initialize:!0,queue:[],sv:"7",version:2,config:l};function m(e,t){var n={},i="Browser";function a(e){e=""+e;return 1===e.length?"0"+e:e}return n[A+"id"]=i[S](),n[A+"type"]=i,n["ai.operation.name"]=w&&w.pathname||"_unknown_",n["ai.internal.sdkVersion"]="javascript:snippet_"+(f.sv||f.version),{time:(i=new Date).getUTCFullYear()+"-"+a(1+i.getUTCMonth())+"-"+a(i.getUTCDate())+"T"+a(i.getUTCHours())+":"+a(i.getUTCMinutes())+":"+a(i.getUTCSeconds())+"."+(i.getUTCMilliseconds()/1e3).toFixed(3).slice(2,5)+"Z",iKey:e,name:"Microsoft.ApplicationInsights."+e.replace(/-/g,"")+"."+t,sampleRate:100,tags:n,data:{baseData:{ver:2}},ver:4,seq:"1",aiDataContract:undefined}}var h=-1,v=0,y=["js.monitor.azure.com","js.cdn.applicationinsights.io","js.cdn.monitor.azure.com","js0.cdn.applicationinsights.io","js0.cdn.monitor.azure.com","js2.cdn.applicationinsights.io","js2.cdn.monitor.azure.com","az416426.vo.msecnd.net"],k=l.url||cfg.src;if(k){if((n=navigator)&&(~(n=(n.userAgent||"").toLowerCase()).indexOf("msie")||~n.indexOf("trident/"))&&~k.indexOf("ai.3")&&(k=k.replace(/(\/)(ai\.3\.)([^\d]*)$/,function(e,t,n){return t+"ai.2"+n})),!1!==cfg.cr)for(var e=0;e<y.length;e++)if(0<k.indexOf(y[e])){h=e;break}var i=function(e){var a,t,n,i,o,r,s,c,p,u;f.queue=[],g||(0<=h&&v+1<y.length?(a=(h+v+1)%y.length,T(k.replace(/^(.*\/\/)([\w\.]*)(\/.*)$/,function(e,t,n,i){return t+y[a]+i})),v+=1):(d=g=!0,o=k,c=(p=function(){var e,t={},n=l.connectionString;if(n)for(var i=n.split(";"),a=0;a<i.length;a++){var o=i[a].split("=");2===o.length&&(t[o[0][S]()]=o[1])}return t[b]||(e=(n=t.endpointsuffix)?t.location:null,t[b]="https://"+(e?e+".":"")+"dc."+(n||"services.visualstudio.com")),t}()).instrumentationkey||l.instrumentationKey||"",p=(p=p[b])?p+"/v2/track":l.endpointUrl,(u=[]).push((t="SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details)",n=o,r=p,(s=(i=m(c,"Exception")).data).baseType="ExceptionData",s.baseData.exceptions=[{typeName:"SDKLoadFailed",message:t.replace(/\./g,"-"),hasFullStack:!1,stack:t+"\nSnippet failed to load ["+n+"] -- Telemetry is disabled\nHelp Link: https://go.microsoft.com/fwlink/?linkid=2128109\nHost: "+(w&&w.pathname||"_unknown_")+"\nEndpoint: "+r,parsedStack:[]}],i)),u.push((s=o,t=p,(r=(n=m(c,"Message")).data).baseType="MessageData",(i=r.baseData).message='AI (Internal): 99 message:"'+("SDK LOAD Failure: Failed to load Application Insights SDK script (See stack for details) ("+s+")").replace(/\"/g,"")+'"',i.properties={endpoint:t},n)),o=u,c=p,JSON&&((r=C.fetch)&&!cfg.useXhr?r(c,{method:D,body:JSON.stringify(o),mode:"cors"}):XMLHttpRequest&&((s=new XMLHttpRequest).open(D,c),s.setRequestHeader("Content-type","application/json"),s.send(JSON.stringify(o))))))},a=function(e,t){g||setTimeout(function(){!t&&f.core||i()},500),d=!1},T=function(e){var n=x.createElement(I),e=(n.src=e,cfg[u]);return!e&&""!==e||"undefined"==n[u]||(n[u]=e),n.onload=a,n.onerror=i,n.onreadystatechange=function(e,t){"loaded"!==n.readyState&&"complete"!==n.readyState||a(0,t)},cfg.ld&&cfg.ld<0?x.getElementsByTagName("head")[0].appendChild(n):setTimeout(function(){x.getElementsByTagName(I)[0].parentNode.appendChild(n)},cfg.ld||0),n};T(k)}try{f.cookie=x.cookie}catch(p){}function t(e){for(;e.length;)!function(t){f[t]=function(){var e=arguments;d||f.queue.push(function(){f[t].apply(f,e)})}}(e.pop())}var r,s,n="track",o="TrackPage",c="TrackEvent",n=(t([n+"Event",n+"PageView",n+"Exception",n+"Trace",n+"DependencyData",n+"Metric",n+"PageViewPerformance","start"+o,"stop"+o,"start"+c,"stop"+c,"addTelemetryInitializer","setAuthenticatedUserContext","clearAuthenticatedUserContext","flush"]),f.SeverityLevel={Verbose:0,Information:1,Warning:2,Error:3,Critical:4},(l.extensionConfig||{}).ApplicationInsightsAnalytics||{});return!0!==l[E]&&!0!==n[E]&&(t(["_"+(r="onerror")]),s=C[r],C[r]=function(e,t,n,i,a){var o=s&&s(e,t,n,i,a);return!0!==o&&f["_"+r]({message:e,url:t,lineNumber:n,columnNumber:i,error:a,evt:C.event}),o},l.autoExceptionInstrumented=!0),f}(cfg.cfg),(C[n]=i).queue&&0===i.queue.length?(i.queue.push(e),i.trackPageView({})):e();})({ 270 src: "https://js.monitor.azure.com/scripts/b/ai.3.gbl.min.js", 271 272 273 274 crossOrigin: "anonymous", 275 276 277 cfg: { 278 connectionString: "InstrumentationKey=12fab97b-b1ea-48a1-9cee-66993623f610;IngestionEndpoint=https://westeurope-5.in.applicationinsights.azure.com/;LiveEndpoint=https://westeurope.livediagnostics.monitor.azure.com/;ApplicationId=29725583-859b-4729-ae05-9db0b75a917a" 279 }}); 280 </script>
280 281</footer> 282 283 284 </main> 285 </div> 286
286<script 287 src="https://cdn.jsdelivr.net/npm/[email protected]/dist/vibrant.min.js"integrity="sha256-awcR2jno4kI5X0zL8ex0vi2z+KMkF24hUW8WePSA9HM="crossorigin="anonymous" 288 289 > 290 </script>
290<script type="text/javascript" src="/ts/main.js" defer></script>
vendor: 1 bytes, line 290
290
291<script> 292 (function () { 293 const customFont = document.createElement('link'); 294 customFont.href = "https://fonts.googleapis.com/css2?family=Lato:wght@300;400;700&display=swap"; 295 296 customFont.type = "text/css"; 297 customFont.rel = "stylesheet"; 298 299 document.head.appendChild(customFont); 300 }()); 301</script>
301 302 303 </body> 304</html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.