<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <id>https://johnnyreilly.com/</id>
    <title>I CAN MAKE THIS WORK</title>
    <updated>2026-03-03T00:00:00.000Z</updated>
    <generator>https://github.com/jpmonette/feed</generator>
    <link rel="alternate" href="https://johnnyreilly.com/"/>
    <subtitle>The blog of John Reilly ❤️🌻</subtitle>
    <icon>https://johnnyreilly.com/favicon.ico</icon>
    <rights>Copyright © 2012 - 2026 John Reilly.</rights>
    <entry>
        <title type="html"><![CDATA[Yargs: statically typed builder commands]]></title>
        <id>https://johnnyreilly.com/yargs-statically-typed-builder-commands</id>
        <link href="https://johnnyreilly.com/yargs-statically-typed-builder-commands"/>
        <updated>2025-11-29T10:22:45.000Z</updated>
        <summary type="html"><![CDATA[This post demonstrates how to use Yargs to create statically typed commands with builders in TypeScript.]]></summary>
        <content type="html"><![CDATA[<p><a href="https://yargs.js.org/" target="_blank" rel="noopener noreferrer">Yargs</a> is a popular library for building command line interfaces in Node.js. And the name is just fabulous. Yargs provides a way to define commands, options, and arguments in a structured way. However, Yargs has been around for a long time and it the documentation makes little mention of TypeScript support.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Yargs: statically typed builder commands&amp;quot; with the Yargs and TypeScript logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-c815d0ea1f65621e92623c53e6b45afe.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>Whilst there is <a href="https://github.com/yargs/yargs/blob/main/docs/typescript.md" target="_blank" rel="noopener noreferrer">some documentation</a>, if you're building more involved command line interfaces with Yargs in TypeScript, you may find that you need to do a bit of extra work to get strong typing working well with commands that have builders. In this post, I'll demonstrate how to use Yargs to create statically typed commands with builders in TypeScript.</p>
<p>Before we start, I should say that I'm working with Yargs version 18.0.0 in this post. The type definitions come from Definitely Typed and the version is 17.0.35. However, there is no significant difference in the types between Yargs 17 and 18 and so the difference is not an issue.</p>
<p>As an aside, it's possibly worth mentioning that these days it's possible to go without third party libraries entirely to parse command line arguments thanks to features like <a href="https://nodejs.org/api/util.html#utilparseargsconfig" target="_blank" rel="noopener noreferrer"><code>parseArgs</code></a> which have been part of Node.js since version 18. However, Yargs remains a popular choice and is still widely used. I have no plans to replace Yargs in my existing projects just yet.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="getting-the-builders-in">Getting the builders in<a href="https://johnnyreilly.com/yargs-statically-typed-builder-commands#getting-the-builders-in" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Let's start with a simple example. Imagine we want to create a command line tool that has a number of commands. Each command has its own options, and we want to use builders to define those options. Here's how we might set that up with Yargs, first we have a main entry point:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> yargs </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'yargs'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> hideBin </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'yargs/helpers'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> mySimpleCommand </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'./commands/mySimpleCommand.js'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> _args </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">yargs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token function" style="color:rgb(250, 208, 0)">hideBin</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">process</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">argv</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">scriptName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'my-cli'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">option</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'verbose'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> alias</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'v'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'boolean'</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">demandCommand</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token number" style="color:rgb(255, 98, 140)">1</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Please specify a command'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">command</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">mySimpleCommand</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Add more commands here as needed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">help</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">argv</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>As we can see, this imports a single command called <code>mySimpleCommand</code>. Let's look at how that command is defined:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">*</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> yargs </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'yargs'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> mySimpleCommand</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> yargs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">CommandModule </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  command</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'simple-command'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  describe</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'This is a simple command example'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">builder</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">b</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    b</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">option</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'myOption'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        demandOption</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'An option for the simple command.'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">help</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">handler</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">argv</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> myOption </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> argv</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// myOption is `unknown` here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Implement your command logic here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>You can see we have a command called <code>simple-command</code> that has a single option called <code>myOption</code> in the <code>builder</code>. However, if we look at the <code>handler</code> function, we can see that <code>myOption</code> is of type <code>unknown</code>. This is because Yargs does not know the shape of the arguments that will be passed to the handler.</p>
<p>This is the problem we need to solve. Inside the handler, we want to have statically typed access to the options defined in the builder.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="statically-typing-command-builders">Statically typing command builders<a href="https://johnnyreilly.com/yargs-statically-typed-builder-commands#statically-typing-command-builders" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>To achieve strong typing, we can define an interface that describes the shape of the arguments for our command. We can then use this interface to type the <code>CommandModule</code>. Here's how we can modify the <code>mySimpleCommand</code> to achieve this:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">*</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> yargs </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'yargs'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Args</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  myOption</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> mySimpleCommand</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> yargs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">CommandModule</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token builtin" style="color:rgb(250, 208, 0)">unknown</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> Args</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  command</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'simple-command'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  describe</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'This is a simple command example'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">builder</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">b</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    b</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">option</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'myOption'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// this is important for strong typing</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        demandOption</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'An option for the simple command.'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">help</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">handler</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">argv</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> myOption </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> argv</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// myOption is now `string`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Implement your command logic here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>There's three things to note here:</p>
<ol>
<li>We've defined an <code>Args</code> interface that describes the shape of the arguments for the command.</li>
<li>We've updated the <code>CommandModule</code> type to use <code>Args</code> as the second type, which defines the return type of the builder.</li>
<li>In the <code>builder</code>, we've specified the type of <code>myOption</code> as <code>string</code>. This is crucial for strong typing to work correctly. Without this we will have compilation errors from TypeScript.</li>
</ol>
<p>Now we have statically typed access to <code>myOption</code> inside the handler. Yay!</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="sharing-options-among-commands-and-builders">Sharing options among commands and builders<a href="https://johnnyreilly.com/yargs-statically-typed-builder-commands#sharing-options-among-commands-and-builders" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>It's not unusual to have options that are shared among multiple commands. Imagine a common option that all commands need to use. How can we share that option definition among multiple commands while maintaining strong typing? We can achieve this by defining a shared interface for the common options and a function that adds those options to a builder. Here's how we can do that:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">SharedOptions</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  someSharedOption</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// other shared options could go here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token generic-function function" style="color:rgb(250, 208, 0)">getSharedOptions</span><span class="token generic-function generic class-name operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token generic-function generic class-name constant" style="color:rgb(250, 208, 0)">T</span><span class="token generic-function generic class-name operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">y</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> yargs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Argv</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token constant" style="color:rgb(250, 208, 0)">T</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> y</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">option</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'someSharedOption'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    demandOption</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// other shared options could go here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>Then, in our command files, we can import the interface and the function and use them like this:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">*</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> yargs </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'yargs'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">SharedOptions</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> getSharedOptions </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'./sharedOptions.js'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Args</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">extends</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">SharedOptions</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  myOption</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> mySimpleCommand</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> yargs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">CommandModule</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token builtin" style="color:rgb(250, 208, 0)">unknown</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> Args</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  command</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'simple-command'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  describe</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'This is a simple command example'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">builder</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">b</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token function" style="color:rgb(250, 208, 0)">getSharedOptions</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">b</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">option</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'myOption'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        demandOption</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'An option for the simple command.'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">help</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">handler</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">argv</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> myOption</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> someSharedOption </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> argv</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Now you have statically typed access to myOption and someSharedOption</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Implement your command logic here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>By following this pattern, we can create statically typed commands with builders in Yargs while also sharing common options among multiple commands.</p>
<p>It's a beautiful pattern that sparks joy in my soul. Happy coding!</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Azure DevOps: merging pull requests with conventional commits]]></title>
        <id>https://johnnyreilly.com/azure-devops-pull-requests-conventional-commits</id>
        <link href="https://johnnyreilly.com/azure-devops-pull-requests-conventional-commits"/>
        <updated>2025-08-29T08:12:25.000Z</updated>
        <summary type="html"><![CDATA[How to merge a pull request in Azure DevOps and maintain a git commit history of conventional commits, using the Azure DevOps API and build validations.]]></summary>
        <content type="html"><![CDATA[<p>There was a time in my life when I didn't really care about commit messages. I would just write whatever I felt like, and it was fine. Over time, I learned that good commit messages are important for understanding the history of a project, especially when working in a team. And also, because I tend to forget what I've been working on surprisingly quickly.</p>
<p>There's also more technical reasons to care about commit messages. For example, if you're using a tool like <a href="https://semantic-release.gitbook.io/semantic-release/" target="_blank" rel="noopener noreferrer">semantic-release</a> to automate your release process, it relies on conventional commit messages to determine the next version number and generate release notes. It turns out that Azure DevOps has some challenges when it comes to maintaining a git commit history of conventional commits, especially when merging pull requests. By default, Azure DevOps uses a commit strategy that creates a merge commit with a message like "Merge PR 123: Title of pull request". This is acts against conventional commits.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Azure DevOps: merging pull requests with conventional commits&amp;quot; with an Azure DevOps logo" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-1ab82b471ea6afa313c1a6fb09361acd.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>You can use the UI to change the commit message when completing a pull request, but it's very easy to forget to do this. And if you're using squash merges, you lose the individual commit messages from the feature branch, which can be a problem if you're trying to maintain a history of conventional commits.</p>
<p>There is a way to bend Azure DevOps to our will; to allow us to control our commit messages. In this post, I'll show you how to do just that using the Azure DevOps API, some TypeScript and build validations. The fact this mechanism lives in a build validation means you cannot forget to set the commit message. That's the feature.</p>
<p>This post is not, in fact, specifically about using conventional commits. That's just a common use case. Rather this post is about being able to control the commit message when merging pull requests in Azure DevOps.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-approach">The approach<a href="https://johnnyreilly.com/azure-devops-pull-requests-conventional-commits#the-approach" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>The internet has been angry about Azure DevOps pull request commit messages for a while. There are <a href="https://developercommunity.visualstudio.com/t/change-default-title-for-pull-request-commits-to-n-1/365716" target="_blank" rel="noopener noreferrer">feature requests</a> which have been open since 2018 and <a href="https://stackoverflow.com/questions/55636169/how-to-change-pr-merge-commit-message" target="_blank" rel="noopener noreferrer">Stack Overflow questions</a> on the topic.</p>
<p>Azure DevOps very rarely gets new features these days, and so it's unlikely that we'll see any changes here. However, there is one avenue that is open to us. Azure DevOps has the ability for a pull request to be set to autocomplete, which means that it will automatically merge when all policies are satisfied. This is useful for ensuring that the pull request is merged without manual intervention once it meets the requirements. For example when build validations have passed, and the required reviewers have approved.</p>
<p>I've written about <a href="https://johnnyreilly.com/azure-devops-api-pull-requests-merge-set-autocomplete">merging pull requests and setting autocomplete with the Azure DevOps API</a> previously. We're going to build on that knowledge here, but add in the magic of setting the merge commit message when we set the pull request to autocomplete. This is achieved by the <a href="https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/update?view=azure-devops-rest-7.1#gitpullrequestcompletionoptions" target="_blank" rel="noopener noreferrer">pull requests API</a>. It allows us to update a pull request and set it autocomplete with a specific message commit message.</p>
<p>This should allow us to go from commits like this:</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of the default merge commit in the style Merge PR 123: Title of pull request" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-default-merge-commit-058f3fe14ef2ea0c570979557d453a8e.webp" width="674" height="110" class="img_ev3q"></p>
<p>To commits like this:</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of a merge commit with a conventional commit message" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-semantic-merge-commit-ebbefd8bb7c4c90912a4d84f9747aac1.webp" width="674" height="110" class="img_ev3q"></p>
<p>I should say that I'm using conventional commits as my commit message style, but you can use whatever style you like. The important thing is that you have control over the commit message.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-code">The code<a href="https://johnnyreilly.com/azure-devops-pull-requests-conventional-commits#the-code" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We're going to write a script that can be run in a build validation pipeline. This script will set the pull request to autocomplete with a specific merge commit message. This means that if you use conventional commits, you'll get to maintain a history of conventional commits in your git history.</p>
<p>Before we dive into the full code, here's the bit that does the magic of setting the merge commit message when setting the pull request to autocomplete:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> updateData </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  autoCompleteSetBy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> authenticatedUser</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  completionOptions</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    mergeStrategy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'squash'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    mergeCommitMessage</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'feat: conventional commit message'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// &lt;- set your commit message here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> response </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fetch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">https://dev.azure.com/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">org</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">project</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/_apis/git/repositories/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">repo</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/pullrequests/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">pullRequestId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">?api-version=7.1</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    method</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'PATCH'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    headers</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> defaultHeaders</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    body</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token constant" style="color:rgb(250, 208, 0)">JSON</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">stringify</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">updateData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>Here we are:</p>
<ul>
<li>Setting the <code>autoCompleteSetBy</code> property to the authenticated user. This is required when setting a pull request to autocomplete.</li>
<li>Setting the <code>completionOptions.mergeStrategy</code> to <code>squash</code>. <a href="https://learn.microsoft.com/en-us/rest/api/azure/devops/git/pull-requests/update?view=azure-devops-rest-7.1#gitpullrequestmergestrategy" target="_blank" rel="noopener noreferrer">You can change this to <code>rebase</code> or <code>noFastForward</code> if you prefer those strategies.</a></li>
<li>Setting the <code>completionOptions.mergeCommitMessage</code>. This is where we set our conventional commit message. Or if you wanted to use a different style, you could set it to whatever you like.</li>
</ul>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-full-typescript-script">The full TypeScript script<a href="https://johnnyreilly.com/azure-devops-pull-requests-conventional-commits#the-full-typescript-script" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>Now that we understand the principle, here's the full <code>set-autocomplete-and-commit-message.ts</code> script that you can use in your build validation pipeline:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> Buffer </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'buffer'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> parseArgs </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'util'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">LocationData</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  authenticatedUser</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> AuthenticatedUser</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">AuthenticatedUser</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  customDisplayName</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  providerDisplayName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getArgs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> values </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">parseArgs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    options</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      token</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'t'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Personal Access Token for Azure DevOps API'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'pr-id'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'i'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Pull Request ID'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      org</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'o'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Azure DevOps organization name'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      project</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'j'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Azure DevOps project name'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      repo</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'r'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Repository name'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> token </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">token</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> currentPullRequestId </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'pr-id'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> org </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">org</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token operator" style="color:rgb(255, 157, 0)">?.</span><span class="token function" style="color:rgb(250, 208, 0)">replace</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'https://dev.azure.com/'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">replace</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'/'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> project </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">project</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> repo </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">repo</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">token</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'PAT token must be provided using --token'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">currentPullRequestId</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Pull Request ID must be provided using --pr-id'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">org</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Organization must be provided using --org'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">project</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Project must be provided using --project'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">repo</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Repository must be provided using --repo'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> token</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> currentPullRequestId</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> org</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> project</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> repo </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAuthenticatedUser</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  org</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  defaultHeaders</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Record</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Fetching authenticated user info...'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> connectionDataResponse </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fetch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">https://dev.azure.com/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">org</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/_apis/ConnectionData?api-version=7.2-preview.1</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      method</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'GET'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      headers</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> defaultHeaders</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">connectionDataResponse</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorText </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> connectionDataResponse</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">text</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Failed to fetch connection data: HTTP </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">String</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation">connectionDataResponse</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">status</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">errorText</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> connectionData </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> connectionDataResponse</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">json</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> LocationData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> authenticatedUser </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> connectionData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">authenticatedUser</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">authenticatedUser</span><span class="token operator" style="color:rgb(255, 157, 0)">?.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Could not determine authenticated user'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Authenticated as: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">authenticatedUser</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">customDisplayName </span><span class="token template-string interpolation operator" style="color:rgb(255, 157, 0)">??</span><span class="token template-string interpolation"> authenticatedUser</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">providerDisplayName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> (ID: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">authenticatedUser</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">id</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">)</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> authenticatedUser</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/**</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic"> * Set the merge commit message for a pull request using squash merge strategy</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">setMergeCommitMessageAndAutocomplete</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pullRequestId</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  mergeCommitMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  token</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  org</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  project</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  repo</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pullRequestId</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  mergeCommitMessage</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  org</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  project</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  repo</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  token</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> defaultHeaders </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    Authorization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Basic </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">Buffer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">from</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string interpolation template-string string" style="color:rgb(165, 255, 144)">:</span><span class="token template-string interpolation template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation template-string interpolation">token</span><span class="token template-string interpolation template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string interpolation template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">toString</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation string" style="color:rgb(165, 255, 144)">'base64'</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'Content-Type'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'application/json'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> authenticatedUser </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAuthenticatedUser</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">org</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> defaultHeaders</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Setting autocomplete and merge commit message for PR #</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">pullRequestId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> as </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">authenticatedUser</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">customDisplayName </span><span class="token template-string interpolation operator" style="color:rgb(255, 157, 0)">??</span><span class="token template-string interpolation"> authenticatedUser</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">providerDisplayName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> (</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">authenticatedUser</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">id</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">)...</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> updateData </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    autoCompleteSetBy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> authenticatedUser</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    completionOptions</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      mergeStrategy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'squash'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      mergeCommitMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> response </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fetch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">https://dev.azure.com/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">org</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">project</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/_apis/git/repositories/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">repo</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/pullrequests/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">pullRequestId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">?api-version=7.1</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      method</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'PATCH'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      headers</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> defaultHeaders</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      body</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token constant" style="color:rgb(250, 208, 0)">JSON</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">stringify</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">updateData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorText </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">text</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Failed to set autocomplete and merge commit message: HTTP </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">String</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation">response</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">status</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">errorText</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Successfully set autocomplete and merge commit message for PR #</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">pullRequestId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">main</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> token</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> currentPullRequestId</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> org</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> project</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> repo </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getArgs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">setMergeCommitMessageAndAutocomplete</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    pullRequestId</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> currentPullRequestId</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    mergeCommitMessage</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'feat: conventional commit message'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// &lt;- set your commit message here</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    token</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    org</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    project</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    repo</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">main</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">catch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">err</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">unknown</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorMessage </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    err </span><span class="token keyword" style="color:rgb(255, 157, 0)">instanceof</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token plain"> err</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">message </span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Unknown error occurred'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">[ERROR] </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">errorMessage</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> err</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>This can also be run locally with <code>node ./set-autocomplete-and-commit-message.ts --token [PAT TOKEN WITH SCOPES: vso.code_write and vso.identity] --pr-id [PULL_REQUEST_ID] --org [NAME_OF_ORGANISATION] --project [NAME_OF_PROJECT] --repo [NAME_OF_REPOSITORY]</code>. You'll need Node.js 24 or later to run this. (And yes, you can run TypeScript files directly with Node.js these days).</p>
<p>The thing I haven't included here is how you determine the <code>mergeCommitMessage</code>. In my case, I use the title of the pull request as the commit message. You can fetch the pull request details using the Azure DevOps API and extract the title. I left this out for brevity, but you can easily add it in. Or use whatever logic you like to determine the commit message. The point is that you have control over it.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-build-validation-pipeline">The build validation pipeline<a href="https://johnnyreilly.com/azure-devops-pull-requests-conventional-commits#the-build-validation-pipeline" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>Now we have our script, we need to run it in a build validation pipeline. Here's an example of an Azure DevOps pipeline that runs the script:</p>
<div class="language-yml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yml codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token key atrule">trigger</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> none</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">pool</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">vmImage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">variables</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">isPullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> eq(variables</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'Build.Reason'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> 'PullRequest') </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">stages</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">stage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> AutoCompleteAndCommitMessage</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Set autocomplete and commit message</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">condition</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> variables</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'isPullRequest'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">job</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">task</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> NodeTool@0</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token key atrule">versionSpec</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">24</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Install Node.js</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">bash</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> node ./scripts/set</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">autocomplete</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">and</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">commit</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">message.ts </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">token $(System.AccessToken) </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">pr</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">id $(System.PullRequest.PullRequestId) </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">org "$(System.CollectionUri)" </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">project "$(System.TeamProject)" </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">repo "$(Build.Repository.Name)"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Set autocomplete and commit message</span><br></span></code></pre></div></div>
<p>Crucially, this pipeline is triggered only for pull requests; the <code>System.PullRequest.PullRequestId</code> is only available in build validations run as part of a pull request. The pipeline installs Node.js 24 and then runs our script, passing in the necessary parameters. The <code>System.AccessToken</code> is used to authenticate with the Azure DevOps API, so make sure that the pipeline has the necessary permissions to use it.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/azure-devops-pull-requests-conventional-commits#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>And that's it! With this setup, you can maintain a git commit history of conventional commits in Azure DevOps, even when merging pull requests. By using the Azure DevOps API to set the merge commit message when setting a pull request to autocomplete, you can ensure that your commit messages are meaningful and consistent.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="TypeScript" term="TypeScript"/>
        <category label="Azure DevOps" term="Azure DevOps"/>
        <category label="Node.js" term="Node.js"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Azure DevOps: merging pull requests and setting autocomplete with the API]]></title>
        <id>https://johnnyreilly.com/azure-devops-api-pull-requests-merge-set-autocomplete</id>
        <link href="https://johnnyreilly.com/azure-devops-api-pull-requests-merge-set-autocomplete"/>
        <updated>2025-08-29T08:12:25.000Z</updated>
        <summary type="html"><![CDATA[How to merge a pull request in Azure DevOps or set it to autocomplete using the Azure DevOps API Client for Node.js.]]></summary>
        <content type="html"><![CDATA[<p>Have you ever wanted to merge a pull request in Azure DevOps using the Azure DevOps API? Or set a pull request to autocomplete, so it automatically merges when all policies are satisfied? If so, you're in the right place. In this post, I'll show you how to do just that using the Azure DevOps Client for Node.js.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Azure DevOps: merging pull requests and setting autocomplete with the API&amp;quot; with an Azure DevOps logo" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-b2838d56371d45367fd75b197cc2f5e2.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>I'm using the Azure DevOps Client for Node.js; but if you want to use the REST API directly, you can do that too. The principles are the same, but you'll need to make HTTP requests instead of using the client library.</p>
<p>To get up and running with the Azure DevOps Client for Node.js, you can <a href="https://johnnyreilly.com/azure-devops-pull-requests-dynamic-required-reviewers">see how we work with it in this post on dynamic required reviewers in Azure DevOps</a> post. This will help you set up your environment and authenticate with Azure DevOps.</p>
<p>If you'd like to read about setting commit messages when merging pull requests in Azure DevOps, you can check out my post on <a href="https://johnnyreilly.com/azure-devops-pull-requests-conventional-commits">merging pull requests with conventional commits in Azure DevOps</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="merging-a-pull-request">Merging a pull request<a href="https://johnnyreilly.com/azure-devops-api-pull-requests-merge-set-autocomplete#merging-a-pull-request" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>To merge a pull request with the API, you need to use the <code>GitApi</code> class from the Azure DevOps Client for Node.js. Here's what merging a pull request looks like:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">mergePullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  gitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  repositoryName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  gitApi</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> IGitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  repositoryName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pullRequest</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> GitPullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token keyword" style="color:rgb(255, 157, 0)">void</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> gitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">updatePullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        status</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> PullRequestStatus</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Completed</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        lastMergeSourceCommit</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">lastMergeSourceCommit</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        completionOptions</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          mergeStrategy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> GitPullRequestMergeStrategy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Squash</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** repositoryId */</span><span class="token plain"> repositoryName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">pullRequestId</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** project */</span><span class="token plain"> projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">✅ Successfully merged pull request </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">pullRequest</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">pullRequestId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorMessage </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">❌ Failed to merge pull request </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">pullRequest</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">pullRequestId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>This expects that you provide the <code>gitApi</code> instance, the name of the repository, the pull request object, and the project name. The <code>mergeStrategy</code> is set to <code>Squash</code>, but you can change it to <code>Rebase</code> or <code>NoFastForward</code> if you prefer those strategies.</p>
<p>If you have policies that restrict your merge type, you must pick the merge strategy that complies with those policies. For example, if your project requires squash merges, you should use <code>GitPullRequestMergeStrategy.Squash</code>. AKA the one true merge strategy.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="setting-a-pull-request-to-autocomplete">Setting a pull request to autocomplete<a href="https://johnnyreilly.com/azure-devops-api-pull-requests-merge-set-autocomplete#setting-a-pull-request-to-autocomplete" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Setting a pull request to autocomplete means that it will automatically merge when all policies are satisfied. This is useful for ensuring that the pull request is merged without manual intervention once it meets the requirements. For example when build validations have passed, and the required reviewers have approved.</p>
<p>Here's how you can set a pull request to autocomplete:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">setPullRequestToAutocomplete</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  gitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  locationsApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  repositoryName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  gitApi</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> IGitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  locationsApi</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> ILocationsApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  repositoryName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pullRequest</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> GitPullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token keyword" style="color:rgb(255, 157, 0)">void</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">autoCompleteSetBy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> authenticatedUser </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> locationsApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">getConnectionData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Setting pull request </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">pullRequest</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">pullRequestId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> to auto-complete with squash merge as </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">authenticatedUser</span><span class="token template-string interpolation operator" style="color:rgb(255, 157, 0)">?.</span><span class="token template-string interpolation">providerDisplayName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> (</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">authenticatedUser</span><span class="token template-string interpolation operator" style="color:rgb(255, 157, 0)">?.</span><span class="token template-string interpolation">id</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">)</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> gitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">updatePullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        autoCompleteSetBy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> authenticatedUser</span><span class="token operator" style="color:rgb(255, 157, 0)">?.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        completionOptions</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          mergeStrategy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> GitPullRequestMergeStrategy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Squash</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** repositoryId */</span><span class="token plain"> repositoryName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">pullRequestId</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** project */</span><span class="token plain"> projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">✅ Successfully set pull request </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">pullRequest</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">pullRequestId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> to auto-complete</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorMessage </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">❌ Failed to set pull request </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">pullRequest</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">pullRequestId</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> to auto-complete</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>What might be surprising about this code is that you have explicitly provide your user id when setting the pull request to autocomplete. The unlovely aspect of this is that you need to discover that id somehow. We achieve it here by fetching the authenticated user from the <code>locationsApi</code>.</p>
<p>Once you have the user id, you can set the <code>autoCompleteSetBy</code> property of the pull request to that user id. This will allow the pull request to be set to autocomplete. Again we must specify the <code>mergeStrategy</code> so it knows how to merge when the time comes.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/azure-devops-api-pull-requests-merge-set-autocomplete#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>In this post, we've seen how to merge a pull request and set it to autocomplete using the Azure DevOps Client for Node.js. This can be a powerful way to automate your workflow and ensure that pull requests are merged when they meet the necessary criteria.</p>
<p>I'm personally using this in build validation pipelines to ensure that pull requests are merged automatically when all policies are satisfied. This helps to streamline the development process and reduce manual intervention.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="TypeScript" term="TypeScript"/>
        <category label="Azure DevOps" term="Azure DevOps"/>
        <category label="Node.js" term="Node.js"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Static Web Apps CLI: local authentication emulation with ASP.NET]]></title>
        <id>https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication</id>
        <link href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication"/>
        <updated>2025-03-28T19:39:57.000Z</updated>
        <summary type="html"><![CDATA[The Static Web Apps CLI has a local authentication emulator. This is a useful tool for local development, and can be used with ASP.NET authentication. This post shows how.]]></summary>
        <content type="html"><![CDATA[<p>When developing web applications that have some dependency on authentication, it can be tricky to get a local development setup that allows you to manage authentication effectively. However, there's a way to achieve this, using the Static Web Apps CLI local authentication emulator.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Static Web Apps CLI: improve performance with Vite server proxy&amp;quot; with the Static Web Apps CLI and Vite logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-bd0f70a54d087f447ca7b6d544692a83.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>I build a lot of SPA style applications that run JavaScript / TypeScript on the front end and C# / ASP.NET on the back end. The majority of those apps require some kind of authentication. In fact I'd struggle to think of many apps that don't. This post will walk through how to integrate ASP.NET authentication with the Static Web Apps CLI local authentication emulator to achieve a great local development setup. Don't worry if that doesn't make sense right now, once we have walked through the setup, it will.</p>
<p>This post builds somewhat on posts I've written about using the Static Web Apps CLI <a href="https://johnnyreilly.com/static-web-apps-cli-improve-performance-with-vite-server-proxy">with the Vite proxy server with for enhanced performance</a> and <a href="https://johnnyreilly.com/static-web-apps-cli-node-18-could-not-connect-to-api">how to use the <code>--api-location</code> argument to connect to a separately running backend API</a>. However, you need not have read either post to understand what we're doing.</p>
<p>We're going to first walk through what we're trying to achieve, and then we'll walk through the steps to get there. When it comes to implementation, we're going to use Vite as our front end server, and ASP.NET as our back end server. The Static Web Apps CLI will be used for local authentication emulation.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="local-authentication-choices">Local authentication choices<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#local-authentication-choices" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>When we're building an application, let's think about the options that we have, with regards to our local development setup. It's pretty typical for applications to use some kind of third party authentication provider, rather than providing their own. This could be <a href="https://www.okta.com/" target="_blank" rel="noopener noreferrer">Okta</a>, <a href="https://learn.microsoft.com/en-us/entra/identity/authentication/overview-authentication" target="_blank" rel="noopener noreferrer">Microsoft Entra / Azure AD</a>, <a href="https://auth0.com/" target="_blank" rel="noopener noreferrer">Auth0</a> or something else.</p>
<p>It's possible to configure a local development setup which integrates with a third party authentication provider. However, is that wise? Do you want to couple your ability to be able to test scenarios on your local machine, to a server, somewhere out there on the internet? You certainly can. It typically involves setting a redirect URI on the authentication provider to <code>http://localhost:5173</code> (or wherever your local setup runs).</p>
<p>But it is inconvenient to get that set up in the first place. And even once it is set up, you're then coupled to being online whenever you're testing locally. We're offline more than we appreciate. I'm writing these words on an aeroplane which is currently flying over Botswana. I have no internet access right now. But as you've gathered, I'm on my computer and I'm able to do things. How? Because I'm using the Azure Static Web Apps CLI local authentication emulator for local development.</p>
<p>That's what this post is about. How to use the Static Web Apps CLI local authentication emulator with ASP.NET authentication to enable a great (and offline-first) local development setup.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-is-the-static-web-apps-cli">What is the Static Web Apps CLI?<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#what-is-the-static-web-apps-cli" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We should probably talk about what the <a href="https://azure.github.io/static-web-apps-cli/" target="_blank" rel="noopener noreferrer">Static Web Apps CLI</a> is. It describes itself as:</p>
<blockquote>
<p>The Static Web Apps (SWA) CLI is an open-source commandline tool that streamlines local development and deployment for Azure Static Web Apps.</p>
</blockquote>
<p>It's original purpose was to provide a local development server for an Azure service known as <a href="https://learn.microsoft.com/en-us/azure/static-web-apps/" target="_blank" rel="noopener noreferrer">Azure Static Web Apps</a>. However, it has a number of features that make it useful for general web application development. For example, it can be used to <a href="https://azure.github.io/static-web-apps-cli/docs/cli/swa-start#serve-both-the-front-end-app-and-api" target="_blank" rel="noopener noreferrer">proxy requests to a backend API server</a>, and it can also be used to <a href="https://azure.github.io/static-web-apps-cli/docs/cli/local-auth" target="_blank" rel="noopener noreferrer">emulate authentication</a>.</p>
<p>We're going to use the authentication emulator to provide a local authentication server whilst we're developing. Just that piece of functionality; we're intentionally only using a subset of the Static Web Apps CLI functionality.</p>
<p>Incidentally, there are alternatives. I'm aware of one other local authentication emulator, which is the <a href="https://firebase.google.com/docs/emulator-suite/connect_auth" target="_blank" rel="noopener noreferrer">Firebase Authentication Emulator</a>. This could likely be used in a similar way. However, we'll be using the Static Web Apps CLI local authentication emulator.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-does-the-static-web-apps-cli-local-authentication-emulator-work">How does the Static Web Apps CLI local authentication emulator work?<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#how-does-the-static-web-apps-cli-local-authentication-emulator-work" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>When running the Static Web Apps <code>start</code> command, it surfaces login endpoints at this location: <code>http://localhost:4280/.auth/login/&lt;PROVIDER_NAME&gt;</code>. <code>&lt;PROVIDER_NAME&gt;</code> is the name of the authentication provider you want to use. This might be <code>aad</code>, <code>github</code> etc. If you look at the code (<a href="https://github.com/Azure/static-web-apps-cli/blob/062fb288d34126a095be6f3e1dc57fe5adb3f4bf/src/public/auth.html" target="_blank" rel="noopener noreferrer">and you can here</a>) you'll realise that the <code>&lt;PROVIDER_NAME&gt;</code> can actually be any string; it's not limited to the names of the authentication providers that are supported by Azure Static Web Apps. So if you want to use an arbitary name like <code>potato</code> as the provider name, you can do that. In terms of emulation, it doesn't matter what the provider name is.</p>
<p>When started, the CLI will serve a local authentication UI at this endpoint which looks like this:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of Static Web Apps CLI local authentication emulator at work" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/swa-cli-local-auth-7a324cc39d640ff7fd8fd5c1f680c293.webp" width="1394" height="1756" class="img_ev3q"></p>
<p>When you hit the <code>Login</code> button, it will use the form data to create a fake user and <a href="https://github.com/Azure/static-web-apps-cli/blob/062fb288d34126a095be6f3e1dc57fe5adb3f4bf/src/public/auth.html#L193-L196" target="_blank" rel="noopener noreferrer">set a cookie in your browser named <code>StaticWebAppsAuthCookie</code></a>. That cookie will look something like this:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of the StaticWebAppsAuthCookie in Chrome Devtools" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-staticwebappsauthcookie-70b7978c3b4d620c7abbe906859bdff2.webp" width="2360" height="582" class="img_ev3q"></p>
<p>And whilst it looks like a JWT, it isn't. It's actually a base64 encoded string which contains the user information that you provided in the form. In fact you can see what it is by flipping open the browser devtools and running this code in the console after you have hit the <code>Login</code> button:</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token known-class-name class-name" style="color:rgb(250, 208, 0)">JSON</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">parse</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function" style="color:rgb(250, 208, 0)">atob</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token dom variable" style="color:rgb(255, 238, 128)">document</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">cookie</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">split</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'; '</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">find</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token parameter">row</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> row</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">startsWith</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'StaticWebAppsAuthCookie='</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token operator" style="color:rgb(255, 157, 0)">?.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">split</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'='</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token number" style="color:rgb(255, 98, 140)">1</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>This will acquire the cookie that has just been created by the Static Web Apps CLI local authentication emulator from your browser. It then decodes it and parses the JSON string to get the user information that you provided in the form. It produces something like this:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"userId"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"4a27b7326639199f5de91c4b9a62531b"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"userRoles"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">"anonymous"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"authenticated"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"other-role"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"claims"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token property" style="color:rgb(255, 157, 0)">"typ"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"MyId"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token property" style="color:rgb(255, 157, 0)">"val"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"123456789"</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"identityProvider"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"couldbeanything"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"userDetails"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"megan.s@thing.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>This cookie will be sent to your backend API server with every request.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="how-is-our-solution-going-to-work">How is our solution going to work?<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#how-is-our-solution-going-to-work" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>To make a working development setup, we need three things:</p>
<ol>
<li>A front end server (Vite)</li>
<li>A back end server (ASP.NET)</li>
<li>A local authentication server (Static Web Apps CLI)</li>
</ol>
<p>We'll bring these three things together to create a local development setup that allows us to develop our application locally, with authentication. This diagram shows how the three components will work together:</p>
<!-- -->
<p>From the developer's browser, HTTP requests will be sent to the Vite server running on <code>http://localhost:5173</code>. The Vite server will proxy authentication emulation requests to the Static Web Apps CLI local authentication emulator running on <code>http://localhost:4280</code>. All other requests will be proxied to the ASP.NET backend server running on <code>http://localhost:5000</code>.</p>
<p>This setup means that the cookie that is set by the Static Web Apps CLI local authentication emulator will be shared with the ASP.NET backend server through the Vite proxy mechanism. So to make the ASP.NET authentication work, we need to make sure that the ASP.NET server is configured to accept this cookie and use it to authenticate the user.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="time-to-implement">Time to implement<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#time-to-implement" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Now that we've talked about what we're trying to achieve, let's walk through the steps to get there. We'll need both the .NET SDK and Node.js installed on our machine. From here on out it's code. The full example can be found in <a href="https://github.com/johnnyreilly/swa-local-auth-emulator-and-dotnet" target="_blank" rel="noopener noreferrer">this GitHub repository</a>.</p>
<p>We'll start by creating a new Vite project in a folder we'll call <code>AppFrontEnd</code>. We'll use the React + TypeScript template. You can use whatever template you like:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> create vite@latest AppFrontEnd -- </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--template</span><span class="token plain"> react-ts</span><br></span></code></pre></div></div>
<p>Next we'll create a new ASP.NET project in a folder we'll call <code>AppBackEnd</code>:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">dotnet new web </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-n</span><span class="token plain"> AppBackEnd</span><br></span></code></pre></div></div>
<p>And we'll initialise a <code>package.json</code> in the root of our project:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> init </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-y</span><br></span></code></pre></div></div>
<p>This <code>package.json</code> will be used as a general purpose task runner later on.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="setting-up-the-back-end">Setting up the back end<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#setting-up-the-back-end" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We'd first like to adjust the port that our ASP.NET server runs on in development. We'll update the <code>launchSettings.json</code> file to look like this:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"$schema"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"https://json.schemastore.org/launchsettings.json"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"profiles"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"http"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"commandName"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Project"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"dotnetRunMessages"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"launchBrowser"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"applicationUrl"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"http://localhost:5000"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"environmentVariables"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token property" style="color:rgb(255, 157, 0)">"ASPNETCORE_ENVIRONMENT"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Development"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>This will set the ASP.NET server to run on <code>http://localhost:5000</code> in development. You can run the server with <code>dotnet run</code>.</p>
<p>We need to build an <code>AuthenticationHandler</code> that will accept the cookie set by the Static Web Apps CLI local authentication emulator. This is a custom authentication handler that will be used to authenticate users based on the cookie set by the Static Web Apps CLI local authentication emulator. So here the <code>StaticWebAppsCLIAuthentication.cs</code> in all its glory:</p>
<div class="language-cs codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cs codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">System</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Security</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Claims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">System</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Text</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Encodings</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Web</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">System</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Text</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Json</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">System</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Text</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Json</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Serialization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">Microsoft</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">AspNetCore</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Authentication</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">Microsoft</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Extensions</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">Options</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">namespace</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">AppBackEnd</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">static</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIAuthentication</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">string</span><span class="token plain"> STATICWEBAPPSCLIAUTH_SCHEMENAME </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"StaticWebAppsCLIAuthScheme"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">static</span><span class="token plain"> </span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">AuthenticationBuilder</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">AddStaticWebAppsCLIAuth</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">this</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">AuthenticationBuilder</span><span class="token plain"> builder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">Action</span><span class="token class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token class-name" style="color:rgb(250, 208, 0)">AuthenticationSchemeOptions</span><span class="token class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token class-name punctuation" style="color:rgb(255, 255, 255)">?</span><span class="token plain"> configure </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">configure </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> configure </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> o </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> builder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token generic-method function" style="color:rgb(250, 208, 0)">AddScheme</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token generic-method generic class-name" style="color:rgb(250, 208, 0)">AuthenticationSchemeOptions</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token generic-method generic class-name" style="color:rgb(250, 208, 0)"> StaticWebAppsCLIAuthenticationHandler</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            STATICWEBAPPSCLIAUTH_SCHEMENAME</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            STATICWEBAPPSCLIAUTH_SCHEMENAME</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            configure</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIAuthenticationHandler</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token type-list class-name" style="color:rgb(250, 208, 0)">AuthenticationHandler</span><span class="token type-list class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token type-list class-name" style="color:rgb(250, 208, 0)">AuthenticationSchemeOptions</span><span class="token type-list class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">StaticWebAppsCLIAuthenticationHandler</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">IOptionsMonitor</span><span class="token class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token class-name" style="color:rgb(250, 208, 0)">AuthenticationSchemeOptions</span><span class="token class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"> options</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">ILoggerFactory</span><span class="token plain"> logger</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">UrlEncoder</span><span class="token plain"> encoder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">base</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">options</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> logger</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> encoder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">protected</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">override</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">Task</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">AuthenticateResult</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">HandleAuthenticateAsync</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">try</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> principal </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">MakeClaimsPrincipalFromHeaderOrCookie</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// eg eyJ1c2VySWQiOiIwMTA2ZWMwYmU2MjAwNDM5YjY5ODc3NTFiOGJmNmU3YSIsInVzZXJSb2xlcyI6WyJhbm9ueW1vdXMiLCJhdXRoZW50aWNhdGVkIl0sImNsYWltcyI6W3sidHlwIjoibmFtZSIsInZhbCI6IkF6dXJlIFN0YXRpYyBXZWIgQXBwcyJ9XSwiaWRlbnRpdHlQcm92aWRlciI6ImFhZCIsInVzZXJEZXRhaWxzIjoiam9obm55cmVpbGx5In0</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                Context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Cookies</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">"StaticWebAppsAuthCookie"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// eg eyJ1c2VySWQiOiIwMTA2ZWMwYmU2MjAwNDM5YjY5ODc3NTFiOGJmNmU3YSIsInVzZXJSb2xlcyI6WyJhbm9ueW1vdXMiLCJhdXRoZW50aWNhdGVkIl0sImlkZW50aXR5UHJvdmlkZXIiOiJhYWQiLCJ1c2VyRGV0YWlscyI6ImpvaG5ueXJlaWxseSJ9</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// for reasons that are unclear, the X-MS-CLIENT-PRINCIPAL header presently excludes claims; see https://github.com/Azure/static-web-apps-cli/blob/062fb288d34126a095be6f3e1dc57fe5adb3f4bf/src/msha/handlers/function.handler.ts#L38-L42</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                Context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Headers</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">"X-MS-CLIENT-PRINCIPAL"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">FirstOrDefault</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">principal </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> AuthenticateResult</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">NoResult</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            Context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">User </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> principal</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> AuthenticateResult</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Success</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">AuthenticationTicket</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">principal</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> principal</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Identity</span><span class="token punctuation" style="color:rgb(255, 255, 255)">?.</span><span class="token plain">AuthenticationType </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"unknown"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token class-name" style="color:rgb(250, 208, 0)">Exception</span><span class="token plain"> ex</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> AuthenticateResult</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Fail</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">ex</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">private</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">Task</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">ClaimsPrincipal</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">?</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">MakeClaimsPrincipalFromHeaderOrCookie</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">string</span><span class="token class-name punctuation" style="color:rgb(255, 255, 255)">?</span><span class="token plain"> headerOrCookie</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">IsNullOrEmpty</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">headerOrCookie</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> decodedBytes </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> Convert</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">FromBase64String</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">headerOrCookie</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> memoryStream </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">MemoryStream</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">decodedBytes</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> staticWebAppClientPrinciple </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> JsonSerializer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token generic-method function" style="color:rgb(250, 208, 0)">DeserializeAsync</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token generic-method generic class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIClientPrinciple</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">memoryStream</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">staticWebAppClientPrinciple </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token keyword" style="color:rgb(255, 157, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">IsNullOrWhiteSpace</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">UserDetails</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token keyword" style="color:rgb(255, 157, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">IsNullOrWhiteSpace</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">UserId</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> claims </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">DefaultMapClaims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> principal </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">ClaimsPrincipal</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        principal</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">AddIdentity</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">ClaimsIdentity</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            claims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token named-parameter punctuation" style="color:rgb(255, 255, 255)">authenticationType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">IdentityProvider </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"unknown"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token named-parameter punctuation" style="color:rgb(255, 255, 255)">nameType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ClaimTypes</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Email</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token named-parameter punctuation" style="color:rgb(255, 255, 255)">roleType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ClaimTypes</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Role</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> principal</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// Method takes the StaticWebAppClientPrinciple and produces a list of claims constructed</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// from the claims, user details and user roles</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">static</span><span class="token plain"> </span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">Claim</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">DefaultMapClaims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIClientPrinciple</span><span class="token plain"> staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> claims </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">List</span><span class="token constructor-invocation class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIClaim</span><span class="token constructor-invocation class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token keyword" style="color:rgb(255, 157, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">IsNullOrWhiteSpace</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">UserDetails</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            claims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Add</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIClaim</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                Type </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"preferred_username"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                Value </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">UserDetails</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            claims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Add</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIClaim</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                Type </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> ClaimTypes</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Email</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                Value </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">UserDetails</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            claims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Add</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIClaim</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                Type </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> ClaimTypes</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                Value </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">UserDetails</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Claims </span><span class="token operator" style="color:rgb(255, 157, 0)">!=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            claims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">AddRange</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Claims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> mappedClaims </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> claims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Select</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">claim </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">Claim</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">claim</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> claim</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// translate the user roles into claims with the type ClaimTypes.Role</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// eg "userRoles": ["anonymous", "authenticated"]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> userRoleClaims </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> staticWebAppClientPrinciple</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">UserRoles</span><span class="token punctuation" style="color:rgb(255, 255, 255)">?.</span><span class="token function" style="color:rgb(250, 208, 0)">Select</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">role </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">Claim</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">ClaimTypes</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Role</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> role</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">Claim</span><span class="token class-name punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token class-name punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> combinedClaims </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token range operator" style="color:rgb(255, 157, 0)">..</span><span class="token plain">mappedClaims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token range operator" style="color:rgb(255, 157, 0)">..</span><span class="token plain">userRoleClaims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> combinedClaims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// This is the JSON object that is decoded from either the</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// StaticWebAppsAuthCookie cookie or the X-MS-CLIENT-PRINCIPAL header</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// when working with the Static Web Apps CLI local authentication emulator.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// The claims element is not present on the X-MS-CLIENT-PRINCIPAL header</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// {</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///     "userId": "9b516349fcf5caf60f715703d8804aa7",</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///     "userRoles": [</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///         "anonymous",</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///         "authenticated"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///     ],</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///     "claims": [{"typ":"blarg","val":"Azure Static Web Apps"}],</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///     "identityProvider": "aad",</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">///     "userDetails": "someone.name@company.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// }</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIClientPrinciple</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// User Id</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token function" style="color:rgb(250, 208, 0)">JsonPropertyName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"userId"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:rgb(255, 157, 0)">string</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">?</span><span class="token plain"> UserId </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">set</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// User roles</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token function" style="color:rgb(250, 208, 0)">JsonPropertyName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"userRoles"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">IEnumerable</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token return-type class-name keyword" style="color:rgb(255, 157, 0)">string</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">?</span><span class="token plain"> UserRoles </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">set</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// Identity provider typically "aad"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token function" style="color:rgb(250, 208, 0)">JsonPropertyName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"identityProvider"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:rgb(255, 157, 0)">string</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">?</span><span class="token plain"> IdentityProvider </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">set</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// User details typically email address</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token function" style="color:rgb(250, 208, 0)">JsonPropertyName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"userDetails"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:rgb(255, 157, 0)">string</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">?</span><span class="token plain"> UserDetails </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">set</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// Claims for the user</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token function" style="color:rgb(250, 208, 0)">JsonPropertyName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"claims"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">IEnumerable</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIClaim</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token return-type class-name punctuation" style="color:rgb(255, 255, 255)">?</span><span class="token plain"> Claims </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">set</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">StaticWebAppsCLIClaim</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// Type of claim eg "name"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token function" style="color:rgb(250, 208, 0)">JsonPropertyName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"typ"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:rgb(255, 157, 0)">string</span><span class="token plain"> Type </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">set</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Empty</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// Value of claim eg "Someone Name"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token function" style="color:rgb(250, 208, 0)">JsonPropertyName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"val"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:rgb(255, 157, 0)">string</span><span class="token plain"> Value </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">set</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Empty</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>Whilst there's a good amount of code here, what we're actually doing is relatively simple:</p>
<ul>
<li>We define a custom authentication scheme called <code>StaticWebAppsCLIAuthScheme</code></li>
<li>We define a custom authentication handler called <code>StaticWebAppsCLIAuthenticationHandler</code>. The handler will decode the cookie and create a <code>ClaimsPrincipal</code> object that contains the user information it. This will be used to authenticate users based on the cookie that has been set by the Static Web Apps CLI local authentication emulator. If the <code>StaticWebAppsAuthCookie</code> cookie is not detected, the handler will fallback to the <code>X-MS-CLIENT-PRINCIPAL</code> header. (This should never happen in practice, but it's there for completeness.)</li>
</ul>
<p>With our handler in place, we need to update the <code>Program.cs</code> file in the <code>AppBackEnd</code> project to use it. We'll also add a <code>/api/me</code> endpoint to test the authentication. So the modified <code>Program.cs</code> file looks like this:</p>
<div class="language-cs codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cs codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">AppBackEnd</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> builder </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> WebApplication</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">CreateBuilder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">args</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">builder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Environment</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">IsDevelopment</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// in development, we want to use the Static Web Apps CLI authentication scheme</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    builder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Services</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">AddAuthentication</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">StaticWebAppsCLIAuthentication</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">STATICWEBAPPSCLIAUTH_SCHEMENAME</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">AddStaticWebAppsCLIAuth</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">else</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// in production we will use an alternative authentication scheme</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">builder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Services</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">AddAuthorization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> app </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> builder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Build</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">UseAuthentication</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">UseAuthorization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">MapGet</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"/"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Hello World!"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">MapGet</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"/api/me"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> user </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">User</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> roleClaimType </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> user</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Identities</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">First</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">RoleClaimType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> userDetails </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        user</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Identity</span><span class="token punctuation" style="color:rgb(255, 255, 255)">?.</span><span class="token plain">Name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        user</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Identity</span><span class="token punctuation" style="color:rgb(255, 255, 255)">?.</span><span class="token plain">IsAuthenticated</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        Claims </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> user</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Claims</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Select</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">claim </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> claim</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> claim</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Value </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">ToArray</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">WriteAsJsonAsync</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">userDetails</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Run</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>I haven't included the production authentication scheme here; that will be specific to your application. The important part is that we are using the <code>StaticWebAppsCLIAuthentication</code> scheme in only in development.</p>
<p>Let's test this works. We'll start the ASP.NET server with <code>dotnet run</code>. Then we'll use the <code>curl</code> command to call the <code>/api/me</code> endpoint:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">curl</span><span class="token plain"> http://localhost:5000/api/me </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--cookie</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"StaticWebAppsAuthCookie=eyJ1c2VySWQiOiIxNDdmMTVjNmFiODBhNmY3NjJhOWQyZDRmNzczNzUwOSIsInVzZXJSb2xlcyI6WyJhbm9ueW1vdXMiLCJhdXRoZW50aWNhdGVkIiwib3RoZXItcm9sZSJdLCJjbGFpbXMiOlt7InR5cCI6Ik15SWQiLCJ2YWwiOiIxMjM0NTY3ODkifV0sImlkZW50aXR5UHJvdmlkZXIiOiJjb3VsZGJlYW55dGhpbmciLCJ1c2VyRGV0YWlscyI6Im1lZ2FuLnNAdGhpbmcuY29tIn0="</span><br></span></code></pre></div></div>
<p>This will return the user information that was set in the cookie by the Static Web Apps CLI local authentication emulator:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"name"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"megan.s@thing.com"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"isAuthenticated"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"claims"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"preferred_username"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"value"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"megan.s@thing.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"value"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"megan.s@thing.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"value"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"megan.s@thing.com"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"MyId"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"value"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"123456789"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"http://schemas.microsoft.com/ws/2008/06/identity/claims/role"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"value"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"anonymous"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"http://schemas.microsoft.com/ws/2008/06/identity/claims/role"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"value"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"authenticated"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"http://schemas.microsoft.com/ws/2008/06/identity/claims/role"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"value"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"other-role"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>So our authentication mechanism works! Now we just need to set up the Vite server and the Static Web Apps CLI local authentication emulator to provide the full local development experience.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="setting-up-the-front-end">Setting up the front end<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#setting-up-the-front-end" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Now we'll move over to the <code>AppFrontEnd</code> folder.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="set-up-the-static-web-apps-cli">Set up the Static Web Apps CLI<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#set-up-the-static-web-apps-cli" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>We'll install the Static Web Apps CLI as a development dependency:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-D</span><span class="token plain"> @azure/static-web-apps-cli</span><br></span></code></pre></div></div>
<p>And we'll add a <code>start</code> script to the <code>package.json</code> file to start the Static Web Apps CLI and the Vite server. The <code>start</code> script will look like this:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"scripts"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"start"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"swa start http://localhost:5173 --run \"npm run dev\" --api-devserver-url http://127.0.0.1:5000"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>When run, this will start the Static Web App CLI and the Vite server. The <code>--run</code> argument will start the Vite server, and the <code>--api-devserver-url</code> argument will set the URL of the ASP.NET backend server. We'll create a mechanism for starting the ASP.NET server alongside the front end shortly.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="set-up-the-vite-server">Set up the Vite server<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#set-up-the-vite-server" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>We already have the Vite server in place, but it needs a little configuration.</p>
<p>I'm now going to borrow from <a href="https://johnnyreilly.com/static-web-apps-cli-improve-performance-with-vite-server-proxy">this post on using the Vite proxy server with for enhanced performance</a>. We'll update the <code>vite.config.ts</code> file to add a proxy configuration. The <code>vite.config.ts</code> file will look like this:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> defineConfig </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'vite'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// https://vitejs.dev/config/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">defineConfig</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  server</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    proxy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'/api'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// our .NET application is running on port 5000</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        target</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'http://127.0.0.1:5000'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        changeOrigin</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        autoRewrite</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'/.auth'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// the Static Web Apps local auth emulator is running on port 4280</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        target</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'http://127.0.0.1:4280'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        changeOrigin</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        autoRewrite</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>The code above is responsible for ensuring that requests to the Vite server with the prefix <code>/.auth</code> are proxied to the Static Web Apps CLI local authentication emulator. Requests with the prefix <code>/api</code> (most requests) are proxied to the ASP.NET backend server.</p>
<p>With this in place, the Static Web Apps CLI local authentication emulator will be able to set the <code>StaticWebAppsAuthCookie</code> cookie in the browser, and the Vite server will proxy requests with that cookie to the ASP.NET backend server for use.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="bringing-front-end-and-back-end-together">Bringing front end and back end together<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#bringing-front-end-and-back-end-together" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We now have a back end and a front end in place. We need both to be running for local development. We mentioned earlier we'd be using the <code>package.json</code> in the root of the project as a task runner.</p>
<p>We'll use the <a href="https://github.com/open-cli-tools/concurrently" target="_blank" rel="noopener noreferrer"><code>concurrently</code></a> package to allow us to run both the ASP.NET server and the Static Web Apps CLI local authentication emulator at the same time.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">install</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-D</span><span class="token plain"> concurrently</span><br></span></code></pre></div></div>
<p>And we'll update the <code>scripts</code> section of the <code>package.json</code> as follows:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"scripts"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"postinstall"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"npm run install:frontend &amp;&amp; npm run install:backend"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"install:frontend"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"cd AppFrontEnd &amp;&amp; npm install"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"install:backend"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"cd AppBackEnd &amp;&amp; dotnet restore"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"start"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"concurrently -n \"FE,BE\" -c \"bgBlue.bold,bgMagenta.bold\" \"npm run start:frontend\" \"npm run start:backend\""</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"start:frontend"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"cd AppFrontEnd &amp;&amp; npm run start"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"start:backend"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"cd AppBackEnd &amp;&amp; dotnet watch run"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>We can now install all our dependencies (front end and backend) with a single command:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">install</span><br></span></code></pre></div></div>
<p>Then we can start the local development server with:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> start</span><br></span></code></pre></div></div>
<p>If we then go to <code>http://localhost:5173</code>, we should see the Vite server running:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of Vite server running" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-vite-server-06ee838b6ca49ff9619b840c6e296186.webp" width="1272" height="1348" class="img_ev3q"></p>
<p>So far, this is just the Vite server. Time to get our login mechanism in place.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="testing-the-authentication">Testing the authentication<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#testing-the-authentication" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We're going to replace the contents of the <code>src/App.tsx</code> file in the <code>AppFrontEnd</code> project with code that will call the <code>/api/me</code> endpoint on the ASP.NET backend server to get the user information from the cookie set by the Static Web Apps CLI local authentication emulator and display it in the browser. If the user is not authenticated, it will show a login link.</p>
<p>A reminder, we're using React and TypeScript for our front end. But that's just because it's my preference; this technique is front end agnostic. You can use whatever framework (or none) that you like.</p>
<p>Our implementation will look like this:</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token imports"> useState</span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token imports"> useEffect </span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'react'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'./App.css'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">UserData</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  name</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  isAuthenticated</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">boolean</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  claims</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> value</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">App</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">userData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> setUserData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token generic-function function" style="color:rgb(250, 208, 0)">useState</span><span class="token generic-function generic class-name operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token generic-function generic class-name" style="color:rgb(250, 208, 0)">UserData</span><span class="token generic-function generic class-name operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function" style="color:rgb(250, 208, 0)">useEffect</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fetchData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> response </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fetch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'/api/me'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">ok</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Failed to fetch user data'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> data </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">json</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token function" style="color:rgb(250, 208, 0)">setUserData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Error fetching user data:'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token function" style="color:rgb(250, 208, 0)">fetchData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">h1</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">Static Web Apps CLI:</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">h1</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">h2</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">local authentication emulation with ASP.NET</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">h2</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">userData </span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        userData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">isAuthenticated</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">p</span><span class="token tag" style="color:rgb(255, 157, 0)"> </span><span class="token tag attr-name" style="color:rgb(255, 180, 84)">style</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:rgb(255, 255, 255)">=</span><span class="token tag script language-javascript punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token tag script language-javascript punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token tag script language-javascript" style="color:rgb(255, 157, 0)"> textAlign</span><span class="token tag script language-javascript operator" style="color:rgb(255, 157, 0)">:</span><span class="token tag script language-javascript" style="color:rgb(255, 157, 0)"> </span><span class="token tag script language-javascript string" style="color:rgb(165, 255, 144)">'left'</span><span class="token tag script language-javascript" style="color:rgb(255, 157, 0)"> </span><span class="token tag script language-javascript punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token tag script language-javascript punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">            You are logged in </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">userData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain-text"> |</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token string" style="color:rgb(165, 255, 144)">' '</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">a</span><span class="token tag" style="color:rgb(255, 157, 0)"> </span><span class="token tag attr-name" style="color:rgb(255, 180, 84)">href</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(255, 255, 255)">=</span><span class="token tag attr-value punctuation" style="color:rgb(255, 255, 255)">"</span><span class="token tag attr-value" style="color:rgb(255, 157, 0)">/.auth/logout</span><span class="token tag attr-value punctuation" style="color:rgb(255, 255, 255)">"</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">Log out</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">a</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">pre</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token known-class-name class-name" style="color:rgb(250, 208, 0)">JSON</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">stringify</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">userData</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">2</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">pre</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">p</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">p</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">a</span><span class="token tag" style="color:rgb(255, 157, 0)"> </span><span class="token tag attr-name" style="color:rgb(255, 180, 84)">href</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(255, 255, 255)">=</span><span class="token tag attr-value punctuation" style="color:rgb(255, 255, 255)">"</span><span class="token tag attr-value" style="color:rgb(255, 157, 0)">/.auth/login/aad</span><span class="token tag attr-value punctuation" style="color:rgb(255, 255, 255)">"</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">Log in</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">a</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">p</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">p</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">Loading user data...</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">p</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token plain"> </span><span class="token maybe-class-name">App</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>Let's test it! First we'll start our local dev setup with <code>npm start</code>. When we browse to <code>http://localhost:5173</code>, we should see the Vite server running:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of the browser when not logged in" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-not-logged-in-7f95bc0b6967fef77a1b090aba01aab2.webp" width="1294" height="1618" class="img_ev3q"></p>
<p>If we click the <code>Login</code> link, we'll be taken to the Static Web Apps CLI local authentication emulator. We'll fill in the form and hit <code>Login</code>. This will set the <code>StaticWebAppsAuthCookie</code> cookie in our browser.</p>
<p><img decoding="async" loading="lazy" alt="screenshot of the Static Web Apps CLI local auth emulator" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-swa-cli-auth-0cb8d333b83d6796d90edf262dbe8ee6.webp" width="1294" height="1618" class="img_ev3q"></p>
<p>We'll be redirected back to the Vite server, and the cookie will be sent to the ASP.NET backend server. The ASP.NET backend server will use the cookie to authenticate the user and return the user information when the browser calls the <code>/api/me</code> endpoint. This will be displayed in the browser:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of the browser when logged in" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-logged-in-cb3e296fdd9bf86d97bbac7440e2764a.png" width="1294" height="1618" class="img_ev3q"></p>
<p>It works!</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="summing-up">Summing up<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#summing-up" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>This has been a long post, but hopefully it has been useful. We've walked through how to set up a local development environment that uses the Static Web Apps CLI local authentication emulator with ASP.NET authentication. This allows us to develop our application locally, with authentication, without being coupled to a third party authentication provider.</p>
<p>The code built in this post <a href="https://github.com/johnnyreilly/swa-local-auth-emulator-and-dotnet" target="_blank" rel="noopener noreferrer">can be found here</a>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="credits">Credits<a href="https://johnnyreilly.com/static-web-apps-cli-local-auth-emulator-with-dotnet-authentication#credits" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<ul>
<li>Server icon by Konstantin Velichko from <a href="https://thenounproject.com/browse/icons/term/server/" target="_blank" rel="noopener noreferrer">Noun Project</a> (CC BY 3.0)</li>
<li>Browser icon by syahruni from <a href="https://thenounproject.com/browse/icons/term/browser/" target="_blank" rel="noopener noreferrer">Noun Project</a> (CC BY 3.0)</li>
</ul>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Azure Static Web Apps" term="Azure Static Web Apps"/>
        <category label="Node.js" term="Node.js"/>
        <category label="ASP.NET" term="ASP.NET"/>
        <category label="Static Web Apps CLI" term="Static Web Apps CLI"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Smuggling .gitignore, .npmrc and friends in npm packages]]></title>
        <id>https://johnnyreilly.com/smuggling-gitignore-npmrc-in-npm-packages</id>
        <link href="https://johnnyreilly.com/smuggling-gitignore-npmrc-in-npm-packages"/>
        <updated>2024-12-28T17:51:36.000Z</updated>
        <summary type="html"><![CDATA[The npm publish command will not just package up .gitignore and .npmrc files. This post shows how to use zipping and unzipping with postinstall and prepare scripts to include these files into your npm package.]]></summary>
        <content type="html"><![CDATA[<p>I recently needed to include a number of <code>.gitignore</code> and <code>.npmrc</code> files in an npm package. I was surprised to find that the <code>npm publish</code> command strips these out of the published package by default. As a consequence, This broke my package, and so I needed to find a way to get round this shortcoming.</p>
<p>I ended up using zipping and unzipping with <code>postinstall</code> and <code>prepare</code> scripts to include these files into my npm package.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Smuggling dotfiles in npm packages&amp;quot; with the Node.js and npm logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-8561ca58b004dee58e58697a4bf27a8e.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>This post shows how to use zipping and unzipping with <code>postinstall</code> and <code>prepare</code> scripts to include these files into your npm package.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="a-little-backstory">A little backstory<a href="https://johnnyreilly.com/smuggling-gitignore-npmrc-in-npm-packages#a-little-backstory" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I'm currently beavering away on a "create-*-app" tool that generates new projects from a number of available templates. That tool takes the form of a CLI tool built with TypeScript, published as a package to an npm registry and consumed with <code>npx</code>. Significantly, the templates that ship with the CLI take the form of a <code>templates</code> folder in the package, and the folders in those templates include <code>.npmrc</code> and <code>.gitignore</code> files; which are key to the functionality of the templates.</p>
<p>When publishing my npm package, I discovered that the <code>.npmrc</code> and <code>.gitignore</code> files in subfolders were being stripped from the package. After a little research, I happened upon this <a href="https://github.com/npm/npm/issues/3763" target="_blank" rel="noopener noreferrer">GitHub issue about npm</a> which describes some of the behaviour I was seeing. After a touch more digging, I came to understand that this behaviour is a result of npm treating the <code>.gitignore</code> and <code>.npmrc</code> files as configuration files rather than part of the package's intended content.</p>
<p>However, given these files are essential to the templates' functionality, I needed to find a way to include them in the package.</p>
<p>I mused with explicitly including the specific files in the <code>files</code> section of the <code>package.json</code> file, but this would have been a maintenance headache. I wanted a more automated solution. Given that I have a single "special" folder called <code>templates</code> that contains all the templates, I pondered whether I could zip the folder on publish and unzip it on install. This would allow me to include the <code>.gitignore</code> and <code>.npmrc</code> files in the templates and have them copied into the new project when the template was used. And if there was another other curious behaviour around publishing, this solution should cover that too.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="prepare-and-postinstall-scripts"><code>prepare</code> and <code>postinstall</code> scripts<a href="https://johnnyreilly.com/smuggling-gitignore-npmrc-in-npm-packages#prepare-and-postinstall-scripts" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I achieved this by using <code>prepare</code> and <code>postinstall</code> scripts in the <code>package.json</code> file.</p>
<p>The <code>prepare</code> and <code>postinstall</code> scripts are two of the lifecycle scripts that npm runs when installing a package. The <code>prepare</code> script runs before the package is packaged and published, and the <code>postinstall</code> script runs after the package is installed. I opted to use these scripts to zip and unzip the <code>templates</code> folder in my package.</p>
<p>I performed the actual zipping and unzipping with some Node.js scripts. We'll look into the implementation of these scripts in a moment, but first please note the scripts we added to the <code>package.json</code> file:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"scripts"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"postinstall"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"node ./scripts/postinstall.js"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"prepare"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"node ./scripts/prepare.js"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><br></span></code></pre></div></div>
<p>These scripts contain the paths to the Node.js scripts that perform the zipping and unzipping. The <code>postinstall</code> script runs after the package is installed, and the <code>prepare</code> script runs before the package is packaged and published.</p>
<p>When it comes to zipping and unzipping, I used the <a href="https://github.com/cthackers/adm-zip" target="_blank" rel="noopener noreferrer"><code>adm-zip</code></a> package. This package provides a simple API for zipping and unzipping files and folders.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="preparejs"><code>prepare.js</code><a href="https://johnnyreilly.com/smuggling-gitignore-npmrc-in-npm-packages#preparejs" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We'll first look at the <code>prepare.js</code> script. This script zips the <code>templates</code> folder in the package into a <code>templates.zip</code> file. The script then writes the zip file to the package's root directory.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">AdmZip</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'adm-zip'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token imports">fs</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'node:fs'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token imports"> fileURLToPath </span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'node:url'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">packTemplates</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'prepare running - packing templates'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> templatesZipPath </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fileURLToPath</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">URL</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'../templates.zip'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">meta</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> templatesDir </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fileURLToPath</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">URL</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'../templates'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">meta</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> zip </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">AdmZip</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">removing existing </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">templatesZipPath</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  fs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">rmSync</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">templatesZipPath</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token literal-property property" style="color:rgb(255, 157, 0)">force</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">adding </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">templatesDir</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> to zip file</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  zip</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">addLocalFolder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">templatesDir</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">writing zip to </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">templatesZipPath</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  zip</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">writeZip</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">templatesZipPath</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">packTemplates</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>It also removes any existing <code>templates.zip</code> file in the package's root directory before creating a new one. This is to ensure that the zip file is always up to date.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="postinstalljs"><code>postinstall.js</code><a href="https://johnnyreilly.com/smuggling-gitignore-npmrc-in-npm-packages#postinstalljs" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Now we'll look at the <code>postinstall.js</code> script. This script unzips the <code>templates.zip</code> file in the package into a <code>templates</code> folder. The script then writes the unzipped folder to the package's root directory.</p>
<div class="language-js codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-js codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token imports maybe-class-name">AdmZip</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'adm-zip'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token imports">fs</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'node:fs'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token imports"> fileURLToPath </span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'node:url'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">extractTemplates</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'postinstall running - extracting templates'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> templatesZipPath </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fileURLToPath</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">URL</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'../templates.zip'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">meta</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> templatesDir </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fileURLToPath</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">URL</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'../templates'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword module" style="color:rgb(255, 157, 0)">import</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">meta</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token property-access">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">let</span><span class="token plain"> templatesExistsAlready </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword control-flow" style="color:rgb(255, 157, 0)">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    fs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">accessSync</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">templatesDir</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword control-flow" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    templatesExistsAlready </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword control-flow" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">templatesExistsAlready</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'templates already extracted'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword control-flow" style="color:rgb(255, 157, 0)">return</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">extracting from </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">templatesZipPath</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> to </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">templatesDir</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> extractZip </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">AdmZip</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">templatesZipPath</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  extractZip</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">extractAllTo</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">templatesDir</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/* overwrite */</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'templates extracted'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">extractTemplates</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>You'll notice that the script checks whether the <code>templates</code> folder already exists before unzipping the <code>templates.zip</code> file. This is to ensure that the folder is only unzipped once.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/smuggling-gitignore-npmrc-in-npm-packages#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>So here we have a method for including <code>.gitignore</code> and <code>.npmrc</code> files in an npm package. By using zipping and unzipping with <code>postinstall</code> and <code>prepare</code> scripts, we can include these files in the package and have them copied into the new project when the package is installed.</p>
<p>My example is a <code>templates</code> folder - yours could be anything. And likewise if you have other files that are being stripped from your package, you could use this method to include them too.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Node.js" term="Node.js"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Azure DevOps: pull requests and dynamic required reviewers]]></title>
        <id>https://johnnyreilly.com/azure-devops-pull-requests-dynamic-required-reviewers</id>
        <link href="https://johnnyreilly.com/azure-devops-pull-requests-dynamic-required-reviewers"/>
        <updated>2025-06-27T19:30:08.000Z</updated>
        <summary type="html"><![CDATA[How to have dynamically assigned required reviewers for a pull request in Azure DevOps using build validations and the Azure DevOps Client for Node.js.]]></summary>
        <content type="html"><![CDATA[<p>Have you ever wanted to have required reviewers for a pull request in Azure DevOps? Probably. And that's an inbuilt feature of Azure DevOps. By using <a href="https://learn.microsoft.com/en-us/azure/devops/repos/git/branch-policies?view=azure-devops&amp;tabs=browser#automatically-include-code-reviewers" target="_blank" rel="noopener noreferrer">branch policies, you can set required reviewers for a pull request</a>. If you want to ensure the code is reviewed by the appropriate people before it is merged into the main branch, this can prove very useful.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Azure DevOps: pull requests and dynamic required reviewers&amp;quot; with an Azure DevOps logo" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-d9d792feb68580211ffd5ef79efee6b6.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>However, the required reviewers are static. You can set them up in the branch policies, but they don't change dynamically based on the code being altered or the people involved in the pull request. I spent many moons trawling the internet for an answer to this question, <a href="https://stackoverflow.com/questions/64754998/how-do-i-add-a-required-reviewer-when-people-of-a-given-team-create-a-pull-reque" target="_blank" rel="noopener noreferrer">and I found that many people were asking the same question</a>. The answer was always the same: "You can't do that."</p>
<p>However, there is a way. It is, hand on heart, marginally clunky. But the clunk is marginal, and more than acceptable. It involves co-opting build validations to achieve the desired effect. In this post, I'll show you how to do that.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="build-validations-and-required-reviewers">Build validations and required reviewers<a href="https://johnnyreilly.com/azure-devops-pull-requests-dynamic-required-reviewers#build-validations-and-required-reviewers" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Build validations in Azure DevOps are a way to ensure that code meets certain criteria before it is merged into the main branch. They are, crucially, Azure DevOps pipelines that run when a pull request is created or updated. They are typically used to ensure that the code builds successfully, tests pass, linting succeeds etc. Build validations are set up in the branch policies for a repository. It's pretty typical for a repository to have a build validations.</p>
<p>The crucial thing to note is that, typically, <strong>build validations must pass before a pull request can be completed</strong>. That's how they provide their value; as a control to prevent changes breaking the codebase. What we're going to do, is use this blocking aspect to our advantage. We'll include a new stage in our build validation pipeline that, each time it runs, does one of the following:</p>
<ol>
<li>Dynamically adds a required reviewer to the pull request, if appropriate. The way we decide which reviewers are dynamically added, if any, is down to us to determine. It's entirely flexible. It could be based on the code being changed or the people involved in the pull request, or indeed something else. If no reviewer is added, the pipeline will pass. But if a reviewer is added to the pull request, the pipeline will be made to fail.</li>
<li>If a reviewer is determined as required, and has already been assigned, check if the reviewer has approved the pull request. If they have approved, the pipeline will pass. If they haven't approved the pull request, the pipeline will fail.</li>
</ol>
<p>The thing to pay attention to is that the pipeline will fail if dynamically assigned required reviewers have not given their approval by the end of the pipeline run. This applies equally if the pipeline is running for the first time against a pull request and assigning the reviewers. <strong>This means that the pull request cannot be completed until any dynamically assigned required reviewers have approved it.</strong></p>
<p>This is the part that makes your risk and audit teams happy. You cannot circumvent the required reviewers; the pipeline failing will prevent the pull request from being merged / completed until the required reviewers have approved it. This is a way to ensure that the code is reviewed by the appropriate people before it is merged into the main branch.</p>
<p>I mentioned "clunky" earlier. The clunkiness comes from the need to rerun the build validation pipeline in the Azure DevOps UI when the approval has been given. This is because there is no way (that I'm aware of) to trigger the build validation pipeline when a reviewer approval has been provided. So, if the required reviewers approve the pull request, you will need to rerun the build validation pipeline to ensure that it passes and the pull request can be completed.</p>
<p>As long as the failing pipeline provides a clear message about what is required, this is a small price to pay for the ability to have dynamic required reviewers.</p>
<p>Now I've convinced you that this is a good idea, let's look at how to implement it.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="co-opting-your-existing-build-validation-pipeline">Co-opting your existing build validation pipeline<a href="https://johnnyreilly.com/azure-devops-pull-requests-dynamic-required-reviewers#co-opting-your-existing-build-validation-pipeline" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I'm making an assumption that you already have a build validation pipeline set up for your repository. If you don't, you'll need to set one up first.</p>
<p>To your existing build validation pipeline, you'll need to add a new stage that will run the code to dynamically assign required reviewers:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">stage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> DynamicRequiredReviewers</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Dynamic required reviewers</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">dependsOn</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># This stage does not depend on any other stages and so will run in parallel with the others</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">job</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">task</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> NodeTool@0</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">versionSpec</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">22</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Install Node.js</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">bash</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> npm ci</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Install dependencies'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">workingDirectory</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'scripts/dynamic-required-reviewers'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">bash</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> npm test</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Run tests'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">workingDirectory</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'scripts/dynamic-required-reviewers'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">bash</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> npm start </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">sat $(System.AccessToken) </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">pullRequestId $(System.PullRequest.PullRequestId) </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">organization $(System.CollectionUri) </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">repositoryName $(Build.Repository.Name) </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">projectName $(System.TeamProject)'</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Validate claims'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">workingDirectory</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'scripts/dynamic-required-reviewers'</span><br></span></code></pre></div></div>
<p>You can see reference to the <code>scripts/dynamic-required-reviewers</code> directory. This is where we'll put the code that will dynamically assign required reviewers. The code will run in a Node.js environment, so we'll need to install Node.js and the dependencies for the code to run.</p>
<p>You can also see that we're using the <code>System.AccessToken</code> and <code>System.PullRequest.PullRequestId</code> variables. The <code>System.AccessToken</code> is a token that allows the code to interact with the Azure DevOps API, and the <code>System.PullRequest.PullRequestId</code> is the ID of the pull request that the build validation pipeline is running against. We'll use these in our code to dynamically assign required reviewers to the pull request.</p>
<p>We also use the <code>System.CollectionUri</code>, <code>Build.Repository.Name</code>, and <code>System.TeamProject</code> variables to get the organization, repository name, and project name respectively. These will be used to make API calls to Azure DevOps with our token.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="setting-up-the-code-to-dynamically-assign-required-reviewers">Setting up the code to dynamically assign required reviewers<a href="https://johnnyreilly.com/azure-devops-pull-requests-dynamic-required-reviewers#setting-up-the-code-to-dynamically-assign-required-reviewers" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>You'll need to create the <code>scripts/dynamic-required-reviewers</code> directory. In there we're going to add a <code>package.json</code> file to manage our dependencies:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"name"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"dynamic-required-reviewers"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"version"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"1.0.0"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"scripts"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"build"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"tsc"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"start"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"npm run build &amp;&amp; node dist/index.js"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"license"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"ISC"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"module"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"dependencies"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"azure-devops-node-api"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^15.1.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"devDependencies"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"@types/node"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^22.0.0"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"typescript"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^5.8.3"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>We also need a <code>tsconfig.json</code> file to configure TypeScript:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"compilerOptions"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"allowJs"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"declaration"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"declarationMap"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"esModuleInterop"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"module"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"NodeNext"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"moduleResolution"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"NodeNext"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"noEmit"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"resolveJsonModule"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"skipLibCheck"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"sourceMap"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"strict"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"target"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"ES2022"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"outDir"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"dist"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"include"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">"src"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>Now we'll add our <code>src/index.ts</code> file where we'll put our code to dynamically assign required reviewers.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> parseArgs </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'node:util'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">*</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> nodeApi </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'azure-devops-node-api'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> GitPullRequest </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'azure-devops-node-api/interfaces/GitInterfaces.js'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> IGitApi </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'azure-devops-node-api/GitApi.js'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">main</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> args </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">parseArgs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    options</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      pat</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'p'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token constant" style="color:rgb(250, 208, 0)">ADO_PAT</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      sat</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'s'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token constant" style="color:rgb(250, 208, 0)">ADO_SAT</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      pullRequestId</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'i'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> process</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token constant" style="color:rgb(250, 208, 0)">ADO_PULL_REQUEST_ID</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      organization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'o'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      repositoryName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'r'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      projectName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'string'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> short</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'j'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> pullRequestId </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">parseInt</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">args</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">pullRequestId </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'0'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">10</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> pat </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">pat </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> sat </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">sat </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// https://dev.azure.com/johnnyreilly/ -&gt; johnnyreilly</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> organization </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">args</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">organization </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">""</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">replace</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"https://dev.azure.com/"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">""</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">replace</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"/"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">""</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> repositoryName </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">repositoryName </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> projectName </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> args</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">values</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">projectName </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> webApi </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">makeWebApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    pat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    sat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    organization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> gitApi </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> webApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">getGitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> pullRequest </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> gitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">getPullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/* repositoryId */</span><span class="token plain"> repositoryName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/* pullRequestId */</span><span class="token plain"> pullRequestId</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/* project */</span><span class="token plain"> projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/* maxCommentLength */</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/* skip */</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/* top */</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/* includeCommits */</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/* includeWorkItemRefs */</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> requiredReviewerName </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">determineRequiredReviewerName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">requiredReviewerName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string" style="color:rgb(165, 255, 144)">'✅ No required reviewer was deemed necessary. No action needed.'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> requiredReviewer </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">searchIdentityForReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    pat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    sat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    organization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    searchTerm</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> requiredReviewerName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">requiredReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorMessage </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">❌ Failed to look up reviewer: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">requiredReviewerName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">determineAction</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    requiredReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    gitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    repositoryName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">determineRequiredReviewerName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pullRequest</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> GitPullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// This is a placeholder function. You should implement your logic to determine the required reviewer name.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Required Reviewer Name'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Replace with actual logic</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">makeWebApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  organization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  sat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  organization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pat</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  sat</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">pat </span><span class="token operator" style="color:rgb(255, 157, 0)">&amp;&amp;</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">sat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string" style="color:rgb(165, 255, 144)">'Either a Personal Access Token (PAT) or a Service Account Token (SAT) must be provided.'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> webApi </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">nodeApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">WebApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">https://dev.azure.com/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">organization</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    pat</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token plain"> nodeApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">getPersonalAccessTokenHandler</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          pat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** allowCrossOriginAuthentication */</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> nodeApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">getHandlerFromToken</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          sat </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** allowCrossOriginAuthentication */</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> webApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Identity</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  providerDisplayName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/**</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic"> * This searches the organization's identity system directly</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic"> * based on https://learn.microsoft.com/en-us/rest/api/azure/devops/ims/identities/read-identities?view=azure-devops-rest-7.1&amp;tabs=HTTP</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">searchIdentityForReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  sat</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  searchTerm</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  organization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pat</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  sat</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  searchTerm</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  organization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token plain">Identity </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Use the identities API endpoint for broader search</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> searchUrl </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">https://vssps.dev.azure.com/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">organization</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/_apis/identities?searchFilter=General&amp;filterValue=</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">encodeURIComponent</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token template-string interpolation">      searchTerm</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token template-string interpolation"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token template-string interpolation">    </span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">&amp;api-version=7.1-preview.1</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> response </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fetch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">searchUrl</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      method</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'GET'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      headers</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        Authorization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Basic </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">Buffer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">from</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string interpolation template-string string" style="color:rgb(165, 255, 144)">PAT:</span><span class="token template-string interpolation template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation template-string interpolation">pat </span><span class="token template-string interpolation template-string interpolation operator" style="color:rgb(255, 157, 0)">||</span><span class="token template-string interpolation template-string interpolation"> sat</span><span class="token template-string interpolation template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string interpolation template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">toString</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token template-string interpolation">          </span><span class="token template-string interpolation string" style="color:rgb(165, 255, 144)">'base64'</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token template-string interpolation"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token template-string interpolation">        </span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        Accept</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'application/json'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'Content-Type'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'application/json'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">warn</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Identity search failed: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">response</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">status</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">response</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">statusText</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> data </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">json</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">value </span><span class="token operator" style="color:rgb(255, 157, 0)">&amp;&amp;</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">length </span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">0</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> identity</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Identity </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token number" style="color:rgb(255, 98, 140)">0</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Take the first match</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">✅ Found identity via search:</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">   ID: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">identity</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">id</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">   Display Name: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">identity</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">providerDisplayName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> identity</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">warn</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">⚠️  No identities found for: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">searchTerm</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">❌ Error searching identities for </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">searchTerm</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">:</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> voteValues </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  approved</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">10</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  approvedWithSuggestions</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">5</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  noVote</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">0</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  waitingForAuthor</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">-</span><span class="token number" style="color:rgb(255, 98, 140)">5</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  rejected</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">-</span><span class="token number" style="color:rgb(255, 98, 140)">10</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">determineAction</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  requiredReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  gitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  repositoryName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  requiredReviewer</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Identity</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pullRequest</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> GitPullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  gitApi</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> IGitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  repositoryName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token keyword" style="color:rgb(255, 157, 0)">void</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> assignedReviewer </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">reviewers</span><span class="token operator" style="color:rgb(255, 157, 0)">?.</span><span class="token function" style="color:rgb(250, 208, 0)">find</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">reviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> reviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">id </span><span class="token operator" style="color:rgb(255, 157, 0)">===</span><span class="token plain"> requiredReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> requiredReviewIsAssigned </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    assignedReviewer </span><span class="token operator" style="color:rgb(255, 157, 0)">&amp;&amp;</span><span class="token plain"> assignedReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">isRequired</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> hasBeenApprovedByRequiredReviewer </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    assignedReviewer </span><span class="token operator" style="color:rgb(255, 157, 0)">&amp;&amp;</span><span class="token plain"> assignedReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">vote </span><span class="token operator" style="color:rgb(255, 157, 0)">===</span><span class="token plain"> voteValues</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">approved</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">requiredReviewIsAssigned </span><span class="token operator" style="color:rgb(255, 157, 0)">&amp;&amp;</span><span class="token plain"> hasBeenApprovedByRequiredReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">✅ Reviewer with ID </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token template-string interpolation">        assignedReviewer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">displayName </span><span class="token template-string interpolation operator" style="color:rgb(255, 157, 0)">??</span><span class="token template-string interpolation"> assignedReviewer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">id</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token template-string interpolation">      </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> is already assigned and has approved the pull request. No action needed.</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">else</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">requiredReviewIsAssigned</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorMessage </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">⚠️ Reviewer with ID </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token template-string interpolation">      assignedReviewer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">displayName </span><span class="token template-string interpolation operator" style="color:rgb(255, 157, 0)">??</span><span class="token template-string interpolation"> assignedReviewer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">id</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token template-string interpolation">    </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> is already assigned but has not approved the pull request.</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">else</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">⚠️ Reviewer with ID </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">requiredReviewer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">providerDisplayName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> is not yet assigned. Will assign them.</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> reviewerToBeAssigned </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> requiredReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      vote</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> voteValues</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">noVote</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      isRequired</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> gitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">createPullRequestReviewer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        reviewerToBeAssigned</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** repositoryId */</span><span class="token plain"> repositoryName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">pullRequestId</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** reviewerId */</span><span class="token plain"> reviewerToBeAssigned</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** project */</span><span class="token plain"> projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'✅ Successfully added reviewer to pull request'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorMessage </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">❌ Failed to add reviewer to pull request</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> cause</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> error </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorMessage </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Added reviewer </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">requiredReviewer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">providerDisplayName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)"> to pull request for review and approval. Once approved, please re-run the build validation and it should pass.</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> gitApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">createThread</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** commentThread */</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          comments</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              parentCommentId</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">0</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              content</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              commentType</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> CommentType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Text</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          status</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> CommentThreadStatus</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Active</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** repositoryId */</span><span class="token plain"> repositoryName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** pullRequestId */</span><span class="token plain"> pullRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">pullRequestId</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** project */</span><span class="token plain"> projectName</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"✅ Successfully add comment to pull request"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorMessage </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">❌ Failed to add comment to pull request</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> cause</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> error </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">main</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>There's a good bit of code here, so let's break it down:</p>
<ul>
<li>The <code>main</code> function is the entry point of the script. It parses the command line arguments and sets up the Azure DevOps API client.</li>
<li>The <code>makeWebApi</code> function creates an instance of the Azure DevOps Web API client using either a Personal Access Token (PAT) or a Service Account Token (SAT). You'll use a PAT for local development and a SAT in the build validation pipeline. If using a PAT it requires the scopes: <code>vso.code</code> and <code>vso.identity</code>.</li>
<li>The <code>getRequiredReviewerName</code> function is a placeholder for your logic to determine the name of your required reviewer, if any. You should implement your logic here to determine when dynamically assigned reviewers are appropriate.</li>
<li>The <code>searchIdentityForReviewer</code> function searches for the required reviewer in the Azure DevOps identity system. It uses the Azure DevOps REST API to search for identities based on a search term. Rather frustratingly, you can't directly use the Azure AD / Entra ID Graph API to search for users in Azure DevOps.</li>
<li>The <code>determineAction</code> function checks if the required reviewer is already assigned to the pull request and whether they have approved it.<!-- -->
<ul>
<li>If they have, it logs a success message.</li>
<li>If they haven't, it throws an error with a message indicating that the required reviewer needs to approve the pull request.</li>
<li>If the required reviewer is not assigned, it assigns them to the pull request and throws an error with a message indicating that the pull request requires their approval. It also adds a comment on the PR to make the required action obvious to anyone looking at the PR.</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="running-the-code">Running the code<a href="https://johnnyreilly.com/azure-devops-pull-requests-dynamic-required-reviewers#running-the-code" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>You can run the code locally to test it. You'll need to set up a Personal Access Token (PAT) with the required scopes and set the environment variables accordingly. You can then run the code using:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> start -- </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--pat</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">YOUR_PAT</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--pullRequestId</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">PULL_REQUEST_ID</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--organization</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">ORGANISATION</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--repositoryName</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">ADO_REPOSITORY_NAME</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--projectName</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">ADO_PROJECT_NAME</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/azure-devops-pull-requests-dynamic-required-reviewers#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>In this post, we've seen how to dynamically assign required reviewers for a pull request in Azure DevOps using build validations and the Azure DevOps API brought together with a little TypeScript. By co-opting your existing build validation pipeline, you can ensure that the code is reviewed by the appropriate people before it is merged into the main branch.</p>
<p>Use this. Make your risk and audit teams happy!</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="TypeScript" term="TypeScript"/>
        <category label="Azure DevOps" term="Azure DevOps"/>
        <category label="Node.js" term="Node.js"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Get Service Connections with the Azure DevOps API (REST and TypeScript)]]></title>
        <id>https://johnnyreilly.com/get-service-connections-with-azure-devops-api</id>
        <link href="https://johnnyreilly.com/get-service-connections-with-azure-devops-api"/>
        <updated>2025-01-25T08:37:30.000Z</updated>
        <summary type="html"><![CDATA[Learn how to get service connections with the Azure DevOps REST API using both curl and TypeScript.]]></summary>
        <content type="html"><![CDATA[<p>If you work with Azure Pipelines, you'll likely have come upon the need to create service connections. These are the connections to external services that your pipelines need to run. You can interrogate these connections using the Azure DevOps REST API. This post goes through how to do this; both using curl and using TypeScript.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Get Service Connections with the Azure DevOps API&amp;quot; with the Azure DevOps logo" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-635c8f7f7f2a2d2d6d61b3c45040d683.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>I'm writing this post because when I attempted to use the <a href="https://github.com/microsoft/azure-devops-node-api" target="_blank" rel="noopener noreferrer">Azure DevOps Client for Node.js</a> package to acquire them I found it lacking, and <a href="https://johnnyreilly.com/create-pipeline-with-azure-devops-api">not for the first time</a>. I am going to allow myself a little moan here; ever since Microsoft acquired GitHub, the Azure DevOps ecosystem feels like it has had insufficient investment.</p>
<p>However, as is often the case, there is a way. The Azure DevOps REST API is there for us, and with a little <code>fetch</code> we can get the job done.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="service-connections-vs-service-endpoints">Service connections vs service endpoints<a href="https://johnnyreilly.com/get-service-connections-with-azure-devops-api#service-connections-vs-service-endpoints" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Before we get into the code, let's clarify the terminology. In Azure DevOps, service connections are the connections to external services that your pipelines need to run. However, the Azure DevOps REST API refers to these as "service endpoints".</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of service connections in the Azure DevOps UI" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-azure-devops-service-connections-37041c60f0e692bbe10d70aa3510280a.webp" width="2066" height="762" class="img_ev3q"></p>
<p>So when you're looking the screenshot above and you see "service connections", remember that in the API they're referred to as "service endpoints". If there is an actual distinction between "service connections" and "service endpoints" I'm not aware of it. If you know, please do let me know!</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="curling-service-connections">Curling service connections<a href="https://johnnyreilly.com/get-service-connections-with-azure-devops-api#curling-service-connections" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Once you know that service connections are referred to as "service endpoints" in the Azure DevOps REST API, you can use the <a href="https://learn.microsoft.com/en-us/rest/api/azure/devops/serviceendpoint/endpoints/get-service-endpoints?view=azure-devops-rest-7.2&amp;tabs=HTTP" target="_blank" rel="noopener noreferrer">documentation</a> to get them. Here's a curl to get you started:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">curl</span><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--user</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token builtin class-name" style="color:rgb(250, 208, 0)">:</span><span class="token string" style="color:rgb(165, 255, 144)">'PERSONAL_ACCESS_TOKEN'</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--header</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Content-Type: application/json"</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--header</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Accept:application/json"</span><span class="token plain"> https://dev.azure.com/</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">organization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain">/</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">project</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain">/_apis/serviceendpoint/endpoints?api-version</span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token number" style="color:rgb(255, 98, 140)">7.1</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-if-i-want-to-use-typescript">What if I want to use TypeScript?<a href="https://johnnyreilly.com/get-service-connections-with-azure-devops-api#what-if-i-want-to-use-typescript" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Now that we can see there's a way to get service connections with curl, let's look at how we can do this with TypeScript. Effectively we'll want to <code>fetch</code> (instead of using curl) and statically type the response with some interfaces. Here's a function that will retrieve service connections:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAzureDevOpsServiceConnections</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  personalAccessToken</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  organization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** requires the vso.serviceendpoint scope which grants the ability to read service endpoints / service connections */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  personalAccessToken</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg "johnnyreilly" */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  organization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg "blog-demos" */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token plain">ServiceConnection</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// https://learn.microsoft.com/en-us/rest/api/azure/devops/serviceendpoint/endpoints/get-service-endpoints?view=azure-devops-rest-7.1&amp;tabs=HTTP</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> url </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">https://dev.azure.com/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">organization</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">projectName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/_apis/serviceendpoint/endpoints?api-version=7.1</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> response </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fetch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    method</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'GET'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    headers</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      Accept</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'application/json'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'Content-Type'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'application/json'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      Authorization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Basic </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">Buffer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">from</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string interpolation template-string string" style="color:rgb(165, 255, 144)">PAT:</span><span class="token template-string interpolation template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation template-string interpolation">personalAccessToken</span><span class="token template-string interpolation template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string interpolation template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">toString</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation string" style="color:rgb(165, 255, 144)">'base64'</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'X-TFS-FedAuthRedirect'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Suppress'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">HTTP error! status: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">response</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">status</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">toString</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> data </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">json</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> Wrapper</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Wrapper</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  count</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  value</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> ServiceConnection</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">ServiceConnection</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  authorization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Authorization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  createdBy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> CreatedBy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  data</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Record</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  isOutdated</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">boolean</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  isReady</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">boolean</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  isShared</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">boolean</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  modificationDate</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  modifiedBy</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> ModifiedBy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  name</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  owner</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  serviceEndpointProjectReferences</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> ServiceEndpointProjectReference</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  serviceManagementReference</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  type</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  url</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">CreatedBy</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  _links</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Links</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  descriptor</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  displayName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  imageUrl</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  uniqueName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  url</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Links</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  avatar</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Avatar</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Avatar</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  href</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Authorization</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  parameters</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Record</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  scheme</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">ServiceEndpointProjectReference</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  description</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  name</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectReference</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> ProjectReference</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">ProjectReference</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  name</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">ModifiedBy</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  displayName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>The function <code>getAzureDevOpsServiceConnections</code> is the one you'll want to call. It takes the following inputs:</p>
<ul>
<li><code>personalAccessToken</code>: the personal access token you've created in Azure DevOps with the <code>vso.serviceendpoint</code> scope. You could equally use the <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&amp;tabs=yaml#systemaccesstoken" target="_blank" rel="noopener noreferrer"><code>System.AccessToken</code></a> that's available in your pipeline if that was appropriate.</li>
<li><code>organization</code>: the name of your Azure DevOps organization</li>
<li><code>projectName</code>: the name of the project you're interested in</li>
</ul>
<p>The function returns an array of <code>ServiceConnection</code> objects. You'll note that the API actually returns a <code>Wrapper</code> object that contains a <code>count</code> and an array of <code>ServiceConnection</code> objects. This isn't actually useful for my purposes, so I've just returned the array of <code>ServiceConnection</code> objects.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/get-service-connections-with-azure-devops-api#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Honestly the hardest part about writing this post was being sure that "service connections" and "service endpoints" were the same thing. Truly naming things is hard.</p>
<p>Hopefully this post has helped you get the service connections you need. And as I mentioned earlier, if you know an actual distinction between "service connections" and "service endpoints" please do let me know!</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Azure Pipelines" term="Azure Pipelines"/>
        <category label="Azure DevOps" term="Azure DevOps"/>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Azure DevOps: using DefaultAzureCredential in an Azure Pipeline with AzureCLI@2]]></title>
        <id>https://johnnyreilly.com/azure-devops-with-defaultazurecredential</id>
        <link href="https://johnnyreilly.com/azure-devops-with-defaultazurecredential"/>
        <updated>2025-07-19T14:03:16.000Z</updated>
        <summary type="html"><![CDATA[How to use DefaultAzureCredential in an Azure DevOps pipeline in the same way as you can locally.]]></summary>
        <content type="html"><![CDATA[<p>I frequently build scripts that work against Azure resources using the <a href="https://github.com/Azure/azure-sdk-for-js" target="_blank" rel="noopener noreferrer">Azure SDK for JavaScript</a>. I use the <a href="https://learn.microsoft.com/en-gb/dotnet/azure/sdk/authentication/credential-chains?tabs=dac#defaultazurecredential-overview" target="_blank" rel="noopener noreferrer"><code>DefaultAzureCredential</code></a> to authenticate against Azure resources - this is also available in other platforms such as .NET.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Azure DevOps: using DefaultAzureCredential in an Azure Pipeline with AzureCLI@2&amp;quot; with an Azure / Azure DevOps logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-c5f5559b43b1b3e7c3219905fa5e92ae.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>The <code>DefaultAzureCredential</code> is a great way to authenticate locally; I can <code>az login</code> and then run my script, safe in the knowledge that the <code>DefaultAzureCredential</code> will authenticate successfully. However, how can I use the <code>DefaultAzureCredential</code> in an Azure DevOps pipeline?</p>
<p>This post will show you how to use the <code>DefaultAzureCredential</code> in an Azure DevOps pipeline, specifically by using the <code>AzureCLI@2</code> task.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-is-defaultazurecredential">What is <code>DefaultAzureCredential</code>?<a href="https://johnnyreilly.com/azure-devops-with-defaultazurecredential#what-is-defaultazurecredential" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>To quote the documentation:</p>
<blockquote>
<p><code>DefaultAzureCredential</code> is an opinionated, preconfigured chain of credentials. It's designed to support many environments, along with the most common authentication flows and developer tools. In graphical form, the underlying chain looks like this:</p>
<p><img decoding="async" loading="lazy" alt="an image representing the various entries in the chain" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/defaultazurecredentialauthflow-548aef2d21b3b534c26973884635b9f1.svg" width="1445" height="274" class="img_ev3q"></p>
</blockquote>
<p>The first credential in the chain is <a href="https://learn.microsoft.com/en-us/dotnet/api/azure.identity.environmentcredential?view=azure-dotnet&amp;preserve-view=true" target="_blank" rel="noopener noreferrer"><code>EnvironmentCredential</code></a>, which looks for environment variables to authenticate. This means that if you set the right environment variables, you can use <code>DefaultAzureCredential</code> and it will authenticate with them.</p>
<p>The specific environment variables that <code>DefaultAzureCredential</code> looks for are:</p>
<ul>
<li><code>AZURE_TENANT_ID</code> - The Microsoft Entra tenant (directory) ID.</li>
<li><code>AZURE_CLIENT_ID</code> - The client (application) ID of an App Registration in the tenant.</li>
<li><code>AZURE_CLIENT_SECRET</code> - A client secret that was generated for the App Registration.</li>
</ul>
<p>The fifth credential in the chain is <a href="https://learn.microsoft.com/en-us/dotnet/api/azure.identity.azureclicredential?view=azure-dotnet&amp;preserve-view=true" target="_blank" rel="noopener noreferrer"><code>AzureCliCredential</code></a>, which uses the Azure CLI to authenticate. This means that if you have already authenticated using <code>az login</code>, you can use <code>DefaultAzureCredential</code> without setting any environment variables.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-have-you-told-me-about-environmentcredential-and-azureclicredential">Why have you told me about <code>EnvironmentCredential</code> and <code>AzureCliCredential</code>?<a href="https://johnnyreilly.com/azure-devops-with-defaultazurecredential#why-have-you-told-me-about-environmentcredential-and-azureclicredential" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Great question! When I'm developing locally, I can use <code>DefaultAzureCredential</code> without thinking further about it. I run <code>az login</code> and then run my script. <code>DefaultAzureCredential</code> will do what I need.</p>
<p>You can make use of <code>AzureCliCredential</code>, both locally and in an Azure DevOps pipeline, and it can be used for authentication as long as you have a service connection set up in Azure DevOps that uses the same credentials as your local <code>az login</code>. Should you need it, <code>EnvironmentCredential</code> is another option, and we'll demonstrate it too.</p>
<p>The nice thing about <code>DefaultAzureCredential</code> is that it supports both approaches, so you can use it in your code without having to specifically cater for the credential type being used.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-the-azurecli2-task-with-azureclicredential">Using the <code>AzureCLI@2</code> task with <code>AzureCliCredential</code><a href="https://johnnyreilly.com/azure-devops-with-defaultazurecredential#using-the-azurecli2-task-with-azureclicredential" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>To use the Azure CLI in an Azure DevOps pipeline, you can use the <a href="https://learn.microsoft.com/en-us/azure/devops/pipelines/tasks/reference/azure-cli-v2?view=azure-pipelines" target="_blank" rel="noopener noreferrer"><code>AzureCLI@2</code></a> task. This task allows you to run Azure CLI commands in your pipeline, given it is configured to use a service connection that has the necessary permissions to access your Azure resources.</p>
<p>Consider the following example pipeline YAML:</p>
<div class="language-yml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yml codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">task</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> AzureCLI@2</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Run with AzureCliCredential</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">azureSubscription</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> myServiceConnection </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># this is the name of your Azure service connection in Azure DevOps</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">scriptType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> bash</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">scriptLocation</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> inlineScript</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">inlineScript</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> npm start </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># where `npm start` is your command that uses DefaultAzureCredential</span><br></span></code></pre></div></div>
<p>The above will run the <code>npm start</code> command in the context of the Azure CLI. We won't document it here, but imagine the <code>npm start</code> command runs a Node.js script which makes use of <code>DefaultAzureCredential</code>. The supplied service connection will authenticate using the credentials of the  associated service principal. When the code runs and <code>DefaultAzureCredential</code> is used, the <code>AzureCliCredential</code> will be used to authenticate, as at this point the pipeline is effectively a logged user with the Azure CLI.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-the-azurecli2-task-with-environmentcredential">Using the <code>AzureCLI@2</code> task with <code>EnvironmentCredential</code><a href="https://johnnyreilly.com/azure-devops-with-defaultazurecredential#using-the-azurecli2-task-with-environmentcredential" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>If, for whatever reason, you want to use <code>EnvironmentCredential</code> in your Azure DevOps pipeline, you can do so by setting the necessary environment variables in the pipeline. I don't have a specific reason to do this, but you may. To achieve this, you can modify the approach as follows:</p>
<div class="language-yml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yml codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">task</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> AzureCLI@2</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Set service principal variables</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">azureSubscription</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> myServiceConnection </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># this is the name of your Azure service connection in Azure DevOps</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">scriptType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> bash</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">scriptLocation</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> inlineScript</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">addSpnToEnvironment</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 98, 140)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">inlineScript</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">|</span><span class="token scalar string" style="color:rgb(165, 255, 144)"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">      echo "##vso[task.setvariable variable=AZURE_CLIENT_ID;issecret=true]${servicePrincipalId}"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">      echo "##vso[task.setvariable variable=AZURE_CLIENT_SECRET;issecret=true]${servicePrincipalKey}"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">      echo "##vso[task.setvariable variable=AZURE_SUBSCRIPTION_ID;issecret=true]$(az account show --query 'id' -o tsv)"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">      echo "##vso[task.setvariable variable=AZURE_TENANT_ID;issecret=true]${tenantId}"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">bash</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> npm start</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Run with EnvironmentCredential'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">#&nbsp;see https://github.com/Azure/azure-sdk-for-net/blob/main/sdk/identity/Azure.Identity/README.md#environment-variables</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">AZURE_CLIENT_ID</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $(AZURE_CLIENT_ID)</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">AZURE_CLIENT_SECRET</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $(AZURE_CLIENT_SECRET)</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">AZURE_SUBSCRIPTION_ID</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $(AZURE_SUBSCRIPTION_ID)</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">AZURE_TENANT_ID</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $(AZURE_TENANT_ID) </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># this is optional and not necessary for EnvironmentCredential to work</span><br></span></code></pre></div></div>
<p>You can see this is a little different from the previous example. We're now using the <code>AzureCLI@2</code> task to set the necessary environment variables for <code>DefaultAzureCredential</code> to work. Specifically, <code>AZURE_CLIENT_ID</code>, <code>AZURE_CLIENT_SECRET</code> and <code>AZURE_SUBSCRIPTION_ID</code>.</p>
<p>The <code>addSpnToEnvironment</code> input of the task is set to <code>true</code>, which ensures that the service principal's credentials are added to the environment. We then map those credentials to the names that the <code>DefaultAzureCredential</code> expects and write them to the pipeline to be used in the next task.</p>
<p>The second task is a simple bash task that runs <code>npm start</code>, but it now has an <code>env</code> section that sets the necessary environment variables for <code>DefaultAzureCredential</code> to work. The environment variables are set using the variables exposed by the previous task.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/azure-devops-with-defaultazurecredential#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>In this post, we explored how to use <code>DefaultAzureCredential</code> in an Azure DevOps pipeline, specifically when using the <code>AzureCLI@2</code> task. We discussed the two main approaches: using <code>AzureCliCredential</code> and <code>EnvironmentCredential</code>, and how to configure the pipeline to support both.</p>
<p>It's worth noting that whilst my own use case is JavaScript / TypeScript, the same principles apply to other languages that support <code>DefaultAzureCredential</code>, such as .NET. The key takeaway is that you can use <code>DefaultAzureCredential</code> in an Azure DevOps pipeline just as easily as you can locally, allowing for a consistent development and deployment experience.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="TypeScript" term="TypeScript"/>
        <category label="Azure DevOps" term="Azure DevOps"/>
        <category label="Node.js" term="Node.js"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[TypeScript is going Go: Why it’s the pragmatic choice]]></title>
        <id>https://johnnyreilly.com/typescript-go-pragmatic-choice</id>
        <link href="https://johnnyreilly.com/typescript-go-pragmatic-choice"/>
        <updated>2025-05-19T21:12:41.000Z</updated>
        <summary type="html"><![CDATA[TypeScript is being ported to Go. This is a reflection on the port, and what it means for the TypeScript ecosystem.]]></summary>
        <content type="html"><![CDATA[<p>TypeScript is <a href="https://devblogs.microsoft.com/typescript/typescript-native-port/" target="_blank" rel="noopener noreferrer">being ported to Go</a>. This is known as "TypeScript 7" (it is currently on 5.8). It's quite likely that you know this by now, as there have been excellent communications from the TypeScript team in a variety of forums. In fact, hats off to the team; it's been an object lesson in how to communicate well; straightforward, clear and open.</p>
<p><img decoding="async" loading="eager" alt="title image showing TypeScript and Go logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-e57aba955c5a744128f89d00321ce4db.avif" width="895" height="597" fetchpriority="high" class="img_ev3q"></p>
<p>There's no shortage of content out there detailing what is known about the port. This piece is not that. Rather, it's the reflections of two people in the TypeScript community. What our thoughts, feelings and reflections on the port are.</p>
<p>It's going to be a somewhat unstructured wander through our reactions and hopes. Buckle up for opinions and feelings.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="who-are-john-reilly-and-ashley-claymore">Who are John Reilly and Ashley Claymore?<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#who-are-john-reilly-and-ashley-claymore" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>John Reilly is a software engineer and an early adopter of TypeScript. He worked on Definitely Typed, the home of high quality type definitions which allow the integration of TypeScript and JavaScript. John wrote the <a href="https://johnnyreilly.com/definitely-typed-the-movie" target="_blank" rel="noopener noreferrer">history of Definitely Typed</a> and featured in the TypeScript documentary. He also worked (and works) on <code>ts-loader</code>, the webpack loader for TypeScript. In his day job, he works at Investec, a South African bank and is based in London. The greatest city on earth (in his opinion).</p>
<p>Ashley is a software engineer who has the pleasure of living not too far from John, occasionally joining him on his morning walks, where we can kick start our day talking about TypeScript together. Ashley first started writing TypeScript when it was on version 1.8 and has thoroughly enjoyed following its evolution. He has <a href="https://github.com/microsoft/TypeScript/pulls?q=author%3Aacutmore+is%3Aclosed" target="_blank" rel="noopener noreferrer">contributed to TypeScript</a> and works at Bloomberg as part of the 'JavaScript Infrastructure &amp; Tooling' team. Opinions are his own.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="was-a-port-necessary">Was a port necessary?<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#was-a-port-necessary" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I mean, weren't we happy with each other anyway? Just as we were? Yes, but also no.</p>
<p>If you're in the JavaScript / TypeScript ecosystem, recent years have been notable for the number of projects that have appeared to support JavaScript related development, but built in non-JavaScript languages. We've had <a href="https://esbuild.github.io/" target="_blank" rel="noopener noreferrer">esbuild</a>, written in Go. We've had <a href="https://swc.rs/" target="_blank" rel="noopener noreferrer">SWC</a>, written in Rust. We've had <a href="https://bun.sh/" target="_blank" rel="noopener noreferrer">Bun</a>, written in Zig. We've had <a href="https://deno.com/" target="_blank" rel="noopener noreferrer">Deno</a>, written in Rust.</p>
<p>The list goes on, and was getting longer and longer. All of these increased performance, which was and is a wonderful thing. We'll talk more about performance later. One hold-out was TypeScript. It remained being written in TypeScript. Whilst performance improvements did happen, and were an area of focus for the team, the level of improvements that happened were incremental; not transformative.</p>
<p>You could see the impatience in the community, as people started making their own efforts to speed up TypeScript by building their own implementations. Most notable here was <a href="https://github.com/kdy1/" target="_blank" rel="noopener noreferrer">DongYoon Kang</a>; the creator of SWC. SWC, amongst other things, implemented the transpilation aspect of TypeScript. Donny decided to see if he could implement the type checker as well, again using Rust. He then switched to <a href="https://kdy1.dev/2022-1-26-porting-tsc-to-go" target="_blank" rel="noopener noreferrer">attempting a port using Go</a>. After some time he then switched back to <a href="https://kdy1.dev/2022-10-27-open-sourcing-stc" target="_blank" rel="noopener noreferrer">trying to implement in Rust</a>.</p>
<p>It didn't end up succeeding, but the fact there were people out there willing to try this demonstrated the desire for performance in the community. At some point a port was likely to succeed, and if it wasn't driven by the actual TypeScript team it would probably have landed the ecosystem in a tricky situation. A port of TypeScript, to a language other than TypeScript, seemed to be inevitable. And here we are.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="performance">Performance<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#performance" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>It's useful to think about what the Go port meaningfully changes about TypeScript. Josh Goldberg has <a href="https://www.learningtypescript.com/articles/what-is-typescript" target="_blank" rel="noopener noreferrer">provided a useful framing on different aspects of TypeScript</a>. It's four things:</p>
<ol>
<li>Language</li>
<li>Type Checker</li>
<li>Compiler</li>
<li>Language services</li>
</ol>
<p>The language is unaffected by the port. Syntax is unchanged. You'll still be writing <code>type</code>s and <code>interfaces</code>s as you were before. No difference.</p>
<p>The same applies to the checks that the type checker is performing. The code that was detected as an error before will still fail to type check with TypeScript 7:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> i</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'not actually a number'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// ts: Type 'string' is not assignable to type 'number'</span><br></span></code></pre></div></div>
<p>This is where the differences begin. The type checker, compiler, and the language services do change. They become an order of magnitude faster.</p>
<p>Put your hands up if you don't care about performance. That's right, no hands went up. We all care about performance. If you ever have the misfortune to work with technology that lags, which breaks you out of flow as you are working, you notice it. It's almost the only thing you can notice.</p>
<p>The TypeScript team has always cared about performance, particularly in the area of development tooling. Anders Hejlsberg in particular has mentioned in interviews, the need for language servers to provide fast feedback as people work. Something measured in milliseconds and not seconds.</p>
<p>What are the implications of these changes to the TypeScript ecosystem? Put simply: a faster VS Code and faster builds.</p>
<p>Where John works, at Investec, there are many engineers who use VS Code, and spend part of their engineering life writing TypeScript and JavaScript. All those engineers will benefit from a snappier development experience. When they open up a project in VS Code, the time taken for the language service to wake up will drop dramatically. As they refactor their code, the experience will be faster. The "time to red squiggly line" metric will decrease. And that's a good thing.</p>
<p>As a consequence, engineers should be incrementally more effective, given that there are fewer pauses in their workflow.</p>
<p>The same incremental gain applies to builds. As our engineers build applications, they run TypeScript builds on their machines and in a Continuous Integration context. These will all be faster than they were before. We'll continually experience a performance improvement which is a benefit.</p>
<p>This, of course, is not Investec specific. Rather this is a general improvement that everyone will benefit from. Across the world, wherever anyone writes and builds TypeScript, they will do so faster.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-typescript-team-will-write-less-typescript">The TypeScript team will write less TypeScript<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#the-typescript-team-will-write-less-typescript" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Many languages have <a href="https://en.wikipedia.org/wiki/Bootstrapping_(compilers)" target="_blank" rel="noopener noreferrer">bootstrapping compilers</a>. This means the compiler is written in the program language that it is the compiler for. TypeScript has been an example of this since it was first open sourced. That is about to change; the compiler will stop being written in TypeScript and will start being written in Go. This is possibly the first example of a language moving away from having a bootstrapping compiler. This is done in the name of performance.</p>
<p>Of all the aspects about the Go port, this one was the one that gave John most anxiety. (It's John writing this by the way, writing in the third person feels very strange.) The TypeScript team will be moving away from writing TypeScript in their day to day lives. They won't abandon it of course, but they will certainly write less TypeScript and more Go. An implication of this is that there will be reduced <a href="https://en.wikipedia.org/wiki/Eating_your_own_dog_food" target="_blank" rel="noopener noreferrer">dogfooding</a> - which means less direct feedback to the makers of TypeScript about what it's like to write TypeScript.</p>
<p>Given how broad the TypeScript community is, this is perhaps not the concern that it might be. The team are very connected with the community and even if they are writing TypeScript less, people who are writing more will be sure to be vocal. It's maybe worth remembering that for most of the time TypeScript has been around, the team has often written TypeScript in a style that is not necessarily representative of the broader community. We're thinking here of classes (talked about below), and until recently modules. Before Jake Bailey's mammoth work to <a href="https://devblogs.microsoft.com/typescript/typescripts-migration-to-modules/" target="_blank" rel="noopener noreferrer">migrate the TypeScript codebase to use modules</a>, the codebase used namespaces. This didn't stop TypeScript working with on improving support for these JavaScript features at all. So it seems reasonable we need not fear.</p>
<p>Another angle on this, is wondering if the TypeScript team might become less involved with TC39 (the committee that develops the JavaScript language specification). TypeScript have been instrumental in language development over the years, from optional chaining to decorators and beyond. As the TypeScript team will be writing less TypeScript, there's a view that they might become less directly involved in influencing the development of JavaScript.</p>
<p>Ashley, who is one of Bloomberg's TC39 delegates, is not at all worried about this. The Principal Product Manager of TypeScript, Daniel Rosenwasser, recently became one of the <a href="https://github.com/tc39/agendas/blob/main/2025/TC39%20Chair%20Group%20Election%20-%20106th%20Meeting.pdf" target="_blank" rel="noopener noreferrer">two incoming TC39 Facilitators</a>. There is also Ron Buckton, another TC39 delegate from the TypeScript team, who continues to champion multiple exciting proposals such as <a href="https://github.com/tc39/proposal-explicit-resource-management" target="_blank" rel="noopener noreferrer">Explicit Resource Management</a>. The importance of having the TypeScript team's input into the evolution of JavaScript remains the same regardless of which language is used to implement TypeScript's analysis of JavaScript.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="interacting-with-typescript">Interacting with TypeScript<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#interacting-with-typescript" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>There are four primary ways to interact with the TypeScript package. Let's have a think about how these might change.</p>
<ul>
<li>Via its command line interface <code>tsc</code>
<ul>
<li>There will still be a CLI and it sounds like the goal will be very close compatibility. The implementation may change to be Go, but you would still be able to interact with the CLI in the same way</li>
</ul>
</li>
<li>Via its JavaScript API, importing it as a module <code>import ts from "typescript"</code>
<ul>
<li>The TypeScript team are still working on this part. There will still be a JavaScript API, though it's almost certain that there will be changes here, but exactly how different they are is not yet known.</li>
<li>One core question is if the currently synchronous API will need to become asynchronous due to calling Go, as this can be <a href="https://journal.stuffwithstuff.com/2015/02/01/what-color-is-your-function/" target="_blank" rel="noopener noreferrer">a difficult change to migrate to</a>. The good news here is that <a href="https://github.com/microsoft/typescript-go/pull/711" target="_blank" rel="noopener noreferrer">it looks like it will be able to retain a synchronous API</a>.</li>
</ul>
</li>
<li>Via the language server <code>tsserver</code>
<ul>
<li>Editors such as VSCode, and even linters, can interact with TypeScript via its language server.</li>
<li>Interestingly even though TypeScript helped inspire the LSP specification it <a href="https://github.com/microsoft/TypeScript/issues/39459" target="_blank" rel="noopener noreferrer">currently doesn't actually implement it</a>. The TypeScript team are using the port as an opportunity to align with the LSP specification. This is a positive change.</li>
</ul>
</li>
<li>Via another tool that uses TypeScript internally<!-- -->
<ul>
<li>Tools use one or a combination of the above to use TypeScript on their user's behalf. There will be work for the tools, but this might be done transparently to the end developer.</li>
</ul>
</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="ecosystem-tools">Ecosystem tools<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#ecosystem-tools" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Let's drill further into tools that use TypeScript internally. There will be an impact. John is the maintainer of <a href="https://github.com/TypeStrong/ts-loader" target="_blank" rel="noopener noreferrer"><code>ts-loader</code></a>, a widely used webpack loader for TypeScript. This loader depends upon TypeScript APIs which have been unchanged in years.</p>
<p>In fact, in John went so far as to comment as such on <a href="https://bsky.app/profile/johnnyreilly.com/post/3ljexijnmdk2m" target="_blank" rel="noopener noreferrer">Bluesky in early March</a>:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of Bluesky post talking about TypeScript stability" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-typescript-is-rock-solid-edfc3caff7b9ee871a23e647bdb96484.png" width="1079" height="1129" class="img_ev3q"></p>
<p>Only to have the TypeScript team effectively come out and say "hold my beer".</p>
<p>It's very early days, but we know for sure that the internal APIs of TypeScript (that <code>ts-loader</code> depends upon) will <a href="https://github.com/microsoft/typescript-go/discussions/455#discussion-8063819" target="_blank" rel="noopener noreferrer">change</a>. To quote Daniel Rosenwasser of the TypeScript team:</p>
<blockquote>
<p>While we are porting most of the existing TypeScript compiler and language service, that does not necessarily mean that all APIs will be ported over.</p>
</blockquote>
<p><code>ts-loader</code> has two modes of operation:</p>
<ol>
<li>With type checking</li>
<li>Without type checking; transpilation only</li>
</ol>
<p>It's very unlikely that TypeScript 7 will work with <code>ts-loader</code>s type checking mode, without significant refactoring. However, it's quite likely that <code>ts-loader</code> might be able to support transpilation only mode with minimal changes. This mode only really depends on the <a href="https://github.com/TypeStrong/ts-loader/blob/847a24936aa12fa18dab21ca8ec37595cadc72c6/src/index.ts#L644-L650" target="_blank" rel="noopener noreferrer"><code>transpileModule</code></a> API of TypeScript. If the <code>transpileModule</code> API lands, then the transpilation only mode of <code>ts-loader</code> should just work. On the other hand, this might be the natural end of the road for the type checking mode of <code>ts-loader</code>.</p>
<p>Ashley is the author of <a href="https://github.com/bloomberg/ts-blank-space" target="_blank" rel="noopener noreferrer"><code>ts-blank-space</code></a> (an open source TypeScript to JavaScript transform published by Bloomberg that avoids the need for source maps). It also depends on TypeScript's API so may be affected by the port. It's too early to say but the change here may turn into an opportunity. A not uncommon request of <code>ts-blank-space</code> is to investigate using a different parser. This is because while <code>ts-blank-space</code> itself is very small and only uses TypeScript's parsing API, this is not an isolated part of TypeScript so still ends up importing the whole type checker. For projects that already depend on TypeScript there is no added cost, but it makes <code>ts-blank-space</code> less appealing for use-cases that are not already importing TypeScript as a library.</p>
<p>Some tooling will have a natural path forwards. For instance, <code>typescript-eslint</code> will continue onwards with TypeScript 7. The TypeScript team are planning to help with typed linting with the new, faster APIs. So this means that ESLint (which many people are used to using), will become faster, as TypeScript becomes faster.</p>
<p>However, it's likely that tooling that depends upon internal TypeScript APIs which are going to radically change, may cease to be in their current forms. This will vary project by project, but expect change. And this is fine. Change is a constant.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="was-go-a-good-language-choice">Was Go a good language choice?<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#was-go-a-good-language-choice" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Given that TypeScript decided to move away from being written with TypeScript, many people had and have opinions on the language being picked: Go. The folk who like C# wish that the team had picked C#. Particularly given Anders' involvement with C#. Those people that like Rust would very much have liked for the team to have picked Rust. The good news for Rust fans is that there is a chance that the Node.js bindings for TypeScript 7 will use <a href="https://github.com/microsoft/libsyncrpc" target="_blank" rel="noopener noreferrer">a package that was written in Rust</a>.</p>
<p>If John was to guess what the team might have picked he would have either said Rust or Zig (what Bun is built with). Go felt like a slightly leftfield choice, but upon reflection it completely makes sense. ESBuild is written in Go, so there's prior art. Go has a garbage collector (Rust does not) which means the work of porting the code is significantly reduced. Likewise, C# is all about <code>class</code>es and so a port from TypeScript (which makes only light use of <code>class</code>es in the compiler codebase) to C# would be uphill work.</p>
<p>The Go choice represents pragmatism; which is very much a TypeScript ethos. In fact if you look at the <a href="https://github.com/microsoft/TypeScript/wiki/TypeScript-Design-Goals" target="_blank" rel="noopener noreferrer">TypeScript Design goals</a>, you can see how TypeScript has always espoused a pragmatic approach. Perhaps most famously by having soundness as a "non-goal". Instead, striking a balance between correctness and productivity.</p>
<p>Pragmatism is the TypeScript way. Go is a pragmatic choice.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="is-this-evidence-that-javascript-is-slow">Is this evidence that JavaScript is slow?<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#is-this-evidence-that-javascript-is-slow" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>This is evidence that JavaScript can be a slow language to implement a type-checker. To re-purpose a quote from Anders in this <a href="https://github.com/microsoft/typescript-go/discussions/411#discussioncomment-12476218a" target="_blank" rel="noopener noreferrer">'why Go' post</a>:</p>
<blockquote>
<p>No single language is perfect for every task</p>
</blockquote>
<p>The task of type-checking is an intensive one. One way to think of type-checking is that it is taking the program it is checking, emulating executing every line of code, and detecting when the emulation breaks a rule. So the larger a program is, the more work there is to do. When the type-checker is written in a dynamic language this means that it requires another program to run it. For TypeScript this means we effectively have a JavaScript engine, running the TypeScript checker which is running an emulation of another program. It's not a surprise that if the type-checker itself can be run natively that it will run noticeable faster.</p>
<p>Going ten times faster with Go has been attributed to roughly 3.5 times faster by being native, and another speed up comes from being able to run more in parallel (<a href="https://youtu.be/10qowKUW82U?t=538" target="_blank" rel="noopener noreferrer">source</a>).</p>
<p>Considering how much more work it is to execute a dynamic language like JavaScript than to execute a pre-compiled native binary, if anything it's amazing that the switch to native isn't a larger difference. This shows how much work has gone into V8, the JavaScript engine used by Node.js, to execute JavaScript very effectively.</p>
<p>It is possible to write JavaScript programs that do work in parallel today, but the APIs to do this efficiently are things like the low-level <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SharedArrayBuffer" target="_blank" rel="noopener noreferrer"><code>SharedArrayBuffer</code></a>, where you are now dealing with the raw bytes. There is a Stage 2 proposal to add <a href="https://github.com/tc39/proposal-structs" target="_blank" rel="noopener noreferrer">"Shared Structs"</a> to JavaScript - if this progresses it will be interesting to see JavaScript programs more easily benefit from using multiple cores.</p>
<p>There are still many benefits to using JavaScript for other tasks. Just some of the benefits:</p>
<ul>
<li>tend to be smaller in size, due to having higher-level concepts</li>
<li>easier to combine together dynamically, great for 3rd party plugin ecosystems</li>
<li>easier to modify while they are still running, which is great for UI development</li>
<li>are not specific to a particular operating systems and CPU architecture</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>The ecosystem demanded a faster TypeScript. Performance cannot be ignored these days. As a consequence, some kind of port of TypeScript was bound to happen. If we accept that view, then what next? Well, the way that the TypeScript team has started executing on the migration fills us with confidence. The TypeScript team are talented, they are pragmatists and their choices are wise.</p>
<p>This is going to Go well.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="credits">Credits<a href="https://johnnyreilly.com/typescript-go-pragmatic-choice#credits" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Thanks to Jake Bailey, of the TypeScript team, for reviewing this piece - greatly appreciated! Also to Josh Goldberg to writing up his classification of what makes up TypeScript; many thanks!</p>
<p><a href="https://blog.logrocket.com/typescript-go-pragmatic-choice/" target="_blank" rel="noopener noreferrer">This post was originally published on LogRocket.</a></p>
]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <author>
            <name>Ashley Claymore</name>
            <uri>https://github.com/acutmore</uri>
        </author>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[npx and Azure Artifacts: the secret CLI delivery mechanism]]></title>
        <id>https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism</id>
        <link href="https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism"/>
        <updated>2024-12-18T07:43:47.000Z</updated>
        <summary type="html"><![CDATA[By combining npx and Azure Artifacts, you can deliver your command line application to consumers in a way that is easy to use and secure.]]></summary>
        <content type="html"><![CDATA[<p>The <a href="https://docs.npmjs.com/cli/v8/commands/npx" target="_blank" rel="noopener noreferrer"><code>npx</code> command</a> is a powerful tool for running CLI tools shipped as npm packages, without having to install them globally. <code>npx</code> is typically used to run packages on the public npm registry. However, if you have a private npm feed, you can also use <code>npx</code> to run packages available on that feed.</p>
<p>Azure Artifacts is a feature of Azure DevOps that supports publishing npm packages to a feed for consumption. (You might want to read <a href="https://johnnyreilly.com/azure-artifacts-publish-private-npm-package-with-azure-devops">this guide on publishing npm packages to Azure Artifacts</a>.) By combining <code>npx</code> and Azure Artifacts, you can deliver your CLI tool to consumers in a way that's easy to use and secure.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Azure Artifacts: Publish a private npm package with Azure DevOps&amp;quot; with an Azure DevOps and npm logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-d0c6ea44c850de93c1f5bc9b5dc3861f.png" width="1600" height="900" fetchpriority="high" class="img_ev3q"></p>
<p>This post shows how to use <code>npx</code> and Azure Artifacts to deliver your private CLI tool to consumers.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="why-is-combining-npx-with-private-npm-feeds-useful">Why is combining <code>npx</code> with private npm feeds useful?<a href="https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism#why-is-combining-npx-with-private-npm-feeds-useful" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>If you've ever found a need to deliver a private CLI tool to consumers, you'll know that it can be a challenge.</p>
<p>I work for a large organization and we need to share internal tools with our colleagues. The problem is, that it's hard to get people to install tools. Either you need to provide detailed instructions on how to acquire and install the tool, or you need to work out some kind of internal distribution mechanism. You also have to think about how to update the tool. It's not simple.</p>
<p>By combining <code>npx</code> and Azure Artifacts it becomes much simpler. You can publish your CLI tool to a private npm feed and then consumers can run it with a single command. They don't need to install anything up front (apart from Node.js which they likely already have), and they don't need to worry about updates.</p>
<p>A typical usecase is the one I've mentioned; sharing tools internally in an organisation. But, broader than that, if you want to deliver a private CLI tool to consumers, this is a great way to do it.</p>
<p>We're going to look at how we'd achieve this with Azure Artifacts as the host of the npm package. But, you could use any private npm feed that you have access to.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="publishing-a-package-to-azure-artifacts">Publishing a package to Azure Artifacts<a href="https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism#publishing-a-package-to-azure-artifacts" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Before you can use <code>npx</code> to run your CLI tool, you need to publish it to a private npm feed. Here is a guide on <a href="https://johnnyreilly.com/azure-artifacts-publish-private-npm-package-with-azure-devops">how to publish a private npm package with Azure Artifacts</a>. In that example we published a package to a feed called <code>npmrc-script-organization</code> in the <code>johnnyreilly</code> organization of Azure DevOps / Azure Artifacts.</p>
<p>For the sake of this post, we'll say that our package is a CLI tool with the name <code>@johnnyreilly/my-cli-tool</code>.</p>
<p>Remember, an npm package which houses a CLI tool is merely an npm package with a <a href="https://docs.npmjs.com/cli/v10/configuring-npm/package-json#bin" target="_blank" rel="noopener noreferrer"><code>bin</code> entry in the <code>package.json</code></a>. This post is not about how to create a CLI tool, but rather how to deliver one to private consumers. If you would like to see an example of what a CLI tool package looks like, you can check out the <a href="https://github.com/johnnyreilly/azdo-npm-auth" target="_blank" rel="noopener noreferrer"><code>azdo-npm-auth</code> package on GitHub</a>. (In fact, we'll use <code>azdo-npm-auth</code> later in this post - it's an example of a CLI tool published to the <strong>public</strong> npm registry.)</p>
<p>The question now is, how we can run the (private) <code>@johnnyreilly/my-cli-tool</code> package with <code>npx</code>?</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-registry-config-setting-of-npm--npx">The <code>registry</code> config setting of <code>npm</code> / <code>npx</code><a href="https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism#the-registry-config-setting-of-npm--npx" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>The secret sauce of running a CLI tool from a private npm feed with <code>npx</code> is the <a href="https://docs.npmjs.com/cli/v8/using-npm/config#registry" target="_blank" rel="noopener noreferrer"><code>registry</code> config setting of <code>npm</code> / <code>npx</code></a>. The <code>registry</code> option allows you to specify the URL of the npm feed that you want to use.</p>
<p>For our case, we grabbed the registry URL from the Azure DevOps UI by clicking on the "Connect to Feed" button in the Azure Artifacts section:</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of &amp;quot;connect to feed&amp;quot; in Azure DevOps" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-connect-to-feed-9420465335c838a5365e11888ea36437.webp" width="1016" height="118" class="img_ev3q"></p>
<p>When we selected <code>npm</code>, ADO displayed instructions for setting up an <code>.npmrc</code> file for private consumption:</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of the instructions for setting up the .npmrc file" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-npmrc-e8b46fa648b27148f109ea8fbf6ba807.png" width="2240" height="678" class="img_ev3q"></p>
<p>We don't need to set up an <code>.npmrc</code> file to run the CLI tool with <code>npx</code>, but we do need to grab the registry URL, which we can see in the example <code>.npmrc</code> file above. In our case, the URL is <code>https://pkgs.dev.azure.com/johnnyreilly/_packaging/npmrc-script-organization/npm/registry/</code>. This is the URL of the registry (private npm feed) that we want to use.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="running-the-cli-tool-with-npx">Running the CLI tool with <code>npx</code><a href="https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism#running-the-cli-tool-with-npx" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Equipped with the registry URL, we can now run our CLI tool with <code>npx</code>:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">npx </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-y</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--registry</span><span class="token plain"> https://pkgs.dev.azure.com/johnnyreilly/_packaging/npmrc-script-organization/npm/registry/ @johnnyreilly/my-cli-tool</span><br></span></code></pre></div></div>
<p>This command will download the <code>@johnnyreilly/my-cli-tool</code> package from the private npm feed and run it. The <code>--registry</code> option tells <code>npx</code> to use the specified registry URL to download the package and the <code>-y</code> option tells <code>npx</code> to answer "yes" to the installation prompt.</p>
<p>If you need to pass arguments to the CLI tool, you can simply add them to the end of the command as you would with any CLI tool: (I'll put this over multiple lines for readability, but you can run it as a single line)</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">npx </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-y</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--registry</span><span class="token plain"> https://pkgs.dev.azure.com/johnnyreilly/_packaging/npmrc-script-organization/npm/registry/ </span><span class="token punctuation" style="color:rgb(255, 255, 255)">\</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  @johnnyreilly/my-cli-tool </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--arg1</span><span class="token plain"> hello</span><br></span></code></pre></div></div>
<p>There is another way to specify the registry URL, which is to use the <code>npm_config_registry</code> environment variable. This approach is more verbose and is not cross platform (it won't work on Windows). But, if you prefer this approach, you can use this style of command:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token assign-left variable" style="color:rgb(255, 238, 128)">npm_config_registry</span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain">https://pkgs.dev.azure.com/johnnyreilly/_packaging/npmrc-script-organization/npm/registry/ npx </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-y</span><span class="token plain"> @johnnyreilly/my-cli-tool</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-about-authentication">What about authentication?<a href="https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism#what-about-authentication" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>If you encounter an error like this:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error code E401</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error Unable to authenticate, your authentication token seems to be invalid.</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error To correct this please try logging </span><span class="token keyword" style="color:rgb(255, 157, 0)">in</span><span class="token plain"> again with:</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error </span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> login</span><br></span></code></pre></div></div>
<p>Then npm is telling you to authenticate with the private npm feed / registry. This is because the feed is private and requires authentication. This is a good thing; it means that your package is secure; just as you'd hoped.</p>
<p>You may have your own way of authenticating with the feed. If so, great! Do that now and skip the next section.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-azdo-npm-auth-to-authenticate-with-azure-artifacts">Using <code>azdo-npm-auth</code> to authenticate with Azure Artifacts<a href="https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism#using-azdo-npm-auth-to-authenticate-with-azure-artifacts" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>On the other hand, if you're using Azure Artifacts (<a href="https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/connect-organization-to-azure-ad?view=azure-devops" target="_blank" rel="noopener noreferrer">and your Azure DevOps organisation is connected with your Azure account / Microsoft Entra ID</a>), you can use <a href="https://github.com/johnnyreilly/azdo-npm-auth" target="_blank" rel="noopener noreferrer"><code>azdo-npm-auth</code></a> to solve your authentication needs. You can run <code>azdo-npm-auth</code> like this:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">npx </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-y</span><span class="token plain"> azdo-npm-auth </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--registry</span><span class="token plain"> https://pkgs.dev.azure.com/johnnyreilly/_packaging/npmrc-script-organization/npm/registry/</span><br></span></code></pre></div></div>
<p>The above command will acquire a PAT (Personal Access Token) from Azure DevOps and use it to create a user <code>.npmrc</code> file, which will be used by <code>npx</code> to authenticate with the feed subsequently.</p>
<p>If you encounter a <code>npm error code E401</code> as you run the <code>azdo-npm-auth</code> command, it's possible that you have a local <code>.npmrc</code> file that is tripping <code>npx</code> up. You can get around that by explicitly passing the <code>--registry</code> of the public npm feed / registry to <code>npx</code>:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">npx </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-y</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--registry</span><span class="token plain"> https://registry.npmjs.org azdo-npm-auth </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--registry</span><span class="token plain"> https://pkgs.dev.azure.com/johnnyreilly/_packaging/npmrc-script-organization/npm/registry/</span><br></span></code></pre></div></div>
<p>That's right; we're passing the public npm registry to <code>npx</code>'s <code>--registry</code> and we're passing our private npm feed / registry to <code>azdo-npm-auth</code>'s <code>--registry</code>. This gets around the <code>npm error code E401</code> issue.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="running-the-original-command-again">Running the original command again<a href="https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism#running-the-original-command-again" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Whichever way you authenticated, you should now be ready. You can now run the original command again; it should work this time. For example:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">npx </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-y</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--registry</span><span class="token plain"> https://pkgs.dev.azure.com/johnnyreilly/_packaging/npmrc-script-organization/npm/registry/ @johnnyreilly/my-cli-tool</span><br></span></code></pre></div></div>
<p>And that's it! You've successfully run your CLI tool from a private npm feed with <code>npx</code>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/npx-and-azure-artifacts-the-secret-cli-delivery-mechanism#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>In this post we've used Azure Artifacts as the host of the npm package, but you could use any npm feed that you have access to. The key is to use the <code>registry</code> option of <code>npm</code> / <code>npx</code> to specify the URL of the npm feed.</p>
<p>By combining <code>npx</code> and private npm feeds, you can deliver your CLI tool to consumers in a way that's easy to use and secure. Consumers can run your tool with a single command, without having to install anything up front. This is a powerful way to share private CLI tools.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Azure DevOps" term="Azure DevOps"/>
        <category label="Node.js" term="Node.js"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Node.js, Azure Application Insights, and Fastify]]></title>
        <id>https://johnnyreilly.com/nodejs-azure-appinsights-fastify</id>
        <link href="https://johnnyreilly.com/nodejs-azure-appinsights-fastify"/>
        <updated>2025-02-17T21:05:48.000Z</updated>
        <summary type="html"><![CDATA[Learn how to set up a Node.js with Azure Application Insights and Fastify.]]></summary>
        <content type="html"><![CDATA[<p>If you deploy a Node.js application to Azure, you might want to use Azure Application Insights to monitor it. This post shows you how to set up a Node.js application with Azure Application Insights. It also includes a Fastify plugin to automatically track requests. (Given the out of the box mechanism for tracking requests does not work with Fastify.)</p>
<p><img decoding="async" loading="lazy" alt="title image reading &amp;quot;Node.js, Azure Application Insights, and Fastify&amp;quot; with the relevant logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-09b4aad04d867b07bb095e561694e59d.png" width="800" height="450" class="img_ev3q"></p>
<p>This is one of those posts that gathers together information I found doing research and puts it in one place.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="setting-up-azure-application-insights-with-nodejs">Setting up Azure Application Insights with Node.js<a href="https://johnnyreilly.com/nodejs-azure-appinsights-fastify#setting-up-azure-application-insights-with-nodejs" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Setting up Azure Application Insights with Node.js is straightforward. You need to install the <a href="https://github.com/microsoft/ApplicationInsights-node.js" target="_blank" rel="noopener noreferrer"><code>applicationinsights</code></a> package which integrates Node.js and Azure Application Insights:</p>
<div class="language-sh codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-sh codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">install</span><span class="token plain"> applicationinsights </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--save</span><br></span></code></pre></div></div>
<p>Then configure it with your connection string. You want to do this as early as possible when your application starts, so if there are issues, you know as soon as possible. Here's how you can do this:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> appInsights </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'applicationinsights'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">let</span><span class="token plain"> client</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> appInsights</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">TelemetryClient </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">process</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token constant" style="color:rgb(250, 208, 0)">APPLICATIONINSIGHTS_CONNECTION_STRING</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// https://github.com/microsoft/applicationinsights-node.js?tab=readme-ov-file#configuration</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  appInsights</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setup</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">process</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token constant" style="color:rgb(250, 208, 0)">APPLICATIONINSIGHTS_CONNECTION_STRING</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectRequests</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectPerformance</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectExceptions</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectDependencies</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectConsole</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// this will enable console logging</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectPreAggregatedMetrics</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setSendLiveMetrics</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setInternalLogging</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">enableWebInstrumentation</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">start</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  client </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> appInsights</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">defaultClient</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>The above code does two things of interest:</p>
<ol>
<li>It sets up Azure Application Insights with the connection string you provide. For Node.js applications, the connection string tends to be stored in the environment variable <code>APPLICATIONINSIGHTS_CONNECTION_STRING</code> and I see no reason to deviate from that.</li>
<li>It configures the telemetry client to collect various types of telemetry data. You can see the full list of options <a href="https://github.com/microsoft/applicationinsights-node.js?tab=readme-ov-file#configuration" target="_blank" rel="noopener noreferrer">here</a>. In the example above, all the defaults are used with one exception. That exception is <code>setAutoCollectConsole(true, true)</code> which enables console logging. This is in response to this part of the docs:</li>
</ol>
<blockquote>
<p>Note that by default <code>setAutoCollectConsole</code> is configured to <em>exclude</em> calls to <code>console.log</code>
(and other <code>console</code> methods). By default, only calls to supported third-party loggers
(e.g. <code>winston</code>, <code>bunyan</code>) will be collected. You can change this behavior to <em>include</em> calls
to <code>console</code> methods by using <code>setAutoCollectConsole(true, true)</code>.</p>
</blockquote>
<p>I'm not using a third-party logger in this example, so I want to include calls to <code>console</code> methods.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="setting-up-fastify-with-azure-application-insights">Setting up Fastify with Azure Application Insights<a href="https://johnnyreilly.com/nodejs-azure-appinsights-fastify#setting-up-fastify-with-azure-application-insights" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>If you're not a <a href="https://fastify.dev/" target="_blank" rel="noopener noreferrer">Fastify</a> user you can stop here. But if you are a Fastify user, you may have discovered that <code>setAutoCollectRequests</code> appears not to be be working. There's a <a href="https://github.com/microsoft/ApplicationInsights-node.js/issues/627" target="_blank" rel="noopener noreferrer">GitHub issue to track this</a>, but no discernible sign that it's going to be fixed soon. So, I've created a Fastify plugin to track requests manually based upon <a href="https://github.com/microsoft/ApplicationInsights-node.js/issues/627#issuecomment-2194527018" target="_blank" rel="noopener noreferrer">@stefanpeer's comment</a>:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> FastifyInstance</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> FastifyPluginOptions </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'fastify'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> fp </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'fastify-plugin'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> appInsights </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'applicationinsights'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">declare</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">module</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'fastify'</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">FastifyRequest</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// here we augment FastifyRequest interface as advised here: https://fastify.dev/docs/latest/Reference/Hooks/#using-hooks-to-inject-custom-properties</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    app</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> start</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// based on https://github.com/microsoft/ApplicationInsights-node.js/issues/627#issuecomment-2194527018</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> appInsightsPlugin </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fp</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">fastify</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> FastifyInstance</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> options</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> FastifyPluginOptions</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">options</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'App Insights not configured'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> client</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> appInsights</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">TelemetryClient </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> options</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> urlsToIgnore </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> options</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">urlsToIgnore </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    fastify</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">addHook</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string" style="color:rgb(165, 255, 144)">'onRequest'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">this</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> FastifyInstance</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> _reply</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// store the start time of the request</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> start </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> Date</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">now</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">app </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> start </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    fastify</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">addHook</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string" style="color:rgb(165, 255, 144)">'onResponse'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">this</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> FastifyInstance</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> reply</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">urlsToIgnore</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">includes</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">raw</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> duration </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> Date</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">now</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">-</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">start</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">trackRequest</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          name</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">raw</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">method </span><span class="token operator" style="color:rgb(255, 157, 0)">+</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">' '</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">+</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">raw</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          url</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">raw</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          duration</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> duration</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          resultCode</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> reply</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">statusCode</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">toString</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          success</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> reply</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">statusCode </span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">400</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          properties</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            method</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">raw</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">method</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            url</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> request</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">raw</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          measurements</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            duration</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> duration</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>The above code creates a Fastify plugin that tracks requests manually. It does this by adding two hooks to Fastify:</p>
<ul>
<li><code>onRequest</code> - This hook stores the start time of the request.</li>
<li><code>onResponse</code> - This hook calculates the duration of the request and sends it to Azure Application Insights.</li>
</ul>
<p>It also includes an <code>urlsToIgnore</code> option. This is an array of URLs that you don't want to track. For example, you might not want to track requests to the root of your application. You can pass this option when you register the plugin.</p>
<p>To make the code play nicely with TypeScript, we augment the <code>FastifyRequest</code> interface to include a <code>start</code> property. This is where we store the start time of the request that we supply to the <code>onResponse</code> hook and read from the <code>onRequest</code> hook to calculate the duration of the request.</p>
<p>To consume the plugin in a Fastify application, you can do something like this:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> appInsights </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'applicationinsights'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> Fastify</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> FastifyInstance </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'fastify'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> appInsightsPlugin </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'./appInsightsPlugin.js'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">let</span><span class="token plain"> client</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> appInsights</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">TelemetryClient </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">undefined</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">process</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token constant" style="color:rgb(250, 208, 0)">APPLICATIONINSIGHTS_CONNECTION_STRING</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// https://github.com/microsoft/applicationinsights-node.js?tab=readme-ov-file#configuration</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  appInsights</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setup</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">process</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token constant" style="color:rgb(250, 208, 0)">APPLICATIONINSIGHTS_CONNECTION_STRING</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectRequests</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectPerformance</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectExceptions</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectDependencies</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectConsole</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// this will enable console logging</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setAutoCollectPreAggregatedMetrics</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setSendLiveMetrics</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">setInternalLogging</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">enableWebInstrumentation</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">false</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">start</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  client </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> appInsights</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">defaultClient</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> fastify</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> FastifyInstance </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">Fastify</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  logger</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">fastify</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">register</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">appInsightsPlugin</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> urlsToIgnore</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'/'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>This both sets up Azure Application Insights and registers the Fastify plugin. The <code>urlsToIgnore</code> option is set to <code>['/']</code> which means that requests to the root of the application will not be tracked.</p>
<p>With this in place you'll see traffic in Azure Application Insights:</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of requests showing up in Application Insights" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-app-insights-requests-9ec422c7488951e303c5f5d1b4aab056.png" width="835" height="588" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/nodejs-azure-appinsights-fastify#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Using Azure Application Insights with Node.js is straightforward. However, if you're using Fastify, you'll need to track requests manually. This post shows you how to do that with a Fastify plugin. I hope you find it useful!</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Azure" term="Azure"/>
        <category label="Node.js" term="Node.js"/>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Where AI-assisted coding accelerates development — and where it doesn’t]]></title>
        <id>https://johnnyreilly.com/ai-assisted-coding</id>
        <link href="https://johnnyreilly.com/ai-assisted-coding"/>
        <updated>2025-11-12T17:36:47.000Z</updated>
        <summary type="html"><![CDATA[AI-assisted coding has transformed software development. However, it also introduces challenges around code quality, architectural decisions, and maintaining foundational engineering skills. This post explores where AI excels and where human judgment remains essential.]]></summary>
        <content type="html"><![CDATA[<p>AI is a fantastic tool. In a short time, it has changed the way the whole industry builds software. Where we used to write every line ourselves, now the code we produce is generally a collaboration between an engineer and AI tooling. The likes of GitHub Copilot, Claude Code, ChatGPT, Replit etc., all combine to make being a software developer in 2025 quite different from what it was in 2022. The rate of change has been giddying.</p>
<p><img decoding="async" loading="eager" alt="title image including this quote &amp;quot;AI can accelerate the journey, but human judgment must still set the destination.&amp;quot;" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-ded08fbb577f22344f904c567b89ca9b.png" width="730" height="487" fetchpriority="high" class="img_ev3q"></p>
<p>This post exists to dig into a little of the nuance around how software development has been changed by the innovations of AI: both the positives and the negatives. We’ll also discuss the tooling and approaches that have carried the industry forward, and the brand new pitfalls that are now revealing themselves.</p>
<p>This post won’t be exhaustive. Given how fast AI has changed and keeps changing, many of the reference points in this post may seem out of date from the moment of publication. But hopefully, the underlying principles should be evergreen.</p>
<p>This will be a personal story, informed by the experiences both from the world of open source and from colleagues and friends in the industry.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="about-me">About me<a href="https://johnnyreilly.com/ai-assisted-coding#about-me" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>A little information about myself: I work as a software engineer for Investec, a multinational financial services group. The story of engineering at Investec is relevant to this piece. Investec has been an early and enthusiastic adopter of AI generally, and AI software tooling particularly. I’ll seek to draw on this experience.</p>
<p>I also work on open source software outside of work and have done for many years, primarily in the world of TypeScript.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="setting-the-table-github-copilot-and-the-emergence-of-agent-mode">Setting the table: GitHub Copilot and the emergence of agent mode<a href="https://johnnyreilly.com/ai-assisted-coding#setting-the-table-github-copilot-and-the-emergence-of-agent-mode" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>One of the most well-known AI coding tools is GitHub Copilot. In many ways, it was the original AI coding tool, and competitors like Claude Code and Cursor still can’t match Copilot’s dominance. Let’s consider for a moment what makes these IDE AI tools useful.</p>
<p>GitHub Copilot has ask and edit features which are not radically different from what you’d get with say ChatGPT, but something like agent mode was truly the game-changer. This feature transformed the way I approach complex coding tasks, shifting from line-by-line assistance to higher-level problem solving.</p>
<p>Rather than implementing a feature myself from scratch, I can describe a feature or a bug fix in natural language, and the AI will generate the necessary code changes across multiple files. This holistic approach to coding feels more like collaborating with a junior developer who can take on significant chunks of work. This allows me to focus on reviewing and refining the output rather than getting bogged down in the minutiae of implementation. AI is just fantastic at boilerplate, it turns out.</p>
<p>Another aspect that agent mode shines at is its ability to generate bash scripts and automation tasks rather than just making direct code changes. There’s something to be said for deterministic results. When AI generates a script that you can review, understand, and then execute, you maintain control over the process. This approach is far more reliable than having AI make direct modifications to your codebase that might introduce subtle bugs or architectural inconsistencies.</p>
<p>The predictable nature of scripted solutions means you can verify the approach before execution, understand exactly what will happen, and easily roll back if needed. It’s a more methodical approach that aligns well with engineering best practices around reproducible builds and transparent processes.</p>
<p>With that context in mind, let’s look at how these AI tools have reshaped application development — sometimes brilliantly, sometimes problematically.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="ai-generated-applications">AI generated applications<a href="https://johnnyreilly.com/ai-assisted-coding#ai-generated-applications" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>One of the most interesting aspects of AI-assisted coding is the notion of prompting your way to an application using a tool like Replit.</p>
<p>Historically, you may have run multiple workshops, the output of which may have been a vague design and some wireframes. But with something like Replit, you can leave a workshop with a functioning application. Crucially, you also have access to the source code of the AI-generated application, so it‘s possible to take that, leave Replit, and deploy it to your environment of choice.</p>
<p>Let’s walk through some of the aspects of this.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="prototype-at-speed">Prototype at speed<a href="https://johnnyreilly.com/ai-assisted-coding#prototype-at-speed" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>The time saved in the early stages of application development is considerable. The number of applications that would struggle to cross the chasm from idea to code is now vastly reduced. If you have an idea, it’s now possible to realise some form of it in hours. That’s incredibly useful.</p>
<p>A number of times in my career, I’ve been confronted by the idea of an application that just doesn’t work. A number of the underlying ideas are in conflict, in non-obvious ways. Discovering that information when you’ve already engaged your team and started developing is a waste of time, money, and good humor.</p>
<p>Using a tool like Replit to prototype drastically reduces the possibility that this might happen. By prompting AI to actually build a prototype of the app for you, it’s possible to surface poorly thought-out aspects of design before the actual build. This is really helpful and increases the chances of success.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="build-bespoke-apps">Build bespoke apps<a href="https://johnnyreilly.com/ai-assisted-coding#build-bespoke-apps" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>Tools like Replit offer the ability to build bespoke applications. Recently, at Investec, the organisation was looking at a tool to manage vendors for the organisation. There are a number of products in the marketplace that perform this function.</p>
<p>But as the company examined these tools, it was clear that each tool was opinionated. If we wanted to use one of the tools, it wouldn’t quite fit our existing organisational processes and structures. We could use one of them, but we’d either be working around the differences or adjusting the way we worked to use the tool effectively.</p>
<p>The idea occurred: why not build our own? Historically, that would have taken a long time to achieve, and may not have been cost-effective given the number of users. But maybe we could prompt our way to an application that aligned with our processes?</p>
<p>Three people got in a room for an afternoon and “maybe” became “actually.” They left the room with an AI-generated application that was more aligned with Investec’s preferences and processes.</p>
<p>The question then was, can we take our prototype application and “productionise” it? To achieve that, we wanted:</p>
<ul>
<li>The application to live in our source control, where we run tooling like GitHub Advanced Security to ensure code quality</li>
<li>We wanted to adjust the application tech stack towards one that better aligned with Investec’s standard choices</li>
<li>We wanted our application to have a deployment pipeline and use CI/CD to deploy new versions</li>
<li>We found that with a couple of engineers and five days’ work, we were able to achieve that. That’s going from idea to a fully working application in just over a week. It’s kind of mind-blowing when you think about it.</li>
</ul>
<p>There’s maybe more detail here than you need, but what hopefully shines through is how it’s possible to build applications for dedicated purposes, in ways that wouldn’t have been practical previously. Brilliant stuff.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="limitations-of-ai-generated-applications">Limitations of AI-generated applications<a href="https://johnnyreilly.com/ai-assisted-coding#limitations-of-ai-generated-applications" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>While AI-generated applications offer tremendous advantages, they’re not without their challenges. The very speed and ease that make these tools so appealing can also mask significant issues that only become apparent later in the development process. Let’s explore some of the key limitations and pitfalls.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="when-app-prompting-fails">When app prompting fails<a href="https://johnnyreilly.com/ai-assisted-coding#when-app-prompting-fails" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>One of the most frustrating experiences with AI app generation occurs when you’re working with a tool like Replit and it starts making changes to one part of your stack while rendering another part meaningless. I’ve encountered situations where Replit created an app with a Python backend and a TypeScript front end.</p>
<p>At some point in the prompting journey, though, it stopped updating the TypeScript frontend and converted the Python into a full-stack web application, effectively leaving half the application non-functional.</p>
<p>The people prompting the app were not aware this had happened, and it wasn’t until we tried to migrate the application from Replit that we realised what had occurred. We’ve talked about AI saving time, but in this case, it cost us a good amount of effort to unravel what had happened.</p>
<p>This highlights a broader issue with AI-generated applications: they often struggle with the complexity of modern full-stack development, where changes in one layer can have cascading effects throughout the system.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="im-depending-on-you-the-issues-of-dependency-management">I'm depending on you: The issues of dependency management<a href="https://johnnyreilly.com/ai-assisted-coding#im-depending-on-you-the-issues-of-dependency-management" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>Tools like Dependabot will often flag AI-generated applications for using outdated libraries with known vulnerabilities. This isn’t necessarily the AI’s fault; it’s working with training data that includes examples using older versions of libraries. But it does mean that the “finished” application isn’t actually ready for production without significant security updates.</p>
<p>Code quality is another concern. While AI can generate working code quickly, it doesn’t always generate good code. The applications work, but they may not follow best practices, lack proper error handling, or may have performance issues that only become apparent under load.
AI application generation works exceptionally well when you “own the domain”, i.e. when the application functionality is self-contained and doesn’t rely heavily on external integrations.</p>
<p>AI tools can, however, create a false sense of completion. This is especially true if parts of your app functionality depend on external systems, APIs that don’t exist in standardised forms, or data that isn’t readily available. The app appears finished, but the hard work of integration is still ahead of you.</p>
<p>Perhaps most concerning is when AI tools use libraries that simply don’t have vulnerability-free versions available. This puts you in the immediate position of choosing between security and functionality: a choice that shouldn’t exist.</p>
<p>At Investec, we are very biased in the direction of security. So when this presents, we will take some time to identify and securely resolve this. This often involves more work, which is fine. The point to note here is that AI will not necessarily land you with production-grade code.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="design-system-challenges">Design system challenges<a href="https://johnnyreilly.com/ai-assisted-coding#design-system-challenges" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>For organisations with established design systems (and Investec certainly has one) AI tools present an interesting challenge. AI can generate visually appealing interfaces that work well functionally, but they often don’t align with your company’s design language and brand guidelines.</p>
<p>You might end up with a beautiful application that looks nothing like the rest of your company’s digital properties. This disconnect between AI capabilities and organisational standards creates additional work to bring AI-generated UIs into compliance with house styles.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-ai-tech-stack--library-choices">The AI tech stack / library choices<a href="https://johnnyreilly.com/ai-assisted-coding#the-ai-tech-stack--library-choices" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>One of the unexpected consequences of AI-generated applications is how they expose the assumptions and biases embedded in AI training data and the system prompts. When you prompt for an application, the AI doesn’t just write code: it makes opinionated choices about which libraries, frameworks, and architectural patterns to use.</p>
<p>These choices often reflect what was popular in the AI’s training data, rather than what’s current or best suited to your specific needs. You might find your AI-generated application using React class components when functional components with Hooks would be more appropriate, or choosing older state management libraries when simpler solutions exist.</p>
<p>The challenge becomes more pronounced when you consider that AI tools tend to default to “safe” choices: libraries and patterns that were widely used and well-documented in their training period. While this reduces the likelihood of completely broken code, it can result in applications that feel outdated from the moment they’re generated.</p>
<p>This presents an interesting dilemma: do you accept the AI’s technology choices for the sake of speed, or do you invest time in modernising the stack to align with your preferences and current best practices? The answer often depends on whether you’re building a quick prototype or something intended for longer-term use.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-knowledge-gap-challenge">The knowledge gap challenge<a href="https://johnnyreilly.com/ai-assisted-coding#the-knowledge-gap-challenge" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>One of the most significant benefits of AI coding tools is how they can help engineers work with languages and frameworks they’re not experts in. The AI becomes a bridge, allowing a Python developer to confidently work with TypeScript or a frontend engineer to write backend services. This democratisation of technical knowledge is genuinely powerful.</p>
<p>However, this strength also reveals a critical weakness. When AI generates code in domains where the engineer lacks expertise, it becomes much harder to review the output critically. The AI might know APIs that you don’t, and while this can accelerate development, it can also lead to situations where engineers ship code they don’t fully understand.</p>
<p>I’ve seen examples where smart engineers have relied on AI to write infrastructure code — Bicep templates, for instance — but then struggled when debugging issues arose. The problem isn’t that the AI wrote bad code; often, the code works perfectly. The issue is that when you don’t understand what’s been written, you can’t effectively maintain, debug, or extend it.</p>
<p>This creates a fundamental principle: if you don’t understand the code, you shouldn’t ship it. It’s important to hold fast to this principle for the long-term benefits of maintainable code. It’s easy to ignore the principle when AI makes it so easy to generate working solutions. But it pays off to do this in the long term.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-testing-paradox">The testing paradox<a href="https://johnnyreilly.com/ai-assisted-coding#the-testing-paradox" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>Interestingly, one area where AI consistently excels is writing and fixing tests. AI-generated tests often surface actual issues in the codebase that human engineers might have missed. This creates an almost heretical situation for TDD advocates; the AI is finding bugs through tests that were written after the fact, not before.</p>
<p>While this approach might make purists uncomfortable, the practical benefit is undeniable. AI-generated tests serve as a safety net, catching edge cases and potential issues that improve overall code quality.</p>
<p>The flip side of this is that it can make some very silly decisions when writing tests. It may cover edge cases that are irrelevant, or miss important scenarios that a human would consider obvious. It often stubs out the implementation that you actually want to test.</p>
<p>Again, we’re highlighting the importance of human review and understanding when working with AI-generated code. We augment with AI; we don’t replace with AI.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-curse-of-ai-generated-pull-requests-prs">The curse of AI generated pull requests (PRs)<a href="https://johnnyreilly.com/ai-assisted-coding#the-curse-of-ai-generated-pull-requests-prs" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>As with many things in my life, it was open source software that got me thinking about this topic. Some OSS pals Josh and Brad <a href="https://bsky.app/profile/joshuakgoldberg.com/post/3luprjvrehs2c" target="_blank" rel="noopener noreferrer">posted this on Bluesky recently</a>:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of post on bluesky concerned about AI generated PRs" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-of-bluesky-post-926de4ed4208bf811a345079dac1f8e9.png" width="1079" height="1194" class="img_ev3q"></p>
<p><img decoding="async" loading="lazy" alt="screenshot of specific pull request listing concerns with it" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-pull-request-136124e9f967939033aec795314ed795.jpg" width="1844" height="808" class="img_ev3q"></p>
<p>For a while now, the land of OSS had been awash with AI generated PRs. These are PRs that have been written by AI. This problem hasn't impacted my projects particularly but I've certainly been aware of the toll it takes on other projects in terms of maintainers time spent on reviews.</p>
<p>The most benign reading of this sort of PR is that the post is talking about, is that the contributor thinks they know what they want, but believes AI will do a better job of writing the code than they will.</p>
<p>It's not about OSS; it's a general software development concern. AI isn't the problem here; humans are. We talk often about working effectively with AI by having a "human in the loop". The issue we're seeing here, is use of AI without sufficient humans.</p>
<p>I've long had a personal rule when coding: <em>If you submit a PR you must be the first reviewer.</em></p>
<p>This predates Copilot by some years. The idea essentially came to me when someone reviewed one of my PRs and raised some perfectly reasonable questions. Essentially as I'd been working on something, I'd changed approaches a few times, and what ended up in the PR wasn't entirely coherent.</p>
<p>So now, before I share a PR, I try to review it and see if it all makes sense.</p>
<p>This rule of thumb has served me well, and the use of AI coding tools only heightens the need for something similar.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="architectural-concerns">Architectural concerns<a href="https://johnnyreilly.com/ai-assisted-coding#architectural-concerns" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>While agent mode is undoubtedly a game-changer, the code it creates can sometimes be questionable from an architectural standpoint. I’ve seen AI choose unusual solutions that work but are suboptimal.</p>
<p>Here’s an example: I’d prompted Copilot to build a particular feature. The nature of the feature is not interesting, but the approach it used was. Rather than having an array of objects that represented the data it should use, it instead created a string array.</p>
<p>Each string in the array contained text that represented the data needed in a human-readable format. It then also created various regex-powered parsing mechanisms to extract the data it needed from the strings later on. It was a very roundabout way of achieving what I wanted. It was also very inefficient and buggy. The approach worked, but it was neither performant nor maintainable.</p>
<p>This highlights the importance of understanding not just what AI-generated code does, but how it does it. The “how” often reveals whether the solution will scale, perform well, and be maintainable over time. Other, less serious concerns I’ve seen include generating poorly factored code, using inefficient algorithms, or creating convoluted logic that is hard to follow.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-broader-implicationsof-ai-assisted-coding">The broader implicationsof AI-assisted coding<a href="https://johnnyreilly.com/ai-assisted-coding#the-broader-implicationsof-ai-assisted-coding" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Beyond anecdotal successes and challenges, the AI-assisted coding trend brings big-picture implications to the world of software development.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="ai-verbosity-and-the-default-trap">AI verbosity and the default trap<a href="https://johnnyreilly.com/ai-assisted-coding#ai-verbosity-and-the-default-trap" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>One consistent characteristic of AI-generated code is its verbosity. AI tends to be wordy, both in code comments and in implementation approaches. While thorough documentation isn’t inherently bad, brevity often leads to more maintainable code. In my experience, developers tend to accept AI’s verbose defaults without question, but this can lead to codebases that are unnecessarily complex and harder to maintain.</p>
<p>The same principle applies to pull request descriptions and documentation; AI tends toward comprehensive but overly detailed explanations when concise clarity would be more valuable. Oh, and emojis, always with the emojis! It can be steered to be more concise, but that requires deliberate prompting and review.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-acceleration-metaphor">The acceleration metaphor<a href="https://johnnyreilly.com/ai-assisted-coding#the-acceleration-metaphor" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>In many ways, AI functions like a travelator (aka a moving walkway) in an airport; it accelerates your movement in the direction you’re already going. If you’re heading in the right direction with solid engineering fundamentals, AI can dramatically speed up your progress. But if your approach or architecture is flawed, AI will help you get to the wrong destination much faster.</p>
<p>This acceleration effect means that the foundational skills of software engineering — understanding requirements, designing systems, and making architectural decisions — become even more critical in an AI-assisted world.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="are-we-still-learning">Are we still learning?<a href="https://johnnyreilly.com/ai-assisted-coding#are-we-still-learning" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>I started out long before AI was a thing. I learned to code by reading books, writing code, making mistakes, and learning from those mistakes. I learned to debug by debugging. I learned to architect systems by studying good architecture and bad architecture and learning from both. With AI tooling, it’s possible to go a long way without really learning these foundational skills.</p>
<p>It’s too early to know what the implications of this will be. Will we see a generation of engineers who can deliver features but don’t understand the underlying principles? Will debugging skills atrophy because AI can often generate working code?</p>
<p>These are open questions, but they highlight the importance of maintaining a strong foundation in software engineering principles even as we embrace AI tools.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="trust-but-verify">Trust, but verify<a href="https://johnnyreilly.com/ai-assisted-coding#trust-but-verify" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>The Zero Trust security principle operates on the concept of “never trust, always verify.” This means that no user, device, or application is inherently trusted, regardless of whether they are inside or outside the network perimeter. Although AI is a very different kettle of fish, this principle remains useful when considering AI inputs into your ecosystem.</p>
<p>GitHub Copilot wrote you some code that seems to do the job? Great! Look hard at what you received and be sure that you’re happy with what it’s doing and how it’s doing it. Source control becomes your safety belt when coding with AI; it provides the rollback mechanism when AI takes you down an unexpected path.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="the-flow-state-dilemma">The flow state dilemma<a href="https://johnnyreilly.com/ai-assisted-coding#the-flow-state-dilemma" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>One unexpected consequence of AI-assisted coding is its impact on flow state. The traditional deep, uninterrupted focus that characterises productive programming sessions becomes harder to achieve when you’re constantly context-switching between writing code and reviewing AI suggestions.</p>
<p>Personally speaking, I derive great joy from getting deep into flow state as I build an application or a feature. It’s a wonderfully meditative state and genuinely improves my mental health.</p>
<p>The collaborative nature of AI-assisted development, while powerful, can disrupt this meditative quality of coding that many engineers cherish. It’s a trade-off between speed and the satisfying rhythm of sustained, focused work.</p>
<p>Every now and then, I’ll have a “feel the force, Luke” moment, turn off my Copilot, and intentionally enter into flow, unaccompanied by my AI buddy. I bet I’m not alone.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/ai-assisted-coding#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>AI has undoubtedly transformed software development, offering unprecedented speed in prototyping, unprecedented access to knowledge across domains, and unprecedented assistance in solving complex problems. It’s a great unblocker; you don’t always need to find the expert (or “Marcel,” as we call him at Investec) when AI can provide guidance.</p>
<p>However, the key to successful AI-assisted development lies in maintaining the human element. AI excels at generating code, but humans remain essential for understanding requirements, making architectural decisions, reviewing outputs critically, and ensuring that solutions align with business needs and engineering standards.</p>
<p>The future of software development isn’t about replacing engineers with AI; it’s about engineers learning to work effectively with AI as a powerful tool in their toolkit. The most successful teams will be those that embrace AI’s capabilities while maintaining rigorous standards for code quality, architectural soundness, and security practices.</p>
<p>As we continue to navigate this rapidly evolving landscape, the principles of good engineering remain constant: understand what you’re building, review what you’re shipping, and never lose sight of the bigger picture. AI can accelerate the journey, but human judgment must still set the destination.</p>
<p><a href="https://blog.logrocket.com/ai-assisted-coding/" target="_blank" rel="noopener noreferrer">This post was originally published on LogRocket.</a></p>
]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="AI" term="AI"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Keeping front end and back end in sync with NSwag generated clients]]></title>
        <id>https://johnnyreilly.com/keeping-front-end-and-back-end-in-sync-with-nswag-generated-clients</id>
        <link href="https://johnnyreilly.com/keeping-front-end-and-back-end-in-sync-with-nswag-generated-clients"/>
        <updated>2025-10-12T08:56:14.000Z</updated>
        <summary type="html"><![CDATA[By generating clients from OpenAPI specs, it is possible to have integration tests that check your front end and your back end are aligned. This post will show you how to do that using NSwag.]]></summary>
        <content type="html"><![CDATA[<p>For many years I've been a big fan of using <a href="https://github.com/RicoSuter/NSwag" target="_blank" rel="noopener noreferrer">NSwag</a> to generate TypeScript and CSharp clients for APIs. I've written about it before in <a href="https://johnnyreilly.com/generate-typescript-and-csharp-clients-with-nswag">Generate TypeScript and CSharp clients with NSwag</a>.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Keeping front end and back end in sync with NSwag generated clients&amp;quot; with the Open API, TypeScript and C# logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-f0945ac5da387c7202af084f33c85949.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>You're likely aware of the popularity of excellent projects like <a href="https://trpc.io/" target="_blank" rel="noopener noreferrer">tRPC</a> which provide a way to use TypeScript end-to-end. However, if you're working in a polyglot environment where your back end is written in C# or [insert other language here], and your front end is written in TypeScript, then cannot take advantage of that. However, by generating front end clients from a server's OpenAPI specs, it's possible to have integration tests that check your front end and your back end are aligned.</p>
<p>This post will show you how to do that using NSwag.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-scenario">The scenario<a href="https://johnnyreilly.com/keeping-front-end-and-back-end-in-sync-with-nswag-generated-clients#the-scenario" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Let's talk about the kind of situation that I'm imagining. From reading the web, you'd think that every organisation is running both TypeScript on its front end and on its back end. In my experience, this is not the case. Many organisations have a particular technology stack on the back end - maybe C#/.NET - and a different one on the front end - generally TypeScript/JavaScript.</p>
<p>The technique of generating clients from OpenAPI specs is useful in this scenario. It means that you can keep your front end and back end in sync, even if they are written in different languages. The source of truth is the Open API spec, which is typically generated from the back end code. The front end client code is generated from the Open API spec. And then, because TypeScript is compiled, you can do a compilation test to check that the front end code is in sync with the back end code.</p>
<p>It's a simple idea but it is powerful. I typically achieve it in two steps:</p>
<ol>
<li>Make sure your continuous integration (CI) process compiles the front end code.</li>
<li>Add an integration test that checks that the generated client is up to date with the latest back end code.</li>
</ol>
<p>Most people will have done the first step. If you have a strongly typed front end project that is compiled as part of your CI process, then you will get a compilation errors if have code that does not type check successfully.</p>
<p>The second step is ensuring alignment between front and back ends. This is where you add an integration test that checks that the generated client is up to date with the back end. This is remarkably easy to achieve. You already have a mechanism for generating a TypeScript client from the back end code. You just need to generate the client code as part of your test, and then check that the generated code is the same as the already committed code.</p>
<p>Let's see what that looks like.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-integration-test">The integration test<a href="https://johnnyreilly.com/keeping-front-end-and-back-end-in-sync-with-nswag-generated-clients#the-integration-test" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I'm going to use <a href="https://vitest.dev/" target="_blank" rel="noopener noreferrer">Vitest</a> for this example. I love it, you could use pretty much any test framework you like. Really we're just checking that one string is the same as another and so you could get by without any test framework at all if you fancied.</p>
<p>In my example, I'll have a separate <code>client-server-tests</code> project for the tests. Here's the <code>package.json</code> for that project:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"name"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"client-server-tests"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"version"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"0.0.0"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"scripts"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"copy-original-generated-client"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"copyfiles --flat ../client-app/src/clients.ts tmp/"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"regenerate-generated-client"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"cd ../../ &amp;&amp; pnpm run generate-client"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"testprepare"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"pnpm run copy-original-generated-client &amp;&amp; pnpm run regenerate-generated-client"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"test:ci"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"pnpm run testprepare &amp;&amp; vitest run --reporter=default --reporter=junit --outputFile=reports/junit.xml"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"test"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"pnpm run testprepare &amp;&amp; vitest"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"devDependencies"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"copyfiles"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^2.4.1"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"typescript"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^5.8.3"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"vite"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^6.3.6"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"vitest"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^3.2.4"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>But you could fold this into your front end project directly if you prefer.</p>
<p>I'm not going to repeat the code of the previous post that demonstrated how to <a href="https://johnnyreilly.com/generate-typescript-and-csharp-clients-with-nswag">generate TypeScript clients with NSwag</a> - but please imagine that the code for the TypeScript client has been generated and is available in a front end project. The code above refers to the front end of that project as <code>client-app</code> and the generated client code is in <code>client-app/src/clients.ts</code>.</p>
<p>For that project, the back end is in C#, but it could be in any language that can generate an OpenAPI spec. The important thing is that the OpenAPI spec is the source of truth for the API.</p>
<p>You'll notice that both the <code>test:ci</code> and <code>test</code> scripts run a <code>testprepare</code> script first. This script does two things:</p>
<ol>
<li>It copies the currently checked-in generated client code to a temporary location.</li>
<li>It regenerates the client code from the OpenAPI spec (which would override the checked-in code).</li>
</ol>
<p>This leaves us ready to do our comparison. We can write a test that checks that the generated code is up to date. Here's what that looks like:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> expect</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> test </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'vitest'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> fs </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'fs'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">test</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'generated-client'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> committedClient </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> fs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">readFileSync</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'tmp/clients.ts'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'utf8'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> newlyGeneratedClient </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> fs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">readFileSync</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token string" style="color:rgb(165, 255, 144)">'../client-app/src/clients.ts'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token string" style="color:rgb(165, 255, 144)">'utf8'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function" style="color:rgb(250, 208, 0)">expect</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    committedClient</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token string" style="color:rgb(165, 255, 144)">"try `pnpm run generate-client`; generated client doesn't match the server"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">toBe</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">newlyGeneratedClient</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>This test simply compares the previously generated client code with the newly generated client code. If they don't match, the test fails and the message suggests running the client generation command. So as well as being a useful test, it also provides a helpful hint to the developer. Like so:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of failing test" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-failing-test-a5daf2ac77c5b0549ab23de6fbfa8f59.png" width="1456" height="490" class="img_ev3q"></p>
<p>Of course, you want to end up seeing a passing test:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of passing test" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-passing-test-3be0cdc3300c87afdf9f101647414ed9.png" width="650" height="236" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="finishing-up">Finishing up<a href="https://johnnyreilly.com/keeping-front-end-and-back-end-in-sync-with-nswag-generated-clients#finishing-up" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>The last thing to do is to make sure that this test project is run as part of your CI process. I'm not going to show you how to do this, because it will depend on your CI system. But the idea is that you add a step to your CI process that installs your dependencies and runs the tests in the <code>client-server-tests</code> project.</p>
<p>With that in place, you now have a way to ensure that your front end and back end are in sync. If someone makes a change to the back end that affects the OpenAPI spec, and they forget to regenerate the client code, the test will fail and they'll be prompted to run the client generation command.</p>
<p>If the change that they made in some way breaks the front end code, then the front end build will fail because of the compilation errors. So you get two layers of protection.</p>
<p>This is a simple but effective way to keep your front end and back end in sync, even if they are written in different languages.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Swagger" term="Swagger"/>
        <category label="C#" term="C#"/>
        <category label="Azure" term="Azure"/>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Microsoft Graph client: how to filter by endswith]]></title>
        <id>https://johnnyreilly.com/microsoft-graphclient-filter-endswith-consistencylevel-eventual-header</id>
        <link href="https://johnnyreilly.com/microsoft-graphclient-filter-endswith-consistencylevel-eventual-header"/>
        <updated>2025-05-11T13:21:31.000Z</updated>
        <summary type="html"><![CDATA[Learn how to filter by endswith using the Microsoft Graph client. This is a common use case when working with Azure AD groups.]]></summary>
        <content type="html"><![CDATA[<p>In this post we're going to look at filtering using an <code>endswith</code> filter with the <a href="https://learn.microsoft.com/en-us/graph/sdks/create-client?tabs=typescript" target="_blank" rel="noopener noreferrer">Microsoft Graph client</a>. This falls into the category of "Advanced query capabilities on Microsoft Entra ID objects" and I found tricky to get working.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Microsoft Graph client: how to filter by endswith&amp;quot; with the relevant logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-1afae25123c0c6e5a4c154eece7cc110.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>Performing an <code>endsWith</code> or similar filter shouldn't be difficult. But the method of how to do so isn't obvious. If you've ever encountered a message like this:</p>
<blockquote>
<p>Operator 'endsWith' is not supported because the 'ConsistencyLevel<!-- -->:eventual<!-- -->' header is missing. Refer to <a href="https://aka.ms/graph-docs/advanced-queries" target="_blank" rel="noopener noreferrer">https://aka.ms/graph-docs/advanced-queries</a> for more information</p>
</blockquote>
<p>Then this blog post is for you.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="related-posts">Related posts<a href="https://johnnyreilly.com/microsoft-graphclient-filter-endswith-consistencylevel-eventual-header#related-posts" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I've written before about the Microsoft Graph client, and how to use it with Azure AD groups. You can find those posts here:</p>
<ul>
<li><a href="https://johnnyreilly.com/azure-ad-claims-static-web-apps-azure-functions">Azure AD Claims with Static Web Apps and Azure Functions</a></li>
<li><a href="https://johnnyreilly.com/graph-api-ad-users-group-name-ids-csharp-sdk">Graph API: getting users Active Directory group names and ids with the C# SDK</a></li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="advanced-query-capabilities-on-microsoft-entra-id-objects">Advanced query capabilities on Microsoft Entra ID objects<a href="https://johnnyreilly.com/microsoft-graphclient-filter-endswith-consistencylevel-eventual-header#advanced-query-capabilities-on-microsoft-entra-id-objects" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>First let's quote from the documentation around <a href="https://learn.microsoft.com/en-us/graph/aad-advanced-queries?tabs=javascript" target="_blank" rel="noopener noreferrer">advanced query capabilities on Microsoft Entra ID objects</a>:</p>
<blockquote>
<p>Microsoft Graph supports advanced query capabilities on various Microsoft Entra ID objects, also called directory objects, to help you efficiently access data. For example, the addition of <strong>not</strong> (<code>not</code>), <strong>not equals</strong> (<code>ne</code>), and <strong>ends with</strong> (<code>endsWith</code>) operators on the <code>$filter</code> query parameter.</p>
</blockquote>
<p>Let's say we have a need for the <code>endsWith</code> operator or similar, for example: when querying for Entra ID / Azure AD groups. We may want to filter for groups that end with a certain string. And you can do that, but it does not work by default.</p>
<p>This is quite disappointing, and the documentation explains why this is the case:</p>
<blockquote>
<p>The Microsoft Graph query engine uses an index store to fulfill query requests. To add support for additional query capabilities on some properties, those properties might be indexed in a separate store. This separate indexing improves query performance. However, these advanced query capabilities aren't available by default but, the requestor must set the <code>ConsistencyLevel</code> header to eventual and, except for <code>$search</code>, use the <code>$count</code> query parameter. The <code>ConsistencyLevel</code> header and <code>$count</code> are referred to as advanced query parameters.</p>
</blockquote>
<p>I find this quite surprising. Essentially, the implementation of the <code>endsWith</code> and similar operators are bleeding through into the API design. This is quite clunky, and not something that I would expect from a modern API. It feels like a design decision that is more about performance than usability. Even if you do want to discourage the use of the <code>endsWith</code> operator, it would be nice to have a more user-friendly way of doing so. For example, you could have a separate endpoint for advanced queries, or simple query parameter that enables advanced queries without having to set headers and a seemingly arbitrary query parameter.</p>
<p>However, the good news is that it is possible to use the <code>endsWith</code> operator (and others like it) with the Microsoft Graph client. The bad news is the way you have to do it.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="failing-to-use-the-endswith-operator-with-the-microsoft-graph-client">Failing to use the <code>endsWith</code> operator with the Microsoft Graph client<a href="https://johnnyreilly.com/microsoft-graphclient-filter-endswith-consistencylevel-eventual-header#failing-to-use-the-endswith-operator-with-the-microsoft-graph-client" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We'll make what we're looking into concrete in this post, by having a meaningful example of querying the Graph client. We'll query for Entra ID / Azure AD groups. First let's see what a broken example looks like:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> Client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">PageCollection</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'@microsoft/microsoft-graph-client'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAzureADGroups</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  graphClient</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token plain">PageCollection</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> graphClient</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">api</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'/groups'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">filter</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">startsWith(displayName, 'startfilter-')</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">filter</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">endsWith(displayName, '-endfilter')</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">select</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'displayName'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'id'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> PageCollection</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>The above code is intended to query for Azure AD groups that start with <code>startfilter-</code> and end with <code>-endfilter</code>. However, it will fail with this error:</p>
<blockquote>
<p>Operator 'endsWith' is not supported because the 'ConsistencyLevel<!-- -->:eventual<!-- -->' header is missing. Refer to <a href="https://aka.ms/graph-docs/advanced-queries" target="_blank" rel="noopener noreferrer">https://aka.ms/graph-docs/advanced-queries</a> for more information</p>
</blockquote>
<p>This error is at least helpful in that it points you to the documentation. But it doesn't mention the <code>$count</code> query parameter. And it doesn't suggest how you might use the query parameter and header with the Microsoft Graph client.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="successfully-using-the-endswith-operator-with-the-microsoft-graph-client">Successfully using the <code>endsWith</code> operator with the Microsoft Graph client<a href="https://johnnyreilly.com/microsoft-graphclient-filter-endswith-consistencylevel-eventual-header#successfully-using-the-endswith-operator-with-the-microsoft-graph-client" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>To use the <code>endsWith</code> operator with the Microsoft Graph client, you need to set the <code>ConsistencyLevel</code> header to <code>eventual</code> and use the <code>$count</code> query parameter as you make your call.</p>
<p>I pieced together how to do this with the Microsoft Graph client based upon these two pieces of documentation:</p>
<ul>
<li><a href="https://learn.microsoft.com/en-us/graph/sdks/create-requests?tabs=typescript#use-http-headers-to-control-request-behavior" target="_blank" rel="noopener noreferrer">https://learn.microsoft.com/en-us/graph/sdks/create-requests?tabs=typescript#use-http-headers-to-control-request-behavior</a></li>
<li><a href="https://learn.microsoft.com/en-us/graph/sdks/create-requests?tabs=typescript#provide-custom-query-parameters" target="_blank" rel="noopener noreferrer">https://learn.microsoft.com/en-us/graph/sdks/create-requests?tabs=typescript#provide-custom-query-parameters</a></li>
</ul>
<p>Based upon this, I was able to produce the following code:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> Client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">PageCollection</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'@microsoft/microsoft-graph-client'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAzureADGroups</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  graphClient</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token plain">PageCollection</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> graphClient</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">api</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'/groups'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">query</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      $count</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'true'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">header</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'ConsistencyLevel'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'eventual'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">filter</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">startsWith(displayName, 'startfilter-')</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">filter</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">endsWith(displayName, '-endfilter')</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">select</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'displayName'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'id'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> PageCollection</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>This code sets the <code>ConsistencyLevel</code> header to <code>eventual</code> and uses the <code>$count</code> query parameter. This allows you to use the <code>endsWith</code> operator in your query. It works as expected and returns the groups that start with <code>startfilter-</code> and end with <code>-endfilter</code>.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="a-complete-example">A complete example<a href="https://johnnyreilly.com/microsoft-graphclient-filter-endswith-consistencylevel-eventual-header#a-complete-example" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>To make this a complete example, let's look at how you might use this in a real application. We'll create a function that retrieves Azure AD groups using the Microsoft Graph client. This function will use the <code>endsWith</code> operator and the <code>ConsistencyLevel</code> header.</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> DefaultAzureCredential </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'@azure/identity'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> Client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">PageCollection</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'@microsoft/microsoft-graph-client'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">AzureADGroup</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg name-of-group */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  displayName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg GUID-GUID-GUID-GUID-GUID */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getMyAzureADGroups</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token plain">AzureADGroup</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAzureADGroupsImpl</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">queryProvider</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">graphClient</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> graphClient</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">api</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'/me/memberOf'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">select</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'displayName'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'id'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> PageCollection</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAzureADGroups</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token plain">AzureADGroup</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAzureADGroupsImpl</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">queryProvider</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">graphClient</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> graphClient</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">api</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'/groups'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">query</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          $count</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'true'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">header</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'ConsistencyLevel'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'eventual'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">filter</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">startsWith(displayName, 'startfilter-')</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">filter</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">endsWith(displayName, '-endfilter')</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">select</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'displayName'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'id'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> PageCollection</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAzureADGroupsImpl</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  queryProvider</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">queryProvider</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">graphClient</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> Client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token plain">PageCollection</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">Promise</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token plain">AzureADGroup</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Use DefaultAzureCredential to authenticate</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> credential </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">DefaultAzureCredential</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Initialize the Graph client</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> graphClient </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> Client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">initWithMiddleware</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    authProvider</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token function-variable function" style="color:rgb(250, 208, 0)">getAccessToken</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> tokenResponse </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> credential</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">getToken</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token string" style="color:rgb(165, 255, 144)">'https://graph.microsoft.com/.default'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> tokenResponse</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">token</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> groups</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> AzureADGroup</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">let</span><span class="token plain"> response </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">queryProvider</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">graphClient</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">while</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">length </span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">0</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">for</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> group </span><span class="token keyword" style="color:rgb(255, 157, 0)">of</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">value </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> AzureADGroup</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// {</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">//   '@odata.type': '#microsoft.graph.group',</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">//   displayName: 'azure-our-engteam',</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">//   id: 'GUID-GUID-GUID-GUID-GUID'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// }</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        groups</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">push</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">group</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'@odata.nextLink'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        response </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> graphClient</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">api</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'@odata.nextLink'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> PageCollection</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">else</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">break</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> data</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> groups </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">err</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> errorMessage </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Error listing Entra ID / Azure AD groups: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">err </span><span class="token template-string interpolation keyword" style="color:rgb(255, 157, 0)">instanceof</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation operator" style="color:rgb(255, 157, 0)">?</span><span class="token template-string interpolation"> err</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">message </span><span class="token template-string interpolation operator" style="color:rgb(255, 157, 0)">:</span><span class="token template-string interpolation"> </span><span class="token template-string interpolation string" style="color:rgb(165, 255, 144)">'UNKNOWN'</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">errorMessage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> cause</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> err </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>This code has an implementation method <code>getAzureADGroupsImpl</code> that takes a <code>queryProvider</code> function. This function is responsible for providing the query to the Microsoft Graph client. The <code>getAzureADGroupsImpl</code> function handles the pagination of the results. It uses the <code>@odata.nextLink</code> property to retrieve the next page of results until there are no more pages left.</p>
<p>The <code>getAzureADGroups</code> and <code>getMyAzureADGroups</code> functions call this <code>getAzureADGroupsImpl</code> function method with their respective queries.</p>
<p>The <code>getMyAzureADGroups</code> function queries for the groups that the signed-in user is a member of. It does <strong>not</strong> use the <code>endsWith</code> operator. It simply queries for all groups that the user is a member of. This will work just fine without the <code>ConsistencyLevel</code> header or the <code>$count</code> query parameter.</p>
<p>The <code>getAzureADGroups</code> function queries for all groups that start with <code>startfilter-</code> and end with <code>-endfilter</code>. It uses the <code>endsWith</code> operator and consequently needs the <code>ConsistencyLevel</code> header and the <code>$count</code> query parameter. If it doesn't have these, it will fail with the error we saw earlier.</p>
<p>This code is a complete example of how to use the Microsoft Graph client to query for Azure AD groups using the <code>endsWith</code> operator. It handles authentication, pagination, and error handling. You can use this code as a starting point for your own applications that need to query Azure AD groups using the Microsoft Graph client.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/microsoft-graphclient-filter-endswith-consistencylevel-eventual-header#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>In this post, we looked at how to use the <code>endsWith</code> operator (and similar "advanced query" operators) with the Microsoft Graph client. We saw that it is possible to use advanced query operators with the Microsoft Graph client, but it requires setting the <code>ConsistencyLevel</code> header to <code>eventual</code> and using the <code>$count</code> query parameter.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Microsoft Graph" term="Microsoft Graph"/>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Introducing azdo-npm-auth (Azure DevOps npm auth)]]></title>
        <id>https://johnnyreilly.com/introducing-azdo-npm-auth</id>
        <link href="https://johnnyreilly.com/introducing-azdo-npm-auth"/>
        <updated>2024-12-08T08:21:50.000Z</updated>
        <summary type="html"><![CDATA[Azure DevOps npm auth eases setting up local authentication to Azure DevOps npm feeds, particularly for non Windows users.]]></summary>
        <content type="html"><![CDATA[<p>Azure DevOps has a feature called Azure Artifacts that supports publishing npm packages to a feed for consumption. Typically those npm packages are intended to be consumed by a restricted audience. To install a package published to a private feed you need to configure authentication, and for non Windows users this is a convoluted process.</p>
<p><img decoding="async" loading="lazy" alt="title image reading &amp;quot;Introducing Azure DevOps npm auth&amp;quot; with an Azure DevOps and npm logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-20d2fea1b99047c4bfb7a058a01ab1fb.png" width="800" height="450" class="img_ev3q"></p>
<p><a href="https://github.com/johnnyreilly/azdo-npm-auth" target="_blank" rel="noopener noreferrer"><code>azdo-npm-auth</code></a> exists to ease the setting up of local authentication to Azure DevOps npm feeds, particularly for non Windows users.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-problem-are-we-solving">What problem are we solving?<a href="https://johnnyreilly.com/introducing-azdo-npm-auth#what-problem-are-we-solving" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Consider the onboarding process for a Windows user for consuming an Azure Artifact npm feed:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of the onboarding process for Windows users" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-onboarding-with-windows-f829c5e94b4ccdc17ec2c9c325cfaca5.png" width="2168" height="592" class="img_ev3q"></p>
<p>Now consider the onboarding process for a non Windows user:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of the onboarding process for non Windows users" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-onboarding-with-other-f763bbba47f0835be032ce1bdeb202bb.png" width="2168" height="1606" class="img_ev3q"></p>
<p>As we can see, there is a significant difference in the onboarding experience between operating systems. Windows users can use a tool named <a href="https://www.npmjs.com/package/vsts-npm-auth" target="_blank" rel="noopener noreferrer"><code>vsts-npm-auth</code></a> which automates onboarding. Non Windows users have a longer road to follow. The instructions walk through manually creating an <code>.npmrc</code> file in a users home directory which contains information including a base 64 encoded Azure DevOps Personal Access Token with the Packaging read and write scopes. It is tedious to do.</p>
<p><code>azdo-npm-auth</code> aims to automate the toil, and make the onboarding experience for non Windows users as simple as it is for Windows users.</p>
<p>There is an official package named <a href="https://github.com/microsoft/ado-npm-auth" target="_blank" rel="noopener noreferrer"><code>ado-npm-auth</code></a>. However, <a href="https://github.com/microsoft/ado-npm-auth/issues/50" target="_blank" rel="noopener noreferrer">due to issues I experienced in using the <code>ado-npm-auth</code> package</a>, I found myself creating <code>azdo-npm-auth</code>. By the way, the package was briefly named <code>ado-npm-auth-lite</code>; I renamed it as I felt <code>azdo-npm-auth</code> was a better name.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="when-do-i-need-to-run-azdo-npm-auth">When do I need to run <code>azdo-npm-auth</code>?<a href="https://johnnyreilly.com/introducing-azdo-npm-auth#when-do-i-need-to-run-azdo-npm-auth" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Should you encounter the following message when you try to <code>npm i</code>:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error code E401</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error Unable to authenticate, your authentication token seems to be invalid.</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error To correct this please try logging </span><span class="token keyword" style="color:rgb(255, 157, 0)">in</span><span class="token plain"> again with:</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error </span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> login</span><br></span></code></pre></div></div>
<p>OR</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error code E401</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error Incorrect or missing password.</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error If you were trying to login, change your password, create an</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error authentication token or </span><span class="token builtin class-name" style="color:rgb(250, 208, 0)">enable</span><span class="token plain"> two-factor authentication </span><span class="token keyword" style="color:rgb(255, 157, 0)">then</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error that means you likely typed your password </span><span class="token keyword" style="color:rgb(255, 157, 0)">in</span><span class="token plain"> incorrectly.</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error Please try again, or recover your password at:</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error   https://www.npmjs.com/forgot</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error If you were doing some other operation </span><span class="token keyword" style="color:rgb(255, 157, 0)">then</span><span class="token plain"> your saved credentials are</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error probably out of date. To correct this please try logging </span><span class="token keyword" style="color:rgb(255, 157, 0)">in</span><span class="token plain"> again with:</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> error   </span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> login</span><br></span></code></pre></div></div>
<p>That means either:</p>
<ul>
<li>You have no user <code>.npmrc</code> file <strong>OR</strong></li>
<li>The token in your user <code>.npmrc</code> file is out of date</li>
</ul>
<p>In either case, running <code>azdo-npm-auth</code> should resolve the issue. But the way you run it is important. To get <code>azdo-npm-auth</code> to create the necessary user <code>.npmrc</code> file for local development, run the following command:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">npx </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-y</span><span class="token plain"> </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--registry</span><span class="token plain"> https://registry.npmjs.org azdo-npm-auth</span><br></span></code></pre></div></div>
<p>It is possible to use environment variables to control the <code>registry</code> setting as well; consider the following (non-Windows compatible) example:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token assign-left variable" style="color:rgb(255, 238, 128)">npm_config_registry</span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain">https://registry.npmjs.org npx azdo-npm-auth</span><br></span></code></pre></div></div>
<p>You might be wondering what the <code>--registry https://registry.npmjs.org</code> part is for. It is a way to ensure that the <code>npx</code> command uses the <strong>public</strong> npm registry to install <code>azdo-npm-auth</code>. Without this, you might encounter a <code>npm error code E401</code> error like those above.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="configuration">Configuration<a href="https://johnnyreilly.com/introducing-azdo-npm-auth#configuration" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p><code>azdo-npm-auth</code> requires that a project <code>.npmrc</code> file exists in order that it can acquire the information to run.</p>
<p>There is an optional <code>config</code> parameter which allows selection of a specific project <code>.npmrc</code> file. If the <code>config</code> parameter is not supplied, <code>azdo-npm-auth</code> will default to use the <code>.npmrc</code> in the current project directory.</p>
<p>Should you not have one of these files already, there will be information in your Azure DevOps Artifacts section for connecting to the npm feed around creating a project <code>.npmrc</code> file. The required file should look something like this:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token assign-left variable" style="color:rgb(255, 238, 128)">registry</span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain">https://pkgs.dev.azure.com/johnnyreilly/_packaging/npmrc-script-organization/npm/registry/</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">always-auth</span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain">true</span><br></span></code></pre></div></div>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="authenticating-to-azure">Authenticating to Azure<a href="https://johnnyreilly.com/introducing-azdo-npm-auth#authenticating-to-azure" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>If you would like <code>azdo-npm-auth</code> to acquire a token on your behalf, then it requires that your <a href="https://learn.microsoft.com/en-us/azure/devops/organizations/accounts/connect-organization-to-azure-ad?view=azure-devops" target="_blank" rel="noopener noreferrer">Azure DevOps organisation is connected with your Azure account / Microsoft Entra ID</a>. Then, assuming you are authenticated with Azure, it can acquire an Azure DevOps Personal Access Token on your behalf. To authenticate, run <code>az login</code>. <a href="https://learn.microsoft.com/en-us/cli/azure/install-azure-cli" target="_blank" rel="noopener noreferrer">If you need to install the Azure CLI, follow these instructions</a>. It is not necessary to run <code>az login</code> if you are already authenticated with Azure.</p>
<p>If you would like to acquire a PAT token manually, there is a <code>--pat</code> option for that very circumstance.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="integration-with-packagejson">Integration with <code>package.json</code><a href="https://johnnyreilly.com/introducing-azdo-npm-auth#integration-with-packagejson" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="using-a-preinstall-script">Using a <code>preinstall</code> script<a href="https://johnnyreilly.com/introducing-azdo-npm-auth#using-a-preinstall-script" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>A great way to integrate <code>azdo-npm-auth</code> is by using it in a <code>preinstall</code> script in your <code>package.json</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token property" style="color:rgb(255, 157, 0)">"scripts"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"preinstall"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"npx --yes azdo-npm-auth --config ./subdirectory-with-another-package-json/.npmrc"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><br></span></code></pre></div></div>
<p>The <code>--yes</code> flag above simply skips having npm challenge the user as to whether to download the package.</p>
<p>However, as you're probably noticing, this requires having multiple <code>package.json</code>s and only having the <code>.npmrc</code> file in the nested one. Assuming that works for you, brilliant. It may not - no worries. We'll talk about that in a second.</p>
<p>With the above <code>preinstall</code> script in place, when the user performs <code>npm i</code> or similar, before attempting to install, the relevant user <code>.npmrc</code> file will be put in place so that installation just works™️. This is a <strong>great</strong> developer experience.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="using-an-auth-script">Using an <code>auth</code> script<a href="https://johnnyreilly.com/introducing-azdo-npm-auth#using-an-auth-script" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>If the complexity of nested <code>package.json</code>s doesn't work for you, we generally advise setting up a script like the one below:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token property" style="color:rgb(255, 157, 0)">"scripts"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"auth"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"npx -y --registry https://registry.npmjs.org azdo-npm-auth"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><br></span></code></pre></div></div>
<p>And running <code>npm run auth</code> when a <code>npm error code E401</code> is encountered. (Your script doesn't have to be called <code>auth</code> necessarily - if you like you could call it <code>fix-code-e401</code>, or something else entirely.)</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-about-ci">What about CI?<a href="https://johnnyreilly.com/introducing-azdo-npm-auth#what-about-ci" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>You might be worried about <code>azdo-npm-auth</code> trying to create user <code>.npmrc</code> files when running CI builds. Happily this does not happen; it detects whether it is running in a CI environment and does <strong>not</strong> create a user <code>.npmrc</code> file in that case.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="summary">Summary<a href="https://johnnyreilly.com/introducing-azdo-npm-auth#summary" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>If you're a Mac or a Linux user, hopefully <code>azdo-npm-auth</code> can significantly ease the friction experienced doing local development with Azure DevOps npm feeds. You can see the <a href="https://github.com/johnnyreilly/azdo-npm-auth" target="_blank" rel="noopener noreferrer">project code on GitHub here</a>.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Azure DevOps" term="Azure DevOps"/>
        <category label="Node.js" term="Node.js"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[List Pipelines with the Azure DevOps API]]></title>
        <id>https://johnnyreilly.com/list-pipelines-with-azure-devops-api</id>
        <link href="https://johnnyreilly.com/list-pipelines-with-azure-devops-api"/>
        <updated>2025-04-06T18:53:41.000Z</updated>
        <summary type="html"><![CDATA[Learn how to list the Azure Pipelines in a project using the Azure DevOps REST API with TypeScript and the continuation token.]]></summary>
        <content type="html"><![CDATA[<p>Listing the Azure Pipelines using the Azure DevOps REST API and TypeScript is possible, but if you use the official <a href="https://github.com/microsoft/azure-devops-node-api" target="_blank" rel="noopener noreferrer">Azure DevOps Client for Node.js</a>, you might have issues. This is because it does not support pagination. So if you have a project with a large number of pipelines, then using the official client might mean you cannot retrieve it.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;List Pipelines with the Azure DevOps API&amp;quot; with the relevant logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-235c4c0ac8257bde8337e841a0992919.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>This post implements an alternative mechanism, directly using the Azure DevOps API and thus handling pagination. If you're curious as to how to create a pipeline, then check out my post on <a href="https://johnnyreilly.com/list-pipelines-with-azure-devops-api">creating a pipeline with the Azure DevOps API</a>.</p>
<p>Here's the TypeScript code:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">AzureDevOpsPipeline</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  _links</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    self</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg "https://dev.azure.com/my-ado-organisation/87cf415b-a062-4f62-93e2-b37c26aa268b/_apis/pipelines/6805?revision=1" */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      href</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    web</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg "https://dev.azure.com/my-ado-organisation/87cf415b-a062-4f62-93e2-b37c26aa268b/_build/definition?definitionId=6805" */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      href</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg "\\my-app" or "\\" */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  folder</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg 25978 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  id</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg "pipeline-name" */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  name</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg 1 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  revision</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg "https://dev.azure.com/my-ado-organisation/87cf415b-a062-4f62-93e2-b37c26aa268b/_apis/pipelines/25978?revision=1" */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  url</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">getAzureDevOpsPipelines</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  personalAccessToken</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  organization</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  personalAccessToken</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg "my-ado-organisation" */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  organization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** eg "my-ado-project" */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  projectName</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> batchSize </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">100</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">let</span><span class="token plain"> continuationToken </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> pipelines</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> AzureDevOpsPipeline</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">while</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// https://learn.microsoft.com/en-us/rest/api/azure/devops/pipelines/pipelines/list?view=azure-devops-rest-7.1</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> url </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">https://dev.azure.com/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">organization</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">projectName</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">/_apis/pipelines?api-version=7.1&amp;$top=</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">batchSize</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">toString</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string string" style="color:rgb(165, 255, 144)">&amp;continuationToken=</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">continuationToken</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">try</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> response </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">fetch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">url</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        method</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'GET'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        headers</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          Accept</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'application/json'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'Content-Type'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'application/json'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          Authorization</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">Basic </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">Buffer</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">from</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string interpolation template-string string" style="color:rgb(165, 255, 144)">PAT:</span><span class="token template-string interpolation template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation template-string interpolation">personalAccessToken</span><span class="token template-string interpolation template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string interpolation template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">toString</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation string" style="color:rgb(165, 255, 144)">'base64'</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'X-TFS-FedAuthRedirect'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'Suppress'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">ok</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token template-string string" style="color:rgb(165, 255, 144)">HTTP error! status: </span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">${</span><span class="token template-string interpolation">response</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation">status</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token template-string interpolation function" style="color:rgb(250, 208, 0)">toString</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token template-string interpolation punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token template-string interpolation interpolation-punctuation punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token template-string template-punctuation string" style="color:rgb(165, 255, 144)">`</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// this will be the name of the next pipeline or not present if there are no more pipelines</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      continuationToken </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">headers</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'x-ms-continuationtoken'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">''</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> json </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> response</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">json</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> nextPipelines </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> json </span><span class="token keyword" style="color:rgb(255, 157, 0)">as</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> value</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> AzureDevOpsPipeline</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// TODO: validate with Zod</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">nextPipelines</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">length </span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">0</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        pipelines</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">push</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">...</span><span class="token plain">nextPipelines</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> noMorePipelines </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        nextPipelines</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">length </span><span class="token operator" style="color:rgb(255, 157, 0)">===</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">0</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">!</span><span class="token plain">continuationToken</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">noMorePipelines</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">break</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token builtin" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Error:'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">throw</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> pipelines</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>The above code uses the <code>fetch</code> API to list the Azure DevOps pipelines, but significantly, it takes the <code>x-ms-continuationtoken</code> from the response headers, so you can keep paginating if the number of pipelines exceeds the page size.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Azure Pipelines" term="Azure Pipelines"/>
        <category label="Azure DevOps" term="Azure DevOps"/>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Slash command your deployment with GitHub Actions]]></title>
        <id>https://johnnyreilly.com/slash-command-your-deployment-with-github-actions</id>
        <link href="https://johnnyreilly.com/slash-command-your-deployment-with-github-actions"/>
        <updated>2025-01-03T08:47:57.000Z</updated>
        <summary type="html"><![CDATA[Slash commands are a great way to interact with your GitHub issues. In this post, we look at how to implement a `/deploy` slash command to deploy an Azure Container Apps service with GitHub Actions.]]></summary>
        <content type="html"><![CDATA[<p>In the world of computing, slash commands have a proud and noble history. They are a way to interact with a system by typing a command into a chat or terminal, usually with a <code>/</code> preceding the command; hence the name "slash commands". <a href="https://docs.github.com/en/issues/tracking-your-work-with-issues/using-issues/about-slash-commands" target="_blank" rel="noopener noreferrer">GitHub has its own slash commands</a> that you can use in issues and pull requests to add code blocks and tables etc. The slash commands are, in truth, quite limited.</p>
<p>However, through clever use of the GitHub Actions platform, it's possible to build something quite powerful which is "slash-command-shaped". In this post, we'll look at how to implement a <code>/deploy</code> slash command which, when invoked in a pull request, will deploy an Azure Container App with GitHub Actions.</p>
<p><img decoding="async" loading="lazy" alt="title image reading &amp;quot;Slash command your deployment with GitHub Actions&amp;quot; with the GitHub Actions logo" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-5c895b855a4bb9606fc2019f57811c42.png" width="800" height="450" class="img_ev3q"></p>
<p>The technique we'll use is covering a deployment usecase, as we'll see, it could be adapted to many other scenarios.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="first-a-bit-about-nuns">First a bit about nuns<a href="https://johnnyreilly.com/slash-command-your-deployment-with-github-actions#first-a-bit-about-nuns" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I have an aunt that is a Poor Clare nun, and I've been <a href="https://johnnyreilly.com/the-convent-with-continuous-delivery">over-engineering her convent's website for years</a>. Most of the time the site moulders away, but every now and then I get a flurry of requests for minor changes. Once I've made the changes, they go live thanks to the magic of continuous deployment. But there's only ever been a single environment; production or "main".</p>
<p>Sometimes I'd like to eyeball a change before I've shipped it. Not always, sometimes. A particular case where this is useful, is when <a href="https://www.mend.io/renovate/" target="_blank" rel="noopener noreferrer">Renovate</a> has submitted a dependency upgrade PR, and I'd like to see the impact without having to install and run it locally somewhere. Because, unless I instead hit "merge" with crossed fingers, that's what I'll need to do. (I have done this and it doesn't always end well.)</p>
<p>So I decided it was time that the "Convent with Continuous Delivery™️" had a staging environment. And I decided that I'd like to be able to deploy to it by entering the slash command <code>/deploy</code> in a pull request comment. Like this:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of pull request comments" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-pull-request-comments-39dd71778a3039bc342d6349667fd81c.webp" width="440" height="501" class="img_ev3q"></p>
<p>As we can see, I entered <code>/deploy</code> in a comment. In response, a GitHub Actions workflow then kicked off and deployed the staging environment. How did I do this? Let's find out.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="the-github-actions-workflow">The GitHub Actions workflow<a href="https://johnnyreilly.com/slash-command-your-deployment-with-github-actions#the-github-actions-workflow" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>The secret sauce that makes implementing slash commands in GitHub Actions possible is the <a href="https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#issue_comment" target="_blank" rel="noopener noreferrer"><code>issue_comment</code> event</a>. This event is triggered when an issue or pull request comment is created, edited, or deleted. We're interested in the situation where a pull request comment is created, and it contains the <code>/deploy</code> command.</p>
<p>Based upon the <a href="https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#issue_comment-on-issues-only-or-pull-requests-only" target="_blank" rel="noopener noreferrer">example here</a> it's possible to create a workflow that is triggered by the <code>issue_comment</code> event, but only when the comment is on a pull request, and that comment contains the text <code>/deploy</code>.</p>
<p>Here's the workflow:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">issue_comment</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">types</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">created</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">run-for-pr-comment-with-deploy-command</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># check if the comment comes from a pull request and contains the command `/deploy`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">if</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> github.event.issue.pull_request </span><span class="token important">&amp;&amp;</span><span class="token plain"> contains(github.event.comment.body</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> '/deploy')</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># ...</span><br></span></code></pre></div></div>
<p>The <code>if</code> statement is the key to this workflow. It checks if the comment comes from a pull request and contains the command <code>/deploy</code>. If both conditions are met, the workflow continues. We're in business!</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="avoiding-duplication-with-a-reusable-workflow">Avoiding duplication with a reusable workflow<a href="https://johnnyreilly.com/slash-command-your-deployment-with-github-actions#avoiding-duplication-with-a-reusable-workflow" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I already have a GitHub Actions workflow that deploys the main environment. I don't want to duplicate this logic in the new workflow. Instead, I want to reuse the existing workflow and just pass in a different environment name. This is where <a href="https://docs.github.com/en/actions/learn-github-actions/reusing-workflows" target="_blank" rel="noopener noreferrer">reusable workflows</a> come in.</p>
<p>I think of these as functions that can be called from other workflows. They have inputs and outputs, and can be parameterised.</p>
<p>I migrated the deployment logic to a reusable workflow called <code>util-build-and-deploy.yaml</code>. I pondered the best way to share this information with you, and I've finally opted to include the entire workflow here. It's a bit long, but I think it's the best way to show you how it all fits together:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Build and deploy</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">workflow_call</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 98, 140)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> boolean</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">branchName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 98, 140)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> string</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">outputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">containerAppUrl</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'The URL of the deployed container app'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> jobs.deploy.outputs.containerAppUrl </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">RESOURCE_GROUP</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> rg</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">my</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">convent</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">REGISTRY</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ghcr.io</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">build</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> read</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">packages</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> write</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">outputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">image-name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> steps.vars.outputs.image_name </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">sha-short</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> steps.vars.outputs.sha_short </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">built-at</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> steps.vars.outputs.built_at </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Checkout repository</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> actions/checkout@v4</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">ref</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> inputs.branchName </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Set sha</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">short and image</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name environment variables</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> vars</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">|</span><span class="token scalar string" style="color:rgb(165, 255, 144)"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">          image_name=$(echo "${{ env.REGISTRY }}/${{ github.repository }}/node-service" | tr '[:upper:]' '[:lower:]')</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">          echo "image_name=$image_name" &gt;&gt; $GITHUB_OUTPUT</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">          sha_short=$(echo "$(git rev-parse --short HEAD)" | tr '[:upper:]' '[:lower:]')</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">          echo "sha_short=$sha_short" &gt;&gt; $GITHUB_OUTPUT</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">          echo "built_at=$(date +'%Y-%m-%dT%H:%M:%S')" &gt;&gt; $GITHUB_OUTPUT</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># Login against a Docker registry</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># https://github.com/docker/login-action</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Log into registry $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> env.REGISTRY </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> docker/login</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">action@v3</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">registry</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> env.REGISTRY </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">username</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> github.actor </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">password</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> secrets.GITHUB_TOKEN </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># Extract metadata (tags, labels) for Docker</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># https://github.com/docker/metadata-action</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Extract Docker metadata</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> meta</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> docker/metadata</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">action@v5</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">images</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> steps.vars.outputs.image_name </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> git </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># so it uses the git branch that is checked out</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">tags</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">|</span><span class="token scalar string" style="color:rgb(165, 255, 144)"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            type=semver,pattern={{version}}</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            type=semver,pattern={{major}}.{{minor}}</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            type=semver,pattern={{major}}</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            type=ref,event=branch</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            type=ref,event=pr</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            type=sha</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># Build and push Docker image with Buildx (don't push if deploy is false)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># https://github.com/docker/build-push-action</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Build and push Docker image</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> docker/build</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">push</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">action@v6</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ./</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">push</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> inputs.deploy </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">tags</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> steps.meta.outputs.tags </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">labels</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> steps.meta.outputs.labels </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">build-args</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">|</span><span class="token scalar string" style="color:rgb(165, 255, 144)"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            VITE_BRANCH_NAME=${{ inputs.branchName }}</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            VITE_GIT_SHA=${{ steps.vars.outputs.sha_short }}</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            VITE_BUILT_AT=${{ steps.vars.outputs.built_at }}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">if</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> inputs.deploy == true</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">needs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> build</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">outputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">containerAppUrl</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> steps.deploy.outputs.CONTAINER_APP_URL </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">permissions</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">id-token</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> write</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">contents</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> read</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">packages</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> write</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Checkout repository</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> actions/checkout@v4</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">ref</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> inputs.branchName </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Azure login</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> azure/login@v2</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">client-id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> secrets.AZURE_CLIENT_ID </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">tenant-id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> secrets.AZURE_TENANT_ID </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">subscription-id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> secrets.AZURE_SUBSCRIPTION_ID </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Deploy to Azure</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> deploy</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> azure/CLI@v2</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">inlineScript</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">|</span><span class="token scalar string" style="color:rgb(165, 255, 144)"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            RESOURCE_GROUP="${{ env.RESOURCE_GROUP }}"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            BUILT_AT="${{ needs.build.outputs.built-at }}"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            BRANCH_NAME="${{ inputs.branchName }}"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            SHA_SHORT="${{ needs.build.outputs.sha-short }}"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            REF_SHA="${{ inputs.branchName }}.${{ needs.build.outputs.sha-short }}"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            DEPLOYMENT_NAME="${REF_SHA////-}"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">            echo "DEPLOYMENT_NAME=$DEPLOYMENT_NAME"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            webServiceImage="$</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> needs.build.outputs.image</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain">sha</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">$SHA_SHORT"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            echo "webServiceImage=$webServiceImage"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            if </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain"> "$BRANCH_NAME" == "main" </span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain">; then</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              webServiceContainerAppName="main</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">web"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            else</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              webServiceContainerAppName="preview</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">web"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            fi</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            echo "webServiceContainerAppName=$webServiceContainerAppName"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            TAGS='</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">"owner"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token string" style="color:rgb(165, 255, 144)">"johnnyreilly"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> "email"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token string" style="color:rgb(165, 255, 144)">"johnny_reilly@hotmail.com"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain">'</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            az deployment group create \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">resource</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">group $RESOURCE_GROUP \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name "$DEPLOYMENT_NAME" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">template</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">file ./infra/main.bicep \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">parameters \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  webServiceImage="$webServiceImage" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  containerRegistry=$</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> env.REGISTRY </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  containerRegistryUsername=$</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> github.actor </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  containerRegistryPassword=$</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> secrets.PACKAGES_TOKEN </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  branchName="$BRANCH_NAME" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  gitSha="$SHA_SHORT" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  builtAt="$BUILT_AT" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  workspaceName='shared</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">log</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">analytics' \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  appInsightsName='shared</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">insights' \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  managedEnvironmentName='shared</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">env' \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  webServiceContainerAppName="$webServiceContainerAppName" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  tags="$TAGS" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  APPSETTINGS_API_KEY="$</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> secrets.APPSETTINGS_API_KEY </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain">" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  APPSETTINGS_DOMAIN="$</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> vars.APPSETTINGS_DOMAIN </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain">" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  APPSETTINGS_PRAYER_REQUEST_FROM_EMAIL="$</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> vars.APPSETTINGS_PRAYER_REQUEST_FROM_EMAIL </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain">" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  APPSETTINGS_PRAYER_REQUEST_RECIPIENT_EMAIL="$</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> vars.APPSETTINGS_PRAYER_REQUEST_RECIPIENT_EMAIL </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain">"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            CONTAINER_APP_URL=$(az containerapp show \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">resource</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">group "$RESOURCE_GROUP" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name "$webServiceContainerAppName" \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">query properties.configuration.ingress.fqdn \</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">output tsv)</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            echo "CONTAINER_APP_URL=$CONTAINER_APP_URL"</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            echo "CONTAINER_APP_URL=$CONTAINER_APP_URL" </span><span class="token punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"> $GITHUB_OUTPUT</span><br></span></code></pre></div></div>
<p>Let's talk through what this workflow does:</p>
<ul>
<li>It's triggered by a <code>workflow_call</code> event, which is how reusable workflows are triggered.</li>
<li>It has two jobs: <code>build</code> and <code>deploy</code> and the <code>deploy</code> job is only run if the <code>deploy</code> input is <code>true</code>. (This allows us to call the workflow with <code>deploy: false</code> to only build the image)</li>
<li>The <code>build</code> job checks out the code, sets some environment variables, logs into the Docker registry, extracts metadata for Docker, builds and pushes the Docker image.</li>
<li>The <code>deploy</code> job checks out the code, logs into Azure, and deploys the container app to Azure Container Apps. It then outputs the URL of the deployed container app in order that we can display it to the user.</li>
</ul>
<p>Now most of this workflow is the same as the one I was originally using to deploy to the main environment. The key difference is it is now parameterised with the <code>branchName</code> input. This is important for two reasons:</p>
<ol>
<li>It allows us to deploy to different environments based on the branch name. In our case we'll deploy to the <code>preview-web</code> container app if the branch name is not <code>main</code>. Otherwise we'll deploy to <code>main-web</code>.</li>
<li>(and this is more subtle) We need to checkout the relevant branch of the repository in our workflow, so that we're building and deploying the correct thing. So you'll see us use the <code>branchName</code> input in the <code>actions/checkout</code> steps and you'll see us use <code>context: git</code> in the <code>docker/metadata-action</code> step.</li>
</ol>
<p>You might be thinking at this point, "fine - but I don't have a containerised application and I don't have an Azure Container Apps service to deploy to". That's great! You can adapt this workflow to build any type of app you would like and deploy to any type of service. The crucial part is that you must build and deploy the code for the correct branch. This is why we pass the <code>branchName</code> input to the workflow.</p>
<p>And now some bad news: the <code>issue_comment</code> event <strong>doesn't</strong> know the branch that the pull request is for. We're going to need to build <em>another</em> reusable workflow that we will use to determine the branch name of the pull request.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="getting-the-branch-name-of-the-pull-request">Getting the branch name of the pull request<a href="https://johnnyreilly.com/slash-command-your-deployment-with-github-actions#getting-the-branch-name-of-the-pull-request" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Now we're old hands at creating reusable workflows, we're going to create another one that will determine the branch name of the pull request. We'll call this workflow <code>util-get-pr-branch-name.yaml</code>:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Get PR branch name</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">workflow_call</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">pullRequestNumber</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">required</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 98, 140)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> number</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">outputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">branchName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">description</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'The source branch name for the pull request'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">value</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> jobs.get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">pr</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">branch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name.outputs.branchName </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">get-pr-branch-name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">outputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">branchName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> steps.get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">pr</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">branch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name.outputs.branchName </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">id</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">pr</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">branch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">|</span><span class="token scalar string" style="color:rgb(165, 255, 144)"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">          branchName=$(gh pr view ${{ inputs.pullRequestNumber }} --json "headRefName" --jq ".headRefName" --repo ${{ github.repository }})</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">          echo "branchName=$branchName" &gt;&gt; $GITHUB_OUTPUT</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">GH_TOKEN</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> github.token </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>This is fairly self explanatory. The workflow takes a <code>pullRequestNumber</code> input and outputs the <code>branchName</code> of the pull request. It uses the <code>gh</code> CLI to get the branch name of the pull request using the <code>headRefName</code> property of a pull request. (Incidentally, the <code>env: GH_TOKEN: ${{ github.token }}</code> line is important as it allows the workflow to authenticate with GitHub.)</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="putting-it-all-together">Putting it all together<a href="https://johnnyreilly.com/slash-command-your-deployment-with-github-actions#putting-it-all-together" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Now we have our two reusable workflows, we can put them together in a workflow that is triggered by the <code>issue_comment</code> event. This workflow will call the <code>util-get-pr-branch-name.yaml</code> workflow to get the branch name of the pull request, and then call the <code>util-build-and-deploy.yaml</code> workflow to build and deploy the code for that branch. Here's the <code>pull-request-commands.yaml</code> workflow:</p>
<div class="language-yaml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yaml codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token key atrule">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Pull request commands</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">on</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">issue_comment</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">types</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">created</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">get-pr-branch-name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ./.github/workflows/util</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">pr</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">branch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name.yaml</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">pullRequestNumber</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> github.event.issue.number </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">pre-deploy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># check if the comment comes from a pull request and contains the command `/deploy`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">if</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> github.event.issue.pull_request </span><span class="token important">&amp;&amp;</span><span class="token plain"> contains(github.event.comment.body</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> '/deploy')</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">|</span><span class="token scalar string" style="color:rgb(165, 255, 144)"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">          gh issue comment ${{ github.event.issue.number }} --body "Preview environment [deploying](https://github.com/johnnyreilly/poorclaresarundel-aca/actions/runs/${{ github.run_id }})..." --repo ${{ github.repository }}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">GH_TOKEN</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> secrets.GITHUB_TOKEN </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic"># check if the comment comes from a pull request and contains the command `/deploy`</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">if</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> github.event.issue.pull_request </span><span class="token important">&amp;&amp;</span><span class="token plain"> contains(github.event.comment.body</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> '/deploy')</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">needs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">pr</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">branch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> pre</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">deploy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">uses</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ./.github/workflows/util</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">build</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">and</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">deploy.yaml</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">with</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">deploy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 98, 140)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token key atrule">branchName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> needs.get</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">pr</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">branch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">name.outputs.branchName </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">secrets</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> inherit</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">post-deploy</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">runs-on</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">needs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> deploy</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">run</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">|</span><span class="token scalar string" style="color:rgb(165, 255, 144)"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token scalar string" style="color:rgb(165, 255, 144)">          gh issue comment ${{ github.event.issue.number }} --body "Preview environment deployed: https://${{ needs.deploy.outputs.containerAppUrl }}" --repo ${{ github.repository }}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">env</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token key atrule">GH_TOKEN</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> secrets.GITHUB_TOKEN </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>What you're hopefully gleaning from the above is that we have 4 jobs in this workflow:</p>
<ul>
<li><code>get-pr-branch-name</code> - this job calls the <code>util-get-pr-branch-name.yaml</code> workflow to get the branch name of the pull request. Note that we pass the <code>github.event.issue.number</code> as the <code>pullRequestNumber</code> input.</li>
<li><code>pre-deploy</code> - this job runs immediately to post a comment in the pull request to let the user know that the preview environment is being deployed. (Again using the GitHub CLI.) The comment gives the user feedback that the command has been received and is being actioned. Based upon my experience, this response will show up in the pull request 5-10 seconds after the <code>/deploy</code> command is entered. Not as fast as I'd like, but reasonable. For bonus points, I've chosen to include a link to the GitHub Actions run that is deploying the preview environment. This is useful as it allows the user to see the progress of the deployment.</li>
<li><code>deploy</code> - this job calls the <code>util-build-and-deploy.yaml</code> workflow to build and deploy the code for the branch. Note that we pass the <code>branchName</code> input to the workflow using the <code>get-pr-branch-name.outputs.branchName</code> output. Note also that we're passing <code>deploy: true</code> to the workflow to ensure that the code is deployed, and that we're inheriting the secrets from the parent workflow. This is important as it allows the child workflow to access the secrets it needs to deploy the code.</li>
<li><code>post-deploy</code> - this job posts a comment in the pull request to let the user know that the preview environment has been deployed and where they can find it.</li>
</ul>
<p>Or maybe I should have said it better as a screenshot:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of pull request comments" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-pull-request-comments-39dd71778a3039bc342d6349667fd81c.webp" width="440" height="501" class="img_ev3q"></p>
<p>Yup! That's the same screenshot as before. I'm just showing it again to remind you that this is what we've built.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="writing-other-slash-commands">Writing other slash commands<a href="https://johnnyreilly.com/slash-command-your-deployment-with-github-actions#writing-other-slash-commands" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We've written a slash command for deployment in this post, but you could write a slash command for anything you like. The key is to use the <code>issue_comment</code> event to trigger the workflow, and to check the comment body for the command you're interested in. You could pass more information in the comment body than just the slash command. For example, you could pass the name of the environment you want to deploy to, or the version of the app you want to deploy. You could even pass multiple commands in a single comment. The world is your oyster!</p>
<p>You can then call other workflows to do the heavy lifting for you, remembering to pass in any inputs that are needed.</p>
<p>If you would like to see the repo where this was implemented, <a href="https://github.com/johnnyreilly/poorclaresarundel-aca/tree/f052dd2f5d55bcec8547624e928bbf90432f3872" target="_blank" rel="noopener noreferrer">look here</a>.</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="GitHub Actions" term="GitHub Actions"/>
        <category label="Azure Container Apps" term="Azure Container Apps"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Full-stack static typing with OpenAPI TypeScript and Microsoft.AspNetCore.OpenApi]]></title>
        <id>https://johnnyreilly.com/dotnet-openapi-and-openapi-ts</id>
        <link href="https://johnnyreilly.com/dotnet-openapi-and-openapi-ts"/>
        <updated>2026-01-02T20:01:38.000Z</updated>
        <summary type="html"><![CDATA[This post will show you how to write full stack applications with static typing from back to front using OpenAPI, TypeScript and .NET.]]></summary>
        <content type="html"><![CDATA[<p>I've long believed in the benefits of static typing. Static typing helps you catch errors early, improves code navigation and makes refactoring easier. In recent years I've been using TypeScript on the front end and C# on the back end to get these benefits. I wrote previously about how to do this with <a href="https://johnnyreilly.com/generate-typescript-and-csharp-clients-with-nswag">NSwag</a> and I thought it was probably worth returning to the topic. How would I do the same thing now?</p>
<p>NSwag is still great, but it produces OpenAPI 3.0 specifications. However, Microsoft have been working on their own OpenAPI tooling for .NET. The <a href="https://learn.microsoft.com/en-us/aspnet/core/fundamentals/openapi/overview?view=aspnetcore-10.0" target="_blank" rel="noopener noreferrer"><code>Microsoft.AspNetCore.OpenApi</code></a> package provides functionality to generate OpenAPI specifications from ASP.NET Core Web APIs and it supports OpenAPI 3.1. This difference turns out to be significant when it comes to handling nullability.</p>
<p>There was a change to <a href="https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0" target="_blank" rel="noopener noreferrer">how nullablity is represented in OpenAPI 3.1 compared to 3.0</a>. Whether that change is the cause or not I'm not sure, but the OpenAPI specifications produced by <code>Microsoft.AspNetCore.OpenApi</code> seem to surface nullability better than I've found with NSwag or Swashbuckle. If something is <strong>not</strong> defined as nullable in the C# model, it is <strong>not</strong> marked as nullable in the OpenAPI spec. This means that when we generate TypeScript clients from the OpenAPI spec, we get better nullability support in TypeScript too. Previously I'd find I'd do a lot of null checks or assertions in TypeScript even when the C# model didn't allow nulls. Now, with OpenAPI 3.1 and <code>Microsoft.AspNetCore.OpenApi</code>, I find that much less often.</p>
<p>The client that NSwag generates is also still very useful. But it is somewhat "heavy" in that it creates a lot of code, and it is runtime code, so it adds to my bundle size and my execution time. The alternative I'm going to show you here is to use <a href="https://openapi-ts.dev/" target="_blank" rel="noopener noreferrer">OpenAPI TypeScript / <code>openapi-ts</code></a>. This is a lightweight TypeScript client generator for OpenAPI 3.x specifications. Most of the work it does is in the form of TypeScript type definitions. Given that type definitions are erased at runtime, the resulting client code is very lightweight. It also has good support for OpenAPI 3.1.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-will-we-do">What will we do?<a href="https://johnnyreilly.com/dotnet-openapi-and-openapi-ts#what-will-we-do" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>So in this post we're going to do exactly what I did in my 2021 post, but this time using <code>Microsoft.AspNetCore.OpenApi</code> to generate the OpenAPI spec and <code>openapi-ts</code> to generate the TypeScript client.</p>
<p>We will:</p>
<ul>
<li>Create a .NET app which exposes an OpenAPI endpoint with <code>Microsoft.AspNetCore.OpenApi</code>.</li>
<li>Create a script which, when run, creates a TypeScript client with <code>openapi-ts</code>.</li>
<li>Consume the API using the generated client in a simple TypeScript application.</li>
</ul>
<p>If you're going to do this, you will need both <a href="https://nodejs.org/" target="_blank" rel="noopener noreferrer">Node.js</a> and the <a href="https://dotnet.microsoft.com/" target="_blank" rel="noopener noreferrer">.NET SDK</a> installed.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="create-an-api">Create an API<a href="https://johnnyreilly.com/dotnet-openapi-and-openapi-ts#create-an-api" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We'll now create an API which exposes an Open API endpoint:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">dotnet new webapi </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-o</span><span class="token plain"> server</span><br></span></code></pre></div></div>
<p>The above command creates a new .NET Web API project in a folder called <code>server</code>. Pretty much all the code we care about is in <code>Program.cs</code>:</p>
<div class="language-cs codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cs codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> builder </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> WebApplication</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">CreateBuilder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">args</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Add services to the container.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Learn more about configuring OpenAPI at https://aka.ms/aspnet/openapi</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">builder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Services</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">AddOpenApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> app </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> builder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Build</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Configure the HTTP request pipeline.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Environment</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">IsDevelopment</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">MapOpenApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">UseHttpsRedirection</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> summaries </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token string" style="color:rgb(165, 255, 144)">"Freezing"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Bracing"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Chilly"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Cool"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Mild"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Warm"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Balmy"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Hot"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Sweltering"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Scorching"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">MapGet</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"/weatherforecast"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> forecast </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain">  Enumerable</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Range</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token number" style="color:rgb(255, 98, 140)">1</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">5</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Select</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">index </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token constructor-invocation class-name" style="color:rgb(250, 208, 0)">WeatherForecast</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            DateOnly</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">FromDateTime</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">DateTime</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Now</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">AddDays</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">index</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            Random</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Shared</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Next</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token operator" style="color:rgb(255, 157, 0)">-</span><span class="token number" style="color:rgb(255, 98, 140)">20</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">55</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            summaries</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">Random</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Shared</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Next</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">summaries</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Length</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">ToArray</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> forecast</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">WithName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">"GetWeatherForecast"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">app</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">Run</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">record</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">WeatherForecast</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token class-name" style="color:rgb(250, 208, 0)">DateOnly</span><span class="token plain"> Date</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">int</span><span class="token plain"> TemperatureC</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">string</span><span class="token class-name punctuation" style="color:rgb(255, 255, 255)">?</span><span class="token plain"> Summary</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name keyword" style="color:rgb(255, 157, 0)">int</span><span class="token plain"> TemperatureF </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">32</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">+</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token keyword" style="color:rgb(255, 157, 0)">int</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">TemperatureC </span><span class="token operator" style="color:rgb(255, 157, 0)">/</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">0.5556</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>This is simply exposing a single endpoint, <code>/weatherforecast</code> which returns some (fake) weather data. If we run our API with:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">dotnet run </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--urls</span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token string" style="color:rgb(165, 255, 144)">"http://localhost:5000"</span><br></span></code></pre></div></div>
<p>We can then navigate to <code>http://localhost:5000/weatherforecast</code> and see the JSON output:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"date"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"2025-12-30"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureC"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">11</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"summary"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Sweltering"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureF"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">51</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"date"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"2025-12-31"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureC"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">4</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"summary"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Cool"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureF"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">39</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"date"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"2026-01-01"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureC"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">-19</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"summary"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Cool"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureF"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">-2</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"date"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"2026-01-02"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureC"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">-8</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"summary"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Warm"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureF"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">18</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"date"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"2026-01-03"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureC"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">-16</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"summary"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"Sweltering"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureF"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">4</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><br></span></code></pre></div></div>
<p>And we can see the OpenAPI endpoint at <code>http://localhost:5000/openapi/v1.json</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"openapi"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"3.1.1"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"info"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"title"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"server | v1"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"version"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"1.0.0"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"servers"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"url"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"http://localhost:5000/"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"paths"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"/weatherforecast"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"get"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token property" style="color:rgb(255, 157, 0)">"tags"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">"server"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token property" style="color:rgb(255, 157, 0)">"operationId"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"GetWeatherForecast"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token property" style="color:rgb(255, 157, 0)">"responses"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token property" style="color:rgb(255, 157, 0)">"200"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"description"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"OK"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"content"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token property" style="color:rgb(255, 157, 0)">"application/json"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token property" style="color:rgb(255, 157, 0)">"schema"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"array"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  </span><span class="token property" style="color:rgb(255, 157, 0)">"items"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                    </span><span class="token property" style="color:rgb(255, 157, 0)">"$ref"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"#/components/schemas/WeatherForecast"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"components"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"schemas"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"WeatherForecast"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token property" style="color:rgb(255, 157, 0)">"required"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">"date"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"temperatureC"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"summary"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"object"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token property" style="color:rgb(255, 157, 0)">"properties"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token property" style="color:rgb(255, 157, 0)">"date"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"string"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"format"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"date"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureC"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"pattern"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^-?(?:0|[1-9]\\d*)$"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">"integer"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"string"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"format"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int32"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token property" style="color:rgb(255, 157, 0)">"summary"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">"null"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"string"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token property" style="color:rgb(255, 157, 0)">"temperatureF"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"pattern"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^-?(?:0|[1-9]\\d*)$"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"type"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">"integer"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"string"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token property" style="color:rgb(255, 157, 0)">"format"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int32"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"tags"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token property" style="color:rgb(255, 157, 0)">"name"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"server"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>This is great! (Actually, there's some problems with the <code>temperatureC</code> and <code>temperatureF</code> properties being marked as both <code>integer</code> and <code>string</code> but we'll ignore that for now.)</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="create-our-client">Create our client<a href="https://johnnyreilly.com/dotnet-openapi-and-openapi-ts#create-our-client" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We'll now create a web app with which to consume our API:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> create vite@latest client -- </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">--template</span><span class="token plain"> react-ts</span><br></span></code></pre></div></div>
<p>This creates a React + TypeScript app in a folder called <code>client</code>. We'll now follow the <a href="https://openapi-ts.dev/introduction#setup" target="_blank" rel="noopener noreferrer"><code>openapi-ts</code> setup</a> instructions to add <code>openapi-ts</code> to our project:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token builtin class-name" style="color:rgb(250, 208, 0)">cd</span><span class="token plain"> client</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> i </span><span class="token parameter variable" style="color:rgb(255, 238, 128)">-D</span><span class="token plain"> openapi-typescript typescript</span><br></span></code></pre></div></div>
<p>And we'll update the <code>tsconfig.app.json</code> to include the recommended settings:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"compilerOptions"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"noUncheckedIndexedAccess"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>To make local development easier, we'll also add a proxy to our <code>vite.config.ts</code> so that API request is proxied to our .NET API:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> defineConfig </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'vite'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> react </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'@vitejs/plugin-react'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// https://vite.dev/config/</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">defineConfig</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  plugins</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token function" style="color:rgb(250, 208, 0)">react</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  server</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    proxy</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'/weatherforecast'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        target</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'http://127.0.0.1:5000'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        changeOrigin</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        autoRewrite</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token boolean" style="color:rgb(255, 98, 140)">true</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>Now we no longer need to deal with CORS during development, and our local development setup more closely resembles production. Incidentally, we could put all our API requests behind the proxy if we wanted to by using a standard prefix like <code>/api</code>, but for this demo we'll just proxy the one endpoint.</p>
<p>We have a front end app ready to consume our API. But we need to generate an OpenAPI client first.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="generate-our-openapi-client">Generate our OpenAPI client<a href="https://johnnyreilly.com/dotnet-openapi-and-openapi-ts#generate-our-openapi-client" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>We'll add an <code>npm script</code> to our <code>package.json</code> in the <code>client</code> folder to generate our OpenAPI client using <code>openapi-ts</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"scripts"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// ... other scripts ...</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"generate-client"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"openapi-typescript http://localhost:5000/openapi/v1.json --output src/GeneratedClient.ts --root-types --root-types-no-schema-prefix"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>This, when run, will generate a TypeScript client in <code>src/GeneratedClient.ts</code> based on the OpenAPI spec exposed by our .NET API. It will also include the "root types" so we can import them in our code easily. To generate the client, we need to ensure our API is running. So we'll jump back up to the root of our .NET / React project and we'll add a <code>package.json</code>. We'll add the following two dependencies:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">install</span><span class="token plain"> --save-dev start-server-and-test concurrently</span><br></span></code></pre></div></div>
<p>Then we'll add scripts to handle running client and server together, and to generate the client:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"name"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"openapi-ts-test"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"version"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"1.0.0"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"license"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"ISC"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"scripts"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"start"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"concurrently -n \"FE,BE\" -c \"bgBlue.bold,bgMagenta.bold\" \"npm run dev:client\" \"npm run dev:server\""</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"dev:client"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"cd client &amp;&amp; npm run dev"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"dev:server"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"cd server &amp;&amp; dotnet run --urls=\"http://localhost:5000\""</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"generate-client"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"start-server-and-test dev:server http-get://localhost:5000/openapi/v1.json generate-client:make"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"generate-client:make"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"cd client &amp;&amp; npm run generate-client"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token property" style="color:rgb(255, 157, 0)">"devDependencies"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"concurrently"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^9.2.1"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token property" style="color:rgb(255, 157, 0)">"start-server-and-test"</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"^2.1.3"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>Running <code>npm run generate-client</code> in the root of our project will now:</p>
<ul>
<li>Start the server API on <code>http://localhost:5000</code></li>
<li>Wait for the OpenAPI endpoint to be available using <a href="https://www.npmjs.com/package/start-server-and-test" target="_blank" rel="noopener noreferrer"><code>start-server-and-test</code></a></li>
<li>Run the <code>generate-client</code> script in the <code>client</code> folder to generate the TypeScript client.</li>
</ul>
<p>Here's what our generated client looks like:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/**</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic"> * This file was auto-generated by openapi-typescript.</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic"> * Do not make direct changes to the file.</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic"> */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">paths</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'/weatherforecast'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    parameters</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      query</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      header</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      path</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      cookie</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    get</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> operations</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'GetWeatherForecast'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    put</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    post</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">delete</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    options</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    head</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    patch</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    trace</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">webhooks</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> Record</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">components</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  schemas</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    WeatherForecast</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** Format: date */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      date</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** Format: int32 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      temperatureC</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      summary</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** Format: int32 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      temperatureF</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  responses</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  parameters</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  requestBodies</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  headers</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  pathItems</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">WeatherForecast</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> components</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'schemas'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'WeatherForecast'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">$defs</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> Record</span><span class="token operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">interface</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">operations</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  GetWeatherForecast</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    parameters</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      query</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      header</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      path</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      cookie</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    requestBody</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">never</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    responses</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** </span><span class="token doc-comment comment keyword" style="color:rgb(255, 157, 0);font-style:italic">@description</span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic"> OK */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token number" style="color:rgb(255, 98, 140)">200</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        headers</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">name</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">unknown</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        content</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token string-property property" style="color:rgb(255, 157, 0)">'application/json'</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> components</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'schemas'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'WeatherForecast'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>You can see our <code>/weatherforecast</code> endpoint is represented in the <code>paths</code> section and the <code>WeatherForecast</code> model is represented in the <code>components.schemas</code> section.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="adjusting-microsoftaspnetcoreopenapi-surfaced-types">Adjusting <code>Microsoft.AspNetCore.OpenApi</code> surfaced types<a href="https://johnnyreilly.com/dotnet-openapi-and-openapi-ts#adjusting-microsoftaspnetcoreopenapi-surfaced-types" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I mentioned earlier that the <code>temperatureC</code> and <code>temperatureF</code> properties were marked as both <code>integer</code> and <code>string</code> in the OpenAPI spec. This is because <code>Microsoft.AspNetCore.OpenApi</code> is being ... interesting ... about number types. If we look at the types created in our client we see:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">    WeatherForecast</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** Format: date */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      date</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** Format: int32 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      temperatureC</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      summary</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** Format: int32 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      temperatureF</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>Note how <code>temperatureC</code> and <code>temperatureF</code> are both <code>number | string</code>. This isn't what we're after; we want them to be just <code>number</code> to reflect the C# <code>int</code> model. To fix this, we can create 2 <code>IOpenApiSchemaTransformer</code> implementations to fix up the <code>number | string</code> types to just <code>number</code> types. One to handle integer style numbers (<code>IntegerSchemaTransformer</code>) and one to handle numbers with decimal places (<code>NumberSchemaTransformer</code>).</p>
<div class="language-cs codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cs codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">Microsoft</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">AspNetCore</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">OpenApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">using</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">Microsoft</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">OpenApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">namespace</span><span class="token plain"> </span><span class="token namespace" style="color:rgb(255, 157, 0)">Server</span><span class="token namespace punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token namespace" style="color:rgb(255, 157, 0)">OpenApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// Transforms OpenAPI schema for integer types to ensure they are represented</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// with proper type and format, removing unwanted pattern and string type alternatives.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// This affects integer types like int, long, short, etc.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">sealed</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">IntegerSchemaTransformer</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token type-list class-name" style="color:rgb(250, 208, 0)">IOpenApiSchemaTransformer</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">Task</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">TransformAsync</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">OpenApiSchema</span><span class="token plain"> schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">OpenApiSchemaTransformerContext</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">CancellationToken</span><span class="token plain"> cancellationToken</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> type </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">JsonTypeInfo</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Handle nullable integers</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> Nullable</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">GetUnderlyingType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Check if this is an integer type</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">int</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">long</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">short</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">byte</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">sbyte</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">uint</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">ulong</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">ushort</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Set type to integer only (not ["integer", "string"])</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Type </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> JsonSchemaType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Integer</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Clear any pattern that might have been added</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Pattern </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Set appropriate format based on the actual type</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Format </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> actualType </span><span class="token keyword" style="color:rgb(255, 157, 0)">switch</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// based on https://spec.openapis.org/oas/v3.1.1.html#data-types</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">int</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int32"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">uint</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int32"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">long</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int64"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">ulong</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int64"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">short</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int32"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">ushort</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int32"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">byte</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int32"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">sbyte</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int32"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                _ </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"int32"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Clear any enum values that might have been set</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Enum</span><span class="token punctuation" style="color:rgb(255, 255, 255)">?.</span><span class="token function" style="color:rgb(250, 208, 0)">Clear</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> Task</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">CompletedTask</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// Transforms OpenAPI schema for number types to ensure they are represented</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// with proper type and format, removing unwanted pattern and string type alternatives.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// This affects floating-point types like double, float, and decimal.</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">/// &lt;/summary&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">sealed</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">class</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">NumberSchemaTransformer</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token type-list class-name" style="color:rgb(250, 208, 0)">IOpenApiSchemaTransformer</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">public</span><span class="token plain"> </span><span class="token return-type class-name" style="color:rgb(250, 208, 0)">Task</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">TransformAsync</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">OpenApiSchema</span><span class="token plain"> schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">OpenApiSchemaTransformerContext</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name" style="color:rgb(250, 208, 0)">CancellationToken</span><span class="token plain"> cancellationToken</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> type </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> context</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">JsonTypeInfo</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Handle nullable numbers</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token class-name keyword" style="color:rgb(255, 157, 0)">var</span><span class="token plain"> actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> Nullable</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">GetUnderlyingType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">??</span><span class="token plain"> type</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Check if this is an integer type</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">double</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">decimal</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">||</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            actualType </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">float</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Set type to integer only (not ["number", "string"])</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Type </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> JsonSchemaType</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Number</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Clear any pattern that might have been added</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Pattern </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Set appropriate format based on the actual type</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Format </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> actualType </span><span class="token keyword" style="color:rgb(255, 157, 0)">switch</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// based on https://spec.openapis.org/oas/v3.1.1.html#data-types</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">double</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"double"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">decimal</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"double"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token class-name" style="color:rgb(250, 208, 0)">Type</span><span class="token plain"> t </span><span class="token keyword" style="color:rgb(255, 157, 0)">when</span><span class="token plain"> t </span><span class="token operator" style="color:rgb(255, 157, 0)">==</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">typeof</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token type-expression class-name keyword" style="color:rgb(255, 157, 0)">float</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"float"</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                _ </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">"double"</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// Clear any enum values that might have been set</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            schema</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Enum</span><span class="token punctuation" style="color:rgb(255, 255, 255)">?.</span><span class="token function" style="color:rgb(250, 208, 0)">Clear</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> Task</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">CompletedTask</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><br></span></code></pre></div></div>
<p>And the <code>Program.cs</code> is updated to register these transformers:</p>
<div class="language-cs codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-cs codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">builder</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token plain">Services</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token function" style="color:rgb(250, 208, 0)">AddOpenApi</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">options </span><span class="token operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    options</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token generic-method function" style="color:rgb(250, 208, 0)">AddSchemaTransformer</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token generic-method generic class-name" style="color:rgb(250, 208, 0)">Server</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token generic-method generic class-name" style="color:rgb(250, 208, 0)">OpenApi</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token generic-method generic class-name" style="color:rgb(250, 208, 0)">IntegerSchemaTransformer</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    options</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token generic-method function" style="color:rgb(250, 208, 0)">AddSchemaTransformer</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token generic-method generic class-name" style="color:rgb(250, 208, 0)">Server</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token generic-method generic class-name" style="color:rgb(250, 208, 0)">OpenApi</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token generic-method generic class-name" style="color:rgb(250, 208, 0)">NumberSchemaTransformer</span><span class="token generic-method generic class-name punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>With this in place, when we next run <code>npm run generate-client</code> from the root of our project, we find that our generated client now has the correct types for <code>temperatureC</code> and <code>temperatureF</code>:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token plain">    WeatherForecast</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** Format: date */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      date</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** Format: int32 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      temperatureC</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      summary</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">null</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">|</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">string</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token doc-comment comment" style="color:rgb(179, 98, 255);font-style:italic">/** Format: int32 */</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      temperatureF</span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token builtin" style="color:rgb(250, 208, 0)">number</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p><a href="https://github.com/dotnet/aspnetcore/issues/64920" target="_blank" rel="noopener noreferrer">I've inquired whether the default behaviour makes the most sense here.</a></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="consume-our-generated-api-client">Consume our generated API client<a href="https://johnnyreilly.com/dotnet-openapi-and-openapi-ts#consume-our-generated-api-client" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Now we want to make use of our generated client in our React app. First we're going to install <code>openapi-fetch</code> to help with making requests:</p>
<div class="language-shell codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-shell codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token function" style="color:rgb(250, 208, 0)">npm</span><span class="token plain"> i openapi-fetch</span><br></span></code></pre></div></div>
<p>(A quick note, <code>openapi-fetch</code> is not strictly necessary here, but it makes things easier. It provides a fetch-based HTTP client which works well with <code>openapi-ts</code> generated clients. It's worth saying that there are plans to deprecate <a href="https://github.com/openapi-ts/openapi-typescript/discussions/2559" target="_blank" rel="noopener noreferrer"><code>openapi-fetch</code> which you can read about here</a>. As of right now though, it's still a useful library to use alongside <code>openapi-ts</code>.)</p>
<p>Now let's start our client and server with <code>npm run start</code>. We'll then replace the contents of <code>App.tsx</code> with:</p>
<div class="language-tsx codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-tsx codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token imports"> useEffect</span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token imports"> useState </span><span class="token imports punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'react'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'./App.css'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token imports">createClient</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'openapi-fetch'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">import</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">type</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> paths</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token maybe-class-name">WeatherForecast</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">from</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'./GeneratedClient'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"> </span><span class="token comment" style="color:rgb(179, 98, 255);font-style:italic">// generated by openapi-typescript</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> client </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token generic-function function" style="color:rgb(250, 208, 0)">createClient</span><span class="token generic-function generic class-name operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token generic-function generic class-name" style="color:rgb(250, 208, 0)">paths</span><span class="token generic-function generic class-name operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">App</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">weather</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> setWeather</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token generic-function function" style="color:rgb(250, 208, 0)">useState</span><span class="token generic-function generic class-name operator" style="color:rgb(255, 157, 0)">&lt;</span><span class="token generic-function generic class-name" style="color:rgb(250, 208, 0)">WeatherForecast</span><span class="token generic-function generic class-name punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token generic-function generic class-name punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token generic-function generic class-name" style="color:rgb(250, 208, 0)"> </span><span class="token generic-function generic class-name operator" style="color:rgb(255, 157, 0)">|</span><span class="token generic-function generic class-name" style="color:rgb(250, 208, 0)"> </span><span class="token generic-function generic class-name keyword" style="color:rgb(255, 157, 0)">null</span><span class="token generic-function generic class-name operator" style="color:rgb(255, 157, 0)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token function" style="color:rgb(250, 208, 0)">useEffect</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token keyword" style="color:rgb(255, 157, 0)">async</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">function</span><span class="token plain"> </span><span class="token function" style="color:rgb(250, 208, 0)">loadWeather</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">const</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> data</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> error </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">=</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">await</span><span class="token plain"> client</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token constant" style="color:rgb(250, 208, 0)">GET</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'/weatherforecast'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token function" style="color:rgb(250, 208, 0)">setWeather</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">data</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">else</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">if</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token console class-name" style="color:rgb(250, 208, 0)">console</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token string" style="color:rgb(165, 255, 144)">'Failed to load weather:'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> error</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token function" style="color:rgb(250, 208, 0)">loadWeather</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token plain">setWeather</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token keyword" style="color:rgb(255, 157, 0)">return</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">div</span><span class="token tag" style="color:rgb(255, 157, 0)"> </span><span class="token tag attr-name" style="color:rgb(255, 180, 84)">className</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(255, 255, 255)">=</span><span class="token tag attr-value punctuation" style="color:rgb(255, 255, 255)">"</span><span class="token tag attr-value" style="color:rgb(255, 157, 0)">App</span><span class="token tag attr-value punctuation" style="color:rgb(255, 255, 255)">"</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">header</span><span class="token tag" style="color:rgb(255, 157, 0)"> </span><span class="token tag attr-name" style="color:rgb(255, 180, 84)">className</span><span class="token tag attr-value punctuation attr-equals" style="color:rgb(255, 255, 255)">=</span><span class="token tag attr-value punctuation" style="color:rgb(255, 255, 255)">"</span><span class="token tag attr-value" style="color:rgb(255, 157, 0)">App-header</span><span class="token tag attr-value punctuation" style="color:rgb(255, 255, 255)">"</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">weather </span><span class="token operator" style="color:rgb(255, 157, 0)">?</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">table</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">thead</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">              </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">tr</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">                </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">th</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">Date</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">th</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">                </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">th</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">Summary</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">th</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">                </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">th</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">Centigrade</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">th</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">                </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">th</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">Fahrenheit</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">th</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">              </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">tr</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">thead</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">tbody</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">weather</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">map</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> date</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> summary</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> temperatureC</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> temperatureF </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token arrow operator" style="color:rgb(255, 157, 0)">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">                </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">tr</span><span class="token tag" style="color:rgb(255, 157, 0)"> </span><span class="token tag attr-name" style="color:rgb(255, 180, 84)">key</span><span class="token tag script language-javascript script-punctuation punctuation" style="color:rgb(255, 255, 255)">=</span><span class="token tag script language-javascript punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token tag script language-javascript" style="color:rgb(255, 157, 0)">date</span><span class="token tag script language-javascript punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">                  </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">td</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token keyword" style="color:rgb(255, 157, 0)">new</span><span class="token plain"> </span><span class="token class-name" style="color:rgb(250, 208, 0)">Date</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain">date</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">.</span><span class="token method function property-access" style="color:rgb(250, 208, 0)">toLocaleDateString</span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">td</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">                  </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">td</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">summary</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">td</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">                  </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">td</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">temperatureC</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">td</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">                  </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">td</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain">temperatureF</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">td</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">                </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">tr</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">            </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">tbody</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">          </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">table</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token plain"> </span><span class="token operator" style="color:rgb(255, 157, 0)">:</span><span class="token plain"> </span><span class="token punctuation" style="color:rgb(255, 255, 255)">(</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;</span><span class="token tag" style="color:rgb(255, 157, 0)">p</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text">Loading weather...</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">p</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">      </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">header</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain-text"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain-text">    </span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&lt;/</span><span class="token tag" style="color:rgb(255, 157, 0)">div</span><span class="token tag punctuation" style="color:rgb(255, 255, 255)">&gt;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">)</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token keyword" style="color:rgb(255, 157, 0)">export</span><span class="token plain"> </span><span class="token keyword" style="color:rgb(255, 157, 0)">default</span><span class="token plain"> </span><span class="token maybe-class-name">App</span><span class="token punctuation" style="color:rgb(255, 255, 255)">;</span><br></span></code></pre></div></div>
<p>Let's break down what's happening here:</p>
<ul>
<li>We import the generated types from <code>GeneratedClient.ts</code></li>
<li>We create an <code>openapi-fetch</code> client using those types.</li>
<li>In a <code>useEffect</code> hook, we call the <code>/weatherforecast</code> endpoint using the generated client.</li>
</ul>
<p>From a users perspective, when we run the app we see: (I've reused the GIF from my previous post here as the experience is the same.)</p>
<p><img decoding="async" loading="eager" alt="load data from server" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/use-generated-client-68121118d8862aee3faf83860aeb6de9.gif" width="600" height="354" fetchpriority="high" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="summary">Summary<a href="https://johnnyreilly.com/dotnet-openapi-and-openapi-ts#summary" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>In this post we've seen how to create a .NET Web API which exposes an OpenAPI endpoint using <code>Microsoft.AspNetCore.OpenApi</code>. We've then seen how to generate a TypeScript client from that OpenAPI spec using <code>openapi-ts</code>. Finally, we've seen how to consume that generated client in a React + TypeScript application.</p>
<p>What's significant here is that we have static typing all the way from back end to front end. The C# models we defined in our .NET API are represented in the OpenAPI spec, and those same models are represented in TypeScript types in our front end application. This means that if we change a model on the back end, we can regenerate the TypeScript client and get type safety on the front end too. I'm using C#, but you could be using something else entirely on the back end, as long as it can produce an OpenAPI spec.</p>
<p>There was a little adjustment needed to get the number types working correctly, but overall this was a pretty straightforward process. If you're building full stack applications with TypeScript on the front end and .NET on the back end, I recommend giving this approach a try!</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Swagger" term="Swagger"/>
        <category label="C#" term="C#"/>
        <category label="Azure" term="Azure"/>
        <category label="TypeScript" term="TypeScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[npmx.dev: with a little help from my friends]]></title>
        <id>https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends</id>
        <link href="https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends"/>
        <updated>2026-03-01T17:16:48.000Z</updated>
        <summary type="html"><![CDATA[This post will show you how to contribute to npmx.dev and share my experience with the project.]]></summary>
        <content type="html"><![CDATA[<p>npmx.dev is an exciting new reimagining of the npmjs.com website. Did I mention it's new? It's new! You can read the alpha blog post announcing the release here: <a href="https://npmx.dev/blog/alpha-release" target="_blank" rel="noopener noreferrer">https://npmx.dev/blog/alpha-release</a></p>
<p>I write this post not as one of the most significant contributors to npmx.dev. I barely rank. At the point of writing I've submitted two PRs, one of which was merged, and one was not (for reasons I entirely agree with).</p>
<p>I'm writing as I'm excited by npmx; I really want it to succeed. So I thought I'd share my own mini story of npmx. If you walk away from this post with one thought I hope it's this: "npmx is a welcoming community, doing good work and very open to contributions, however minor".</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;npmx.dev: with a little help from my friends&amp;quot; with a screenshot of the npmx.dev website" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-2d2d0d3bbe9f79c11268fbcec0efd7ae.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="genesis">Genesis<a href="https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends#genesis" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I saw <a href="https://bsky.app/profile/danielroe.dev/post/3md47mg7qjs22" target="_blank" rel="noopener noreferrer">Daniel Roe's post on Bluesky in January</a> and I thought "huh, that's interesting!":</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of Daniel Roe&amp;#39;s post on Bluesky" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-daniel-roe-bluesky-7cf6e3000b2700d7de1cc26e5c04e3a7.png" width="612" height="524" class="img_ev3q"></p>
<p>I'd long felt that the npm website might generously be described as "adequate". It works, sure. But it does not spark joy. The last time I could remember a feature being added to the npm website was the addition of a "DT" badge to packages which had TypeScript type definitions available via Definitely Typed.</p>
<p><a href="https://www.npmjs.com/package/lodash" target="_blank" rel="noopener noreferrer"><img decoding="async" loading="lazy" alt="Screenshot of lodash with DT badge" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-lodash-npm-9959675ca40da71ba9ac99eb0192a417.webp" width="151" height="77" class="img_ev3q"></a></p>
<p>That was added a long time ago, and possibly by <a href="https://github.com/orta" target="_blank" rel="noopener noreferrer">Orta Therox</a>. (During his time with the TypeScript compiler team if my memory serves me right.) The point is, npmjs.com is in no way under active development.</p>
<p>Daniel's post seemed really punk rock. "I'll do it myself!"</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-happened-nuxt">What happened Nuxt?<a href="https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends#what-happened-nuxt" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Little pun there 😅. Given Daniel is the lead maintainer of Nuxt, it entirely made sense he would use that for the npm browser registry.</p>
<p>I was interested, but I wasn't sure whether I'd make any contributions myself; given my web background is mostly React. Also, life was and is quite full in other ways.</p>
<p>But before I knew what had happened, there was already this <a href="https://npmx.dev/" target="_blank" rel="noopener noreferrer">npmx.dev</a> website in existence. It was new, shiny and impressive. Many people were <a href="https://github.com/npmx-dev/npmx.dev/graphs/contributors" target="_blank" rel="noopener noreferrer">actively contributing to the codebase day by day</a>:</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of the contributors graph for npmx.dev" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-contributors-c7c23565dcdb31c9fa309e32a03a9ec7.png" width="693" height="872" class="img_ev3q"></p>
<p>I starred the repo in GitHub and allowed the flood of notifications to flow into my inbox. As a matter of course, I do this rarely. Generally there's too much noise to have notifications on. But I was interested in seeing if I might pick something up through the osmosis. Similarly I joined the (very active) Discord.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="a-bug">A bug?<a href="https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends#a-bug" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>I'd expected my contributions to npmx to be limited to a bit of testing. So I thought I'd do some testing. I looked up a project I work on called <code>ts-loader</code> and was surprised to discover it was missing from npmx.</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of ts-loader missing from npmx" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-where-is-ts-loader-1922ec02909fdea48bc503df95be323e.webp" width="1080" height="1007" class="img_ev3q"></p>
<p>I found myself raising <a href="https://github.com/npmx-dev/npmx.dev/issues/1174" target="_blank" rel="noopener noreferrer">an issue</a>, puzzled at the absence:</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of GitHub issue" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-github-issue-a474af1ea134a8ead1daf8e034e633bf.png" width="957" height="311" class="img_ev3q"></p>
<p>It turned out that I was running into npm rate limiting API requests. So when I was typing in "ts-loader", behind the scenes API requests were firing at npmjs.com, and at a point the server decided to say "you've had enough!" and returning 429 Too Many Requests responses.</p>
<p>But the npmx.dev website didn't reflect that. It rather suggested that the package didn't exist. This niggled at me. And one fine Saturday morning I decided to see if I could have a go working on this.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="contributing">Contributing<a href="https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends#contributing" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>The repo for npmx.dev has a very fine <a href="https://github.com/npmx-dev/npmx.dev/blob/main/CONTRIBUTING.md" target="_blank" rel="noopener noreferrer"><code>CONTRIBUTING.md</code></a>. By following that I was quickly able to get a working version of the website on my machine.</p>
<p>I had two ideas of potential fixes. First idea: debounce the input box people type into.</p>
<p>As I've mentioned, I don't know Vue / Nuxt. I know TypeScript, but not the frameworks. So I fired up Claude Code, told it what I wanted to do and it wrote the code for idea 1. It didn't work. The implementation did; we had debounced successfully. But that was not sufficient to stop 429s from showing up.</p>
<p>So I reverted that. I blooming love Git.</p>
<p>Idea number two was more straightforward: when a 429 happens make the UI simply say "you've been rate limited - give it a moment".</p>
<p>For the second time I gave Claude Code his marching orders. (Sidebar: is Claude a "he"? Probably. Claude sounds like "a gent" 😅. Sorry.)</p>
<p>This time the approach worked. When I typed into the input box and 429s happened, I was presented with something like this:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of the rate limit message in Google Chrome with 429s visible" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-after-changes-1f0f086a5e5531724cf2b9f9bec3cd37.webp" width="1849" height="822" class="img_ev3q"></p>
<p>Beautiful right?</p>
<p>When I looked at the code produced, it seemed plausible. It seemed to reflect the idioms of the codebase as best I could tell. And crucially, it worked.</p>
<p>The <a href="https://github.com/npmx-dev/npmx.dev/blob/main/CONTRIBUTING.md#using-ai" target="_blank" rel="noopener noreferrer">CONTRIBUTING.md specifically calls out using AI</a>. Let me quote the guidance in full as I think it is excellent:</p>
<blockquote>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="using-ai">Using AI<a href="https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends#using-ai" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>You're welcome to use AI tools to help you contribute. But there are two important ground rules:</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="1-never-let-an-llm-speak-for-you">1. Never let an LLM speak for you<a href="https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends#1-never-let-an-llm-speak-for-you" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>When you write a comment, issue, or PR description, use your own words. Grammar and spelling don't matter – real connection does. AI-generated summaries tend to be long-winded, dense, and often inaccurate. Simplicity is an art. The goal is not to sound impressive, but to communicate clearly.</p>
<h3 class="anchor anchorWithStickyNavbar_LWe7" id="2-never-let-an-llm-think-for-you">2. Never let an LLM think for you<a href="https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends#2-never-let-an-llm-think-for-you" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h3>
<p>Feel free to use AI to write code, tests, or point you in the right direction. But always understand what it's written before contributing it. Take personal responsibility for your contributions. Don't say "ChatGPT says..." – tell us what <em>you</em> think.</p>
<p>For more context, see <a href="https://roe.dev/blog/using-ai-in-open-source" target="_blank" rel="noopener noreferrer">Using AI in open source</a>.</p>
</blockquote>
<p>I made sure that my usage of AI for this change was above board. I looked at what code I'd ended up with and learned a bit about how Vue and Nuxt work. It was PR time: <a href="https://github.com/npmx-dev/npmx.dev/pull/1200" target="_blank" rel="noopener noreferrer">https://github.com/npmx-dev/npmx.dev/pull/1200</a></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="you-can-contribute-too">You can contribute too!<a href="https://johnnyreilly.com/npmx-with-a-little-help-from-my-friends#you-can-contribute-too" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Some PRs have a lot of back and forth. This one just landed. Which was nice!</p>
<p>Ironically, by the time you read this the original issue I raised, and the fix I provided, is I think generally no longer relevant. The npmx.dev website is now not just querying npmjs.com for package information, so the likelihood of running into npm rate limits is much lower. But that's the nature of development. You fix one thing, and then the world changes and that thing is no longer an issue. But that's fine.</p>
<p>I'm really happy npmx.dev exists and I've a good feeling about it. If you're thinking about something in npmx.dev that you might be able to improve, you should have a crack. It's a wonderful community; get involved!</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="JavaScript" term="JavaScript"/>
    </entry>
    <entry>
        <title type="html"><![CDATA[Azure Artifacts: Publish a private npm package with Azure DevOps]]></title>
        <id>https://johnnyreilly.com/azure-artifacts-publish-private-npm-package-with-azure-devops</id>
        <link href="https://johnnyreilly.com/azure-artifacts-publish-private-npm-package-with-azure-devops"/>
        <updated>2024-12-01T09:45:10.000Z</updated>
        <summary type="html"><![CDATA[Azure DevOps has a feature called Azure Artifacts that supports publishing npm packages to a feed for consumption. This post shows how to publish a private npm package with Azure DevOps.]]></summary>
        <content type="html"><![CDATA[<p>Azure DevOps has a feature called Azure Artifacts that supports publishing npm packages to a feed for consumption. Publishing a private npm package with Azure DevOps is a common scenario for teams that want to share code across projects or organizations. This post shows how to publish a private npm package with Azure DevOps.</p>
<p><img decoding="async" loading="eager" alt="title image reading &amp;quot;Azure Artifacts: Publish a private npm package with Azure DevOps&amp;quot; with an Azure DevOps and npm logos" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/title-image-f41c20b738c408ad754258ede7571c6f.png" width="800" height="450" fetchpriority="high" class="img_ev3q"></p>
<p>Publishing a private npm package with Azure DevOps is fairly straightforward, but surprisingly documentation is a little sparse.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="what-feeds-are-available-in-azure-artifacts">What feeds are available in Azure Artifacts?<a href="https://johnnyreilly.com/azure-artifacts-publish-private-npm-package-with-azure-devops#what-feeds-are-available-in-azure-artifacts" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>If you don't already have a feed to publish your npm package to, you can create one in Azure DevOps <a href="https://learn.microsoft.com/en-us/azure/devops/artifacts/concepts/feeds?view=azure-devops" target="_blank" rel="noopener noreferrer">by following these instructions</a>.</p>
<p>If you're trying to find out what feeds are available in Azure Artifacts, you can find them in the Azure DevOps UI. Go to the Artifacts section in Azure DevOps and you'll see a list of feeds. The URL for the feed will be in the format <code>https://dev.azure.com/[ORGANIZATION]/_artifacts/feed</code>.</p>
<p>There you'll see a dropdown with the feeds you have access to:</p>
<p><img decoding="async" loading="lazy" alt="screenshot of the feeds in Azure DevOps" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-npm-feeds-in-azure-artifacts-b561f2cc09bb8358baf4078561fc4c06.webp" width="1248" height="690" class="img_ev3q"></p>
<p>You'll see from the screenshot that I have access to a feed called <code>npmrc-script-organization</code>. Let's use that feed to publish a private npm package.</p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="setting-up-the-npmrc-file">Setting up the <code>.npmrc</code> file<a href="https://johnnyreilly.com/azure-artifacts-publish-private-npm-package-with-azure-devops#setting-up-the-npmrc-file" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>So that you can publish to a private feed, you need to set up an <code>.npmrc</code> file in your project. This file will contain the URL of the feed you want to publish to, and your credentials. To set up the <code>.npmrc</code> file, you can click on the "Connect to Feed" button in the Azure DevOps UI:</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of &amp;quot;connect to feed&amp;quot; in Azure DevOps" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-connect-to-feed-9420465335c838a5365e11888ea36437.webp" width="1016" height="118" class="img_ev3q"></p>
<p>Then select <code>npm</code> and you'll see the instructions for setting up the <code>.npmrc</code> file:</p>
<p><img decoding="async" loading="lazy" alt="Screenshot of the instructions for setting up the .npmrc file" src="https://res.cloudinary.com/priou/image/fetch/f_auto,q_auto,w_auto,dpr_auto/https://johnnyreilly.com/assets/images/screenshot-npmrc-e8b46fa648b27148f109ea8fbf6ba807.png" width="2240" height="678" class="img_ev3q"></p>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="publishing-with-azure-pipelines">Publishing with Azure Pipelines<a href="https://johnnyreilly.com/azure-artifacts-publish-private-npm-package-with-azure-devops#publishing-with-azure-pipelines" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>Now we're ready to publish our npm package with Azure DevOps. Here's an example of an Azure Pipelines YAML file that publishes a private npm package:</p>
<div class="language-yml codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#9EFEFF;--prism-background-color:#2D2A55"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-yml codeBlock_bY9V thin-scrollbar" style="color:#9EFEFF;background-color:#2D2A55"><code class="codeBlockLines_e6Vv"><span class="token-line" style="color:#9EFEFF"><span class="token key atrule">trigger</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">batch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token boolean important" style="color:rgb(255, 98, 140)">true</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">pool</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">vmImage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> ubuntu</span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain">latest</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">variables</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token key atrule">isMainBranch</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token punctuation" style="color:rgb(255, 255, 255)">{</span><span class="token plain"> eq(variables</span><span class="token punctuation" style="color:rgb(255, 255, 255)">[</span><span class="token string" style="color:rgb(165, 255, 144)">'Build.SourceBranch'</span><span class="token punctuation" style="color:rgb(255, 255, 255)">]</span><span class="token punctuation" style="color:rgb(255, 255, 255)">,</span><span class="token plain"> 'refs/heads/main') </span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token punctuation" style="color:rgb(255, 255, 255)">}</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain"></span><span class="token key atrule">stages</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">  </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">stage</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Build_Package_Publish</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Build package and publish</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">    </span><span class="token key atrule">jobs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">      </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">job</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">        </span><span class="token key atrule">steps</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">task</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> NodeTool@0</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token key atrule">versionSpec</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token number" style="color:rgb(255, 98, 140)">20</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Install Node.js</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">task</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> npmAuthenticate@0</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token key atrule">workingFile</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> $(System.DefaultWorkingDirectory)/.npmrc</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">bash</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> npm install</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'npm install'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">bash</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> npm run build</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'npm build'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain" style="display:inline-block"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">          </span><span class="token punctuation" style="color:rgb(255, 255, 255)">-</span><span class="token plain"> </span><span class="token key atrule">task</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Npm@1</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">displayName</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> Publish Package</span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">            </span><span class="token key atrule">inputs</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token key atrule">command</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'publish'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token key atrule">publishRegistry</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'useFeed'</span><span class="token plain"></span><br></span><span class="token-line" style="color:#9EFEFF"><span class="token plain">              </span><span class="token key atrule">publishFeed</span><span class="token punctuation" style="color:rgb(255, 255, 255)">:</span><span class="token plain"> </span><span class="token string" style="color:rgb(165, 255, 144)">'npmrc-script-organization'</span><br></span></code></pre></div></div>
<p>Let's break down the steps in this YAML file:</p>
<ul>
<li>We're installing Node.js and authenticating with the <code>.npmrc</code> file.</li>
<li>We're running <code>npm install</code> and <code>npm run build</code>. These are standard steps for building a Node.js project; yours might vary; what's important is that you are able to get your built package set up.</li>
<li>Finally, we use the <code>Npm@1</code> task to publish the package. We specify the <code>publishRegistry</code> as <code>useFeed</code> and the <code>publishFeed</code> as <code>npmrc-script-organization</code>. This is the feed we're publishing to.</li>
</ul>
<h2 class="anchor anchorWithStickyNavbar_LWe7" id="conclusion">Conclusion<a href="https://johnnyreilly.com/azure-artifacts-publish-private-npm-package-with-azure-devops#conclusion" class="hash-link" aria-label="Direct link to heading" title="Direct link to heading">​</a></h2>
<p>In this post, we've seen how to publish a private npm package with Azure DevOps. We've set up the <code>.npmrc</code> file, and we've used an Azure Pipelines YAML file to publish the package. This is a common scenario for teams that want to share code across projects or organizations. I hope this post has been helpful to you!</p>]]></content>
        <author>
            <name>John Reilly</name>
            <uri>https://johnnyreilly.com/about</uri>
        </author>
        <category label="Azure DevOps" term="Azure DevOps"/>
        <category label="Node.js" term="Node.js"/>
    </entry>
</feed>