<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Nitin Kumar Yadav</title>
    <description>The latest articles on DEV Community by Nitin Kumar Yadav (@nitinkumaryadav1307).</description>
    <link>https://dev.to/nitinkumaryadav1307</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3728348%2Fc4d0b99f-ce87-4b39-88ec-7254012820f5.jpeg</url>
      <title>DEV Community: Nitin Kumar Yadav</title>
      <link>https://dev.to/nitinkumaryadav1307</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/nitinkumaryadav1307"/>
    <language>en</language>
    <item>
      <title>I Spent 3 Days Building a CLI for an Open Source Project. Here’s What Actually Happened.</title>
      <dc:creator>Nitin Kumar Yadav</dc:creator>
      <pubDate>Sun, 05 Jul 2026 15:47:49 +0000</pubDate>
      <link>https://dev.to/nitinkumaryadav1307/i-spent-3-days-building-a-cli-for-an-open-source-project-heres-what-actually-happened-29ao</link>
      <guid>https://dev.to/nitinkumaryadav1307/i-spent-3-days-building-a-cli-for-an-open-source-project-heres-what-actually-happened-29ao</guid>
      <description>&lt;p&gt;I'm a second-year IT engineering student. What I do have is GitHub, a laptop that overheats, and a habit of picking up problems that are slightly too big for me.&lt;/p&gt;

&lt;p&gt;This is the story of how I built the official CLI for urBackend  a BaaS (Backend as a Service) platform  from scratch, over three hard days, and somehow got it merged.&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Found This
&lt;/h2&gt;

&lt;p&gt;I was scrolling through GSSOC (GirlScript Summer of Code) organizations looking for something interesting to contribute to. Most projects wanted documentation fixes or UI tweaks. Then I saw urBackend:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"The developer's backend. Instantly generate secure CRUD APIs for your frontend applications using Node.js and MongoDB. Focus on the UI, let us handle the DB."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That got me. It's a real product. Real users. Real codebase. And they had an open issue for something ambitious — building an official CLI tool so developers could manage their projects from the terminal instead of clicking through a dashboard.&lt;/p&gt;

&lt;p&gt;I claimed it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Was Building
&lt;/h2&gt;

&lt;p&gt;The idea was simple: a command called &lt;code&gt;ub&lt;/code&gt; that lets developers do things like this from their terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ub login
ub project list
ub collection list
ub status
ub doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No browser. No dashboard. Just the terminal.&lt;/p&gt;

&lt;p&gt;The CLI needed to talk to urBackend's dashboard API using Personal Access Tokens — basically long-lived keys that prove you're a real developer without needing a browser session.&lt;/p&gt;

&lt;p&gt;Simple enough on paper. Three days of reality had other plans.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 1: Building the Foundation
&lt;/h2&gt;

&lt;p&gt;I scaffolded the entire CLI in TypeScript using Commander.js and tsup. The architecture I landed on had a clean separation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;core/&lt;/code&gt;&lt;/strong&gt; — HTTP client, config reader/writer, logger, error classes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;services/&lt;/code&gt;&lt;/strong&gt; — pure API wrappers, no console output, no filesystem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;commands/&lt;/code&gt;&lt;/strong&gt; — orchestrate service calls and print results&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;types/&lt;/code&gt;&lt;/strong&gt; — TypeScript interfaces for every API response&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The rule I enforced on myself: commands never touch JSON directly. Config persistence goes through &lt;code&gt;config.ts&lt;/code&gt;. Network calls go through &lt;code&gt;services/&lt;/code&gt;. Commands only orchestrate and print.&lt;/p&gt;

&lt;p&gt;One thing I'm proud of from day one is atomic config writes. When you run &lt;code&gt;ub login&lt;/code&gt;, your token gets saved to &lt;code&gt;~/.ub/config.json&lt;/code&gt;. If the process crashes mid-write, you don't end up with a corrupted file. The trick is simple:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tmp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;CONFIG_PATH&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;.tmp&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;writeFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;renameSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tmp&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;CONFIG_PATH&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;  &lt;span class="c1"&gt;// atomic on most OS&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Write to a temp file first. Rename it. The rename either completes or it doesn't — there's no half-written state. Small thing. Matters in production.&lt;/p&gt;

&lt;p&gt;By end of day one, the build was compiling clean and &lt;code&gt;ub --help&lt;/code&gt; was showing commands. Nothing actually worked yet, but it existed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 2: The Most Frustrating Six Hours of My Life
&lt;/h2&gt;

&lt;p&gt;This is the part I almost didn't include. But it's the most honest part, so here it is.&lt;/p&gt;

&lt;p&gt;I tried to run &lt;code&gt;ub login&lt;/code&gt; against the local backend. I pasted my test token. And I got this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✓ Logged in successfully.
✖ Unable to connect to the urBackend API.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Success and failure. At the same time. For six hours.&lt;/p&gt;

&lt;p&gt;The first problem was that the PAT UI wasn't built yet. The dashboard had no "Generate Token" page. So I had to manually insert a fake token directly into MongoDB by computing its SHA-256 hash and writing it to the &lt;code&gt;pats&lt;/code&gt; collection. That took an hour just to figure out.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ubpat_test_localdevtoken123&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createHash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sha256&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;digest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;hex&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pats&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insertOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;developer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;ObjectId&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;...&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="na"&gt;tokenHash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second problem: even after fixing that, &lt;code&gt;ub login&lt;/code&gt; would print "Logged in successfully" and then immediately print "Unable to connect." I added a debug line to see the raw API response:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Not Found"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"replayId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"kiroo-1782642930559-63fbf1"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not Found. But the curl command worked fine. Same token. Same endpoint. Different result.&lt;/p&gt;

&lt;p&gt;I spent two hours on this before I realized: every time I ran &lt;code&gt;ub login&lt;/code&gt; before setting the &lt;code&gt;URBACKEND_API_URL&lt;/code&gt; environment variable, the CLI saved &lt;code&gt;https://api.urbackend.bitbros.in&lt;/code&gt; (the production URL) to &lt;code&gt;~/.ub/config.json&lt;/code&gt;. Then even after I set the env variable, the config file took priority and the CLI kept hitting production.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;rm&lt;/span&gt; ~/.ub/config.json
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;URBACKEND_API_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://localhost:1234
ub login
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That fixed it. One deleted file. Six hours of debugging.&lt;/p&gt;

&lt;p&gt;The third problem was CSRF. The dashboard API protects all routes with CSRF tokens for browser sessions. The CLI doesn't use cookies — it uses Bearer tokens — but CSRF middleware was still running and blocking requests. I had to add a bypass in &lt;code&gt;app.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/billing/webhook&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/api/user/cli&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// skip CSRF for CLI routes&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fourth problem: all the project routes used &lt;code&gt;authMiddleware&lt;/code&gt; which validates JWT session cookies. The CLI sends a Bearer PAT token. So every &lt;code&gt;ub project list&lt;/code&gt; call got "Invalid Token."&lt;/p&gt;

&lt;p&gt;The fix was a new middleware called &lt;code&gt;authFlexible.js&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authFlexible&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;authHeader&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;authHeader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Bearer ubpat_&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;authenticateCLI&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;authMiddleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One middleware. Accepts both. Browser requests unchanged. CLI requests work.&lt;/p&gt;

&lt;p&gt;By end of day two, &lt;code&gt;ub login&lt;/code&gt;, &lt;code&gt;ub whoami&lt;/code&gt;, &lt;code&gt;ub project list&lt;/code&gt;, &lt;code&gt;ub project use&lt;/code&gt;, and &lt;code&gt;ub collection list&lt;/code&gt; were all working.&lt;/p&gt;

&lt;h2&gt;
  
  
  Day 3: CI, Review Comments, and What I Learned About Hidden Files
&lt;/h2&gt;

&lt;p&gt;I opened the PR on day three. 12 commits. The CI pipeline immediately failed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;FAIL src/__tests__/routes.user.test.js
  ● Test suite failed to run
    REDIS_URL is not defined in .env
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;My &lt;code&gt;authenticateCLI&lt;/code&gt; middleware imports from &lt;code&gt;@urbackend/common&lt;/code&gt;, which imports Redis, which crashes on import if &lt;code&gt;REDIS_URL&lt;/code&gt; isn't set. The test environment didn't have it.&lt;/p&gt;

&lt;p&gt;Two fixes. First, add the env var to CI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;REDIS_URL&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redis://localhost:6379&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Second, mock the middlewares in the test file so the ESM import chain never reaches Redis:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;mock&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../middlewares/authenticateCLI&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="nx"&gt;jest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fn&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mock_user_id&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;155 tests passing. 0 failing.&lt;/p&gt;

&lt;p&gt;Then came the review comments. The maintainer pointed out that my &lt;code&gt;ub doctor&lt;/code&gt; command was collapsing all errors into the same bucket. A timeout looked the same as a bad token, which would tell users to re-login when the real problem was connectivity.&lt;/p&gt;

&lt;p&gt;He was right. I added a three-state system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;true&lt;/code&gt; — check passed&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;false&lt;/code&gt; — definitively failed (HTTP 401, 404)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"unknown"&lt;/code&gt; — could not validate due to network or 5xx&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So now &lt;code&gt;ub doctor&lt;/code&gt; shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;⚠  PAT    Could not validate — API may be unreachable
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✖  PAT    Invalid or expired — run 'ub login'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;when the server is just down. Small change. Much less confusing for users.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things I Actually Learned
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PATs (Personal Access Tokens)&lt;/strong&gt; — I had used GitHub tokens before without thinking about how they work. Building this made it concrete: the raw token is shown once and never stored. What gets stored is a SHA-256 hash. When you authenticate, your token gets hashed and compared against the stored hash. The raw token never touches the database.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Hidden files start with a dot&lt;/strong&gt; — &lt;code&gt;~/.ub/config.json&lt;/code&gt; is hidden because the folder is &lt;code&gt;.ub&lt;/code&gt;. That's it. No special flag, no metadata. Just a dot prefix. Same reason you never see &lt;code&gt;.gitconfig&lt;/code&gt;, &lt;code&gt;.npmrc&lt;/code&gt;, or &lt;code&gt;.ssh&lt;/code&gt; in your home folder without &lt;code&gt;ls -a&lt;/code&gt;. Decades-old Unix convention that every CLI tool follows.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CSRF doesn't apply to Bearer token auth&lt;/strong&gt; — CSRF attacks work by tricking a browser into making requests using its stored cookies. If you're not using cookies, CSRF protection does nothing useful. The CLI uses Bearer tokens, not cookies, so bypassing CSRF for CLI routes is correct and safe.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tests mock things for a reason&lt;/strong&gt; — The &lt;code&gt;marked&lt;/code&gt; package is ESM-only. Jest runs in CommonJS mode. When my middleware imported &lt;code&gt;@urbackend/common&lt;/code&gt;, which imported &lt;code&gt;emailService.js&lt;/code&gt;, which imported &lt;code&gt;marked&lt;/code&gt;, the entire test suite crashed. Mocking the middleware in tests isn't laziness — it's isolating what you're actually testing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Read the config file before debugging for six hours&lt;/strong&gt; — If something worked yesterday and doesn't today, check your config file first.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Final Command List
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ub login              &lt;span class="c"&gt;# authenticate with PAT&lt;/span&gt;
ub &lt;span class="nb"&gt;logout&lt;/span&gt;             &lt;span class="c"&gt;# remove stored credentials&lt;/span&gt;
ub &lt;span class="nb"&gt;whoami&lt;/span&gt;             &lt;span class="c"&gt;# show authenticated developer&lt;/span&gt;
ub project list       &lt;span class="c"&gt;# list all projects with usage&lt;/span&gt;
ub project use        &lt;span class="c"&gt;# select active project&lt;/span&gt;
ub project info       &lt;span class="c"&gt;# show project details&lt;/span&gt;
ub collection list    &lt;span class="c"&gt;# list collections with schema&lt;/span&gt;
ub collection delete  &lt;span class="c"&gt;# delete collection (with confirmation)&lt;/span&gt;
ub status             &lt;span class="c"&gt;# account plan, limits, recent activity&lt;/span&gt;
ub doctor             &lt;span class="c"&gt;# diagnostic checks&lt;/span&gt;
ub doctor &lt;span class="nt"&gt;--json&lt;/span&gt;      &lt;span class="c"&gt;# same output as JSON for CI/AI agents&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--json&lt;/code&gt; flag on &lt;code&gt;ub doctor&lt;/code&gt; is something I'm especially happy with. AI agents and CI pipelines can consume it directly without parsing terminal output. One command tells you everything about your setup in a machine-readable format.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;ub pull&lt;/code&gt;, &lt;code&gt;ub push&lt;/code&gt;, and &lt;code&gt;ub generate&lt;/code&gt; aren't implemented yet — they need a schema sync endpoint on the backend that doesn't exist yet. That's Phase 3 of the roadmap. I'll build it when the backend is ready.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I'm Writing This
&lt;/h2&gt;

&lt;p&gt;I'm a second-year student with no internship and no referral. What I have is a merged PR in a real open source project, a CLI that actual developers will use, and a much better understanding of how auth middleware actually works.&lt;/p&gt;

&lt;p&gt;If you're in the same position — no experience, no connections — find a project with a real issue, claim it, and do the work. The debugging is miserable. The CI failures are annoying. The review comments sting a little. But the PR number at the end is real, and nobody can take it away.&lt;/p&gt;

&lt;p&gt;PR #340 if you want to see the code: &lt;a href="https://github.com/geturbackend/urBackend/pull/340" rel="noopener noreferrer"&gt;github.com/geturbackend/urBackend/pull/340&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Follow me on X for more open source, systems programming, and the unglamorous parts of learning to code.*&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;👉 &lt;a href="https://x.com/KATSUKI_EXPO" rel="noopener noreferrer"&gt;@KATSUKI_EXPO&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;👉 &lt;a href="https://github.com/Nitin-kumar-yadav1307" rel="noopener noreferrer"&gt;Github&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>cli</category>
      <category>opensource</category>
      <category>github</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Courage Browser — 1 month Later: CSS Variables, Tabs, Link Navigation &amp; More</title>
      <dc:creator>Nitin Kumar Yadav</dc:creator>
      <pubDate>Sat, 09 May 2026 16:25:47 +0000</pubDate>
      <link>https://dev.to/nitinkumaryadav1307/courage-browser-1-month-later-css-variables-tabs-link-navigation-more-5fh8</link>
      <guid>https://dev.to/nitinkumaryadav1307/courage-browser-1-month-later-css-variables-tabs-link-navigation-more-5fh8</guid>
      <description>&lt;p&gt;1 month ago I posted about building a browser from scratch — no Chromium, no WebKit, no libraries. Just Node.js, Electron, and a Canvas to draw on.&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Missed Part 1?&lt;/strong&gt; → &lt;a href="https://dev.to/nitinkumaryadav1307/i-built-a-web-browser-from-scratch-in-42-days-no-libraries-just-nodejs-416h"&gt;"I Built a Web Browser from Scratch in 42 Days"&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;That post covered the first 42 days: raw TCP connections, HTML tokenizer, DOM builder, CSS parser, layout engine, and a working Canvas renderer. Basic text showed up. It was exciting but rough.&lt;/p&gt;

&lt;p&gt;Since then — &lt;strong&gt;approximately 30 more days of building&lt;/strong&gt;. Here's what Courage can do now.&lt;/p&gt;




&lt;h2&gt;
  
  
  🆕 What shipped in Days 43–57
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Tabs
&lt;/h3&gt;

&lt;p&gt;The most visually obvious change. Courage now has a working tab system:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;+&lt;/code&gt; button creates a new tab&lt;/li&gt;
&lt;li&gt;Each tab maintains its own history, URL, and rendered page&lt;/li&gt;
&lt;li&gt;Clicking a tab switches context; the &lt;code&gt;×&lt;/code&gt; button closes it&lt;/li&gt;
&lt;li&gt;Active tab is highlighted&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fehkoczx7ccubtn2cax8i.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fehkoczx7ccubtn2cax8i.gif" alt="Courage Browser tab system" width="600" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every tab is fully independent — opening a link in a new tab doesn't share state with others.&lt;/p&gt;




&lt;h3&gt;
  
  
  Clickable Links (Anchor Navigation)
&lt;/h3&gt;

&lt;p&gt;This one took the most debugging. The challenge: Canvas 2D has no DOM events — I had to implement my own hit-testing.&lt;/p&gt;

&lt;p&gt;When you click the canvas, Courage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Gets the click coordinates via &lt;code&gt;canvas.getBoundingClientRect()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Walks the layout boxes looking for an anchor whose bounding rect contains that point&lt;/li&gt;
&lt;li&gt;If found, navigates to the &lt;code&gt;href&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I also added a &lt;strong&gt;pointer cursor&lt;/strong&gt; on hover — &lt;code&gt;mousemove&lt;/code&gt; triggers the same hit-test, and if the cursor is over a link, &lt;code&gt;cursor: pointer&lt;/code&gt; is set on the canvas element. Small detail, huge feel difference.&lt;/p&gt;




&lt;h3&gt;
  
  
  External CSS Fetching
&lt;/h3&gt;

&lt;p&gt;Before Day 44, Courage only read &lt;code&gt;&amp;lt;style&amp;gt;&lt;/code&gt; blocks inline. Now it fetches external stylesheets — it looks for &lt;code&gt;&amp;lt;link rel="stylesheet"&amp;gt;&lt;/code&gt; tags in the DOM, fires a second HTTP request for each one, and merges the rules into the cascade. This is what made real sites start to look like real sites.&lt;/p&gt;




&lt;h3&gt;
  
  
  Class &amp;amp; ID Selector Matching
&lt;/h3&gt;

&lt;p&gt;Added proper &lt;code&gt;.className&lt;/code&gt; and &lt;code&gt;#idName&lt;/code&gt; selector support. Before this, only tag selectors (&lt;code&gt;h1&lt;/code&gt;, &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;) worked. Now the full selector matching order is:&lt;br&gt;
tag → class → ID → pseudo-class (:link, :visited)&lt;/p&gt;


&lt;h3&gt;
  
  
  CSS &lt;code&gt;var()&lt;/code&gt; Resolution — The Big One
&lt;/h3&gt;

&lt;p&gt;GitHub, Wikipedia, and most modern sites define their entire color scheme using CSS custom properties like &lt;code&gt;--color-fg-default&lt;/code&gt; on &lt;code&gt;:root&lt;/code&gt;. Without &lt;code&gt;var()&lt;/code&gt; support, everything was either black or invisible.&lt;/p&gt;

&lt;p&gt;I added:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new &lt;code&gt;computed-styles.js&lt;/code&gt; module with a &lt;code&gt;getComputedStyle()&lt;/code&gt; function&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;:root&lt;/code&gt; selector support so variables defined globally actually register&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parentNode&lt;/code&gt; references in &lt;code&gt;dom-builder.js&lt;/code&gt; so the resolver walks up the tree correctly&lt;/li&gt;
&lt;li&gt;Pipeline wiring in &lt;code&gt;browser.js&lt;/code&gt; to run computed styles after selector matching&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The resolver works recursively — &lt;code&gt;var(--x)&lt;/code&gt; where &lt;code&gt;--x&lt;/code&gt; is itself &lt;code&gt;var(--y)&lt;/code&gt; chains correctly.&lt;/p&gt;


&lt;h3&gt;
  
  
  UA Stylesheet + &lt;code&gt;em&lt;/code&gt; Units + Bold/Italic
&lt;/h3&gt;

&lt;p&gt;Three smaller but visible wins:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;UA stylesheet:&lt;/strong&gt; Added browser default styles — &lt;code&gt;h1&lt;/code&gt; is now big and bold, &lt;code&gt;h2&lt;/code&gt; slightly smaller, etc. Before this, every element rendered the same size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;em&lt;/code&gt; units:&lt;/strong&gt; Font sizes expressed as &lt;code&gt;1.5em&lt;/code&gt; or &lt;code&gt;2em&lt;/code&gt; now compute correctly via a &lt;code&gt;headingSizes&lt;/code&gt; lookup table relative to the parent's font size.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Bold/italic fix:&lt;/strong&gt; The Canvas &lt;code&gt;ctx.font&lt;/code&gt; string has a strict format — &lt;code&gt;bold 16px serif&lt;/code&gt; works, &lt;code&gt;16px bold serif&lt;/code&gt; does not. Fixed a bug where both were silently ignored even when the styles matched correctly.&lt;/p&gt;


&lt;h2&gt;
  
  
  📸 Current state
&lt;/h2&gt;

&lt;p&gt;Here's how Courage has evolved:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Day 42&lt;/th&gt;
&lt;th&gt;Day 57&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Text renders, basic colors&lt;/td&gt;
&lt;td&gt;Tabs, links, h1 large &amp;amp; bold&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Inline CSS only&lt;/td&gt;
&lt;td&gt;External CSS fetched over network&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No link clicking&lt;/td&gt;
&lt;td&gt;Click navigates, hover shows pointer cursor&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;No CSS variables&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;var()&lt;/code&gt; resolves recursively&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h2&gt;
  
  
  🔭 What's next: Attribute Selectors
&lt;/h2&gt;

&lt;p&gt;The next open issue is attribute selector matching — things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-color-mode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;rel&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"stylesheet"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="err"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub's entire dark theme is gated behind &lt;code&gt;[data-color-mode="dark"]&lt;/code&gt; on the &lt;code&gt;&amp;lt;html&amp;gt;&lt;/code&gt; element. Once this lands, CSS variables on GitHub and Wikipedia will finally resolve to the right values.&lt;/p&gt;

&lt;p&gt;After that: image rendering. &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; tags currently just get skipped.&lt;/p&gt;




&lt;h2&gt;
  
  
  💻 Code
&lt;/h2&gt;

&lt;p&gt;Everything is open source:&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;&lt;a href="https://github.com/Nitin-kumar-yadav1307/Courage" rel="noopener noreferrer"&gt;github.com/Nitin-kumar-yadav1307/Courage&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I post updates here as milestones land. If you're curious about how browsers actually work — the TCP handshake, the tokenizer state machine, the CSS cascade, the layout algorithm — the code is readable and heavily iterative. No magic, no black boxes.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Day 57. Still going.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>programming</category>
      <category>browser</category>
      <category>computerscience</category>
      <category>systems</category>
    </item>
    <item>
      <title>"I Built a Web Browser from Scratch in 42 Days — No Libraries, Just Node.js"</title>
      <dc:creator>Nitin Kumar Yadav</dc:creator>
      <pubDate>Sat, 04 Apr 2026 19:02:26 +0000</pubDate>
      <link>https://dev.to/nitinkumaryadav1307/i-built-a-web-browser-from-scratch-in-42-days-no-libraries-just-nodejs-416h</link>
      <guid>https://dev.to/nitinkumaryadav1307/i-built-a-web-browser-from-scratch-in-42-days-no-libraries-just-nodejs-416h</guid>
      <description>&lt;h1&gt;
  
  
  I Built a Web Browser from Scratch in 42 Days
&lt;/h1&gt;

&lt;p&gt;42 days ago I made a decision.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyop0fgkq8galy7rsc2y.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwyop0fgkq8galy7rsc2y.jpeg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I wanted to understand how the internet actually works. Not just use it. Not just build on top of it. Actually understand it — at the wire level.&lt;/p&gt;

&lt;p&gt;So I started building a web browser from scratch. In Node.js. No external libraries. Every line written by hand.&lt;/p&gt;

&lt;p&gt;I called it Courage.&lt;/p&gt;




&lt;h2&gt;
  
  
  What Courage can do today
&lt;/h2&gt;

&lt;p&gt;...&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6m6v5klaqz1p3oi9ilsx.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6m6v5klaqz1p3oi9ilsx.jpeg" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parse URLs into protocol, host, port, path&lt;/li&gt;
&lt;li&gt;Open raw TCP and TLS connections&lt;/li&gt;
&lt;li&gt;Build and send HTTP GET requests&lt;/li&gt;
&lt;li&gt;Parse HTTP responses including chunked encoding&lt;/li&gt;
&lt;li&gt;Tokenize raw HTML character by character&lt;/li&gt;
&lt;li&gt;Build a DOM tree using a stack&lt;/li&gt;
&lt;li&gt;Match CSS rules to DOM nodes&lt;/li&gt;
&lt;li&gt;Calculate layout (x, y, width, height) for every element&lt;/li&gt;
&lt;li&gt;Paint rectangles and text on a Canvas using Electron&lt;/li&gt;
&lt;li&gt;Execute JavaScript via eval()&lt;/li&gt;
&lt;li&gt;Navigate with back, forward, reload&lt;/li&gt;
&lt;li&gt;Multiple tabs with independent history&lt;/li&gt;
&lt;li&gt;Render real websites — today it rendered Wikipedia&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What I learned
&lt;/h2&gt;

&lt;p&gt;Before this project I knew how to use the web. Now I understand it.&lt;/p&gt;

&lt;p&gt;TCP handshakes aren't magic. HTTP is just text. HTML is just tokens. CSS is just rules. A browser is just a pipeline.&lt;/p&gt;

&lt;p&gt;Every abstraction that used to feel like magic now makes complete sense.&lt;/p&gt;

&lt;p&gt;The most surprising thing — a layout engine is just two passes over a tree. Width flows down from parent to child. Height bubbles up from child to parent. That's it. That's what every browser does.&lt;/p&gt;




&lt;h2&gt;
  
  
  The hard days
&lt;/h2&gt;

&lt;p&gt;Day 9 — built a DOM tree using a stack. Failed 6 times before it clicked.&lt;/p&gt;

&lt;p&gt;Day 21 — layout engine born. Took 3 days to get width and height right.&lt;/p&gt;

&lt;p&gt;Day 42 — fixed a bug where CSS inside style tags was breaking the entire DOM tree because my tokenizer treated &amp;gt; in CSS selectors as HTML tag endings.&lt;/p&gt;

&lt;p&gt;Every bug taught me something real.&lt;/p&gt;




&lt;h2&gt;
  
  
  The code
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/Nitin-kumar-yadav1307/Courage" rel="noopener noreferrer"&gt;github.com/Nitin-kumar-yadav1307/Courage&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;42 days. No libraries. Every line by hand.&lt;/p&gt;

&lt;p&gt;This is just the beginning.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>programming</category>
      <category>beginners</category>
      <category>computerscience</category>
    </item>
    <item>
      <title>BugVaulty — Auto-Track Every Error to Notion with AI Solutions 🐛</title>
      <dc:creator>Nitin Kumar Yadav</dc:creator>
      <pubDate>Sat, 28 Mar 2026 07:23:31 +0000</pubDate>
      <link>https://dev.to/nitinkumaryadav1307/bugvaulty-auto-track-every-error-to-notion-with-ai-solutions-am9</link>
      <guid>https://dev.to/nitinkumaryadav1307/bugvaulty-auto-track-every-error-to-notion-with-ai-solutions-am9</guid>
      <description>&lt;p&gt;🤖 &lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion MCP Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;BugVaulty&lt;/strong&gt; is an npm package that automatically catches &lt;br&gt;
every error in your Node.js, Express, or React app — analyzes &lt;br&gt;
it with AI — and saves it to Notion automatically.&lt;/p&gt;

&lt;p&gt;No manual work. One line of code. Forever.&lt;/p&gt;

&lt;p&gt;In my early coding journey, I used to write down errors because it's one of the best ways to debug. If you already know how an error happened, it becomes easier to fix.&lt;/p&gt;

&lt;p&gt;Also, if you're working in a team, everyone can see the errors and help debug them, so you don't have to search the same error again and again on Google or AI.&lt;/p&gt;

&lt;p&gt;So I automated this process using Notion, MCP, and AI. Once you install and configure this package, all the errors from your terminal will automatically be tracked in your Notion.&lt;/p&gt;
&lt;h3&gt;
  
  
  How it works:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error happens in your app
        ↓
BugVaulty catches it automatically
        ↓
AI analyzes it (Groq / OpenAI / Claude)
        ↓
Notion page created automatically with:
  📅 Date &amp;amp; Time
  📁 File path &amp;amp; line number  
  ❌ What went wrong
  💡 Step by step solution
  🔧 Code fix
  🚀 How to avoid it
  ⭐ Difficulty rating
  🏷️ Tags
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  One line setup:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;bugvaulty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bugvaulty&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;bugvaulty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// That's it! All errors now tracked to Notion automatically&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Supports 3 AI providers:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Free option - Groq&lt;/span&gt;
&lt;span class="nx"&gt;bugvaulty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;groq&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;gsk_xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}})&lt;/span&gt;

&lt;span class="c1"&gt;// OpenAI&lt;/span&gt;
&lt;span class="nx"&gt;bugvaulty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;openai&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sk-xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}})&lt;/span&gt;

&lt;span class="c1"&gt;// Anthropic Claude&lt;/span&gt;
&lt;span class="nx"&gt;bugvaulty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;ai&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;claude&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sk-ant-xxx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Works with Express:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;use&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;bugvaulty&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;expressMiddleware&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Works with React:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;BugVaultyProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bugvaulty/react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;BugVaultyProvider&lt;/span&gt; &lt;span class="na"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;{...}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;App&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;BugVaultyProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  What Notion looks like after an error:
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bv4r377hezahy5wx100.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1bv4r377hezahy5wx100.png" alt="BugVaulty Notion Error Page" width="588" height="907"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every project gets its own page in Notion — detected automatically from your package.json name!&lt;/p&gt;
&lt;h2&gt;
  
  
  Video Demo
&lt;/h2&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/gvG2qNiNXFs"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  Show us the code
&lt;/h2&gt;

&lt;p&gt;📦 &lt;a href="https://www.npmjs.com/package/bugvaulty" rel="noopener noreferrer"&gt;npm package&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💻 &lt;a href="https://github.com/Nitin-kumar-yadav1307/bug-vaulty" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Install it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install &lt;/span&gt;bugvaulty
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How I Used Notion MCP
&lt;/h2&gt;

&lt;p&gt;Notion MCP is the entire backbone of BugVaulty.&lt;/p&gt;

&lt;p&gt;Instead of building a custom database or dashboard, &lt;br&gt;
I used Notion as the storage and display layer via &lt;br&gt;
the Notion API and MCP.&lt;/p&gt;

&lt;p&gt;Here is what Notion MCP unlocks:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Auto project organization&lt;/strong&gt;&lt;br&gt;
BugVaulty detects your project name from package.json &lt;br&gt;
and automatically creates a dedicated Notion page for &lt;br&gt;
that project. Every error goes under the right project &lt;br&gt;
automatically.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Rich structured pages&lt;/strong&gt;&lt;br&gt;
Each error is saved as a beautifully structured Notion &lt;br&gt;
page with headings, bullet points, and code blocks — &lt;br&gt;
not just plain text.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Searchable knowledge base&lt;/strong&gt;&lt;br&gt;
Because everything is in Notion, you can search your &lt;br&gt;
entire error history instantly. Next time you hit the &lt;br&gt;
same error — your own solution is already there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Zero infrastructure&lt;/strong&gt;&lt;br&gt;
No custom dashboard needed. No database to maintain. &lt;br&gt;
Notion IS the interface. This is what makes Notion MCP &lt;br&gt;
so powerful for developer tools.&lt;/p&gt;

&lt;p&gt;BugVaulty + Notion MCP = your personal AI debugging &lt;br&gt;
brain that never forgets. 🧠&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>notionchallenge</category>
      <category>mcp</category>
      <category>ai</category>
    </item>
    <item>
      <title>⚡ GitZip — Turning Git Commands into One-Word Spells with GitHub Copilot CLI</title>
      <dc:creator>Nitin Kumar Yadav</dc:creator>
      <pubDate>Tue, 10 Feb 2026 05:11:28 +0000</pubDate>
      <link>https://dev.to/nitinkumaryadav1307/gitzip-turning-git-commands-into-one-word-spells-with-github-copilot-cli-976</link>
      <guid>https://dev.to/nitinkumaryadav1307/gitzip-turning-git-commands-into-one-word-spells-with-github-copilot-cli-976</guid>
      <description>&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;GitZip is a CLI productivity tool that turns long and hard-to-remember Git commands into short, reusable one-word "spells".&lt;/p&gt;

&lt;p&gt;Instead of repeatedly typing complex Git commands or copy-pasting them from Google or ChatGPT, developers can save a command once and reuse it instantly.&lt;/p&gt;

&lt;p&gt;For example, instead of running:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git reset --soft HEAD~1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I can save it as a spell called &lt;code&gt;gundo&lt;/code&gt; and run it anytime with a single word.&lt;/p&gt;

&lt;p&gt;GitZip focuses on improving developer productivity, reducing mistakes, and making Git easier to use — especially for beginners.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;🎥 &lt;strong&gt;Demo Video:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://drive.google.com/file/d/1gLayfnghgSHl3z6Chc-XuTbF9KIL1_mk/view?usp=sharing" rel="noopener noreferrer"&gt;for demo video click here&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The demo shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Creating a Git command spell&lt;/li&gt;
&lt;li&gt;Running the spell inside a Git repository&lt;/li&gt;
&lt;li&gt;Using &lt;code&gt;gitzip demo&lt;/code&gt;, which automatically:

&lt;ul&gt;
&lt;li&gt;Initializes a Git repository&lt;/li&gt;
&lt;li&gt;Creates commits&lt;/li&gt;
&lt;li&gt;Creates a spell&lt;/li&gt;
&lt;li&gt;Undoes a commit&lt;/li&gt;
&lt;li&gt;Shows before/after Git logs&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;This makes it very easy for judges to test and understand the project.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Experience with GitHub Copilot CLI
&lt;/h2&gt;

&lt;p&gt;GitHub Copilot CLI played an important role in my development workflow.&lt;/p&gt;

&lt;p&gt;While building GitZip, I frequently used Copilot (CLI / Chat) to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ask for correct Git commands&lt;/li&gt;
&lt;li&gt;Understand safer alternatives to risky Git operations&lt;/li&gt;
&lt;li&gt;Validate commands before saving them as reusable spells&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;GitZip is designed around this exact workflow:&lt;br&gt;
Ask GitHub Copilot for a Git command → Save it once → Reuse it forever.&lt;/p&gt;

&lt;p&gt;Instead of replacing Copilot, GitZip extends it by making Copilot's output reusable inside the terminal. This helped me build faster, avoid mistakes, and focus on designing a better user experience.&lt;/p&gt;




&lt;h2&gt;
  
  
  Source Code
&lt;/h2&gt;

&lt;p&gt;🔗 GitHub Repository:&lt;br&gt;&lt;br&gt;
&lt;a href="https://github.com/Nitin-kumar-yadav1307/GitZip" rel="noopener noreferrer"&gt;https://github.com/Nitin-kumar-yadav1307/GitZip&lt;/a&gt;&lt;/p&gt;




</description>
      <category>devchallenge</category>
      <category>githubchallenge</category>
      <category>cli</category>
      <category>githubcopilot</category>
    </item>
  </channel>
</rss>
