<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Mayank Srivastava</title>
    <description>The latest articles on DEV Community by Mayank Srivastava (@mayank7924).</description>
    <link>https://dev.to/mayank7924</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3940903%2F4fd37efa-a174-43b0-85e7-8b83ad17896e.jpeg</url>
      <title>DEV Community: Mayank Srivastava</title>
      <link>https://dev.to/mayank7924</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/mayank7924"/>
    <language>en</language>
    <item>
      <title>Why GitHub Cannot Paginate Through Millions of Repositories - Deep Pagination Explained</title>
      <dc:creator>Mayank Srivastava</dc:creator>
      <pubDate>Mon, 03 Aug 2026 20:32:03 +0000</pubDate>
      <link>https://dev.to/mayank7924/why-github-silently-caps-your-search-at-1000-results-and-whats-actually-happening-underneath-1oik</link>
      <guid>https://dev.to/mayank7924/why-github-silently-caps-your-search-at-1000-results-and-whats-actually-happening-underneath-1oik</guid>
      <description>&lt;p&gt;You've paginated through a big result set before. Page 2 loads fine. Page 200 takes a beat. Nobody stops to ask why — until something like this shows up instead of a slow page:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Only the first 1000 search results are available."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"documentation_url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://docs.github.com/v3/search/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"status"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"422"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's GitHub's own search API. Ask it for page 9 or page 10 of a search and you get real results. Ask for page 11 and you get that, on purpose, every time.&lt;/p&gt;

&lt;p&gt;I went and looked at why. Full video below, written breakdown after it for anyone who'd rather read.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Reproducing it
&lt;/h2&gt;

&lt;p&gt;Not a guess — this is live, right now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="s2"&gt;"https://api.github.com/search/repositories?q=javascript&amp;amp;page=9&amp;amp;per_page=100"&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; real results&lt;/span&gt;

curl &lt;span class="s2"&gt;"https://api.github.com/search/repositories?q=javascript&amp;amp;page=10&amp;amp;per_page=100"&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; real results&lt;/span&gt;

curl &lt;span class="s2"&gt;"https://api.github.com/search/repositories?q=javascript&amp;amp;page=11&amp;amp;per_page=100"&lt;/span&gt;
&lt;span class="c"&gt;# -&amp;gt; 422, the message above&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://docs.github.com/en/rest/search/search" rel="noopener noreferrer"&gt;GitHub documents this cap directly&lt;/a&gt; — it's not rate limiting, and it's not a bug that slipped through. Once you understand the mechanism it's protecting against, the same wall shows up in a lot of places that don't bother telling you about it — including, probably, a table you've built yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  The query behind the page number
&lt;/h2&gt;

&lt;p&gt;Most pagination UIs turn a page number into something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="k"&gt;OFFSET&lt;/span&gt; &lt;span class="mi"&gt;999980&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Twenty rows per page, page 50,000 requested — that's an offset of nearly a million. It's easy to assume the database can jump straight to row 999,980. It can't, and the reason is in how the underlying index is built, not in anything about this specific query.&lt;/p&gt;

&lt;h2&gt;
  
  
  What an index actually is
&lt;/h2&gt;

&lt;p&gt;A sorted column is backed by a &lt;strong&gt;B-tree&lt;/strong&gt; — a structure built for one thing: walking rows &lt;em&gt;in order&lt;/em&gt;, fast. The leaf level is a chain of rows in sorted sequence. That chain is what makes &lt;code&gt;ORDER BY&lt;/code&gt; cheap.&lt;/p&gt;

&lt;p&gt;It is also the whole problem. There's no operation in a B-tree for "give me whatever's at position 999,980." Only for "give me whatever's next after this value." Position and value are not the same axis, and only one of them has a shortcut.&lt;/p&gt;

&lt;p&gt;So &lt;code&gt;OFFSET 999980&lt;/code&gt; does the only thing it can: start at the beginning of the scan, count a row, discard it, count the next, discard it — one at a time, nearly a million times — before it starts collecting the 20 rows you actually asked for.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cost = O(offset), not O(page size).&lt;/strong&gt; Page 10 is nearly free. Page 50,000 means walking and throwing away roughly a million rows just to reach the part you wanted.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually happens, measured
&lt;/h2&gt;

&lt;p&gt;Theory's cheap. Here's a real table — 5 million rows, local Postgres:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; pagination-demo &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;demo &lt;span class="nt"&gt;-p&lt;/span&gt; 5432:5432 &lt;span class="nt"&gt;-d&lt;/span&gt; postgres:16
docker &lt;span class="nb"&gt;exec&lt;/span&gt; &lt;span class="nt"&gt;-it&lt;/span&gt; pagination-demo psql &lt;span class="nt"&gt;-U&lt;/span&gt; postgres
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;TABLE&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="nb"&gt;BIGINT&lt;/span&gt; &lt;span class="k"&gt;PRIMARY&lt;/span&gt; &lt;span class="k"&gt;KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;title&lt;/span&gt; &lt;span class="nb"&gt;TEXT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="nb"&gt;TIMESTAMP&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;INSERT&lt;/span&gt; &lt;span class="k"&gt;INTO&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Video '&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;NOW&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;interval&lt;/span&gt; &lt;span class="s1"&gt;'3 years'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;generate_series&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5000000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="k"&gt;g&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="k"&gt;OFFSET&lt;/span&gt; &lt;span class="mi"&gt;999980&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;Limit&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;33323&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;00&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;33323&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;67&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;121&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;583&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;121&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;586&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="k"&gt;Index&lt;/span&gt; &lt;span class="n"&gt;Scan&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;videos_pkey&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;166616&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;4999991&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;012&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;94&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;512&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1000000&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Planning&lt;/span&gt; &lt;span class="nb"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;201&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;
&lt;span class="n"&gt;Execution&lt;/span&gt; &lt;span class="nb"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;121&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;603&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;rows=1000000&lt;/code&gt; on the scan node isn't an estimate — that's the actual count the engine walked through before it could return anything, exactly matching the mechanism above.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix everyone reaches for, and what it actually does
&lt;/h2&gt;

&lt;p&gt;The usual advice is "use cursor pagination instead of OFFSET." Here's the query that advice produces:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;EXPLAIN&lt;/span&gt; &lt;span class="k"&gt;ANALYZE&lt;/span&gt;
&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;845923&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;Limit&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;15&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;477&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;484&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;  &lt;span class="k"&gt;Index&lt;/span&gt; &lt;span class="n"&gt;Scan&lt;/span&gt; &lt;span class="k"&gt;Backward&lt;/span&gt; &lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="n"&gt;videos_pkey&lt;/span&gt; &lt;span class="k"&gt;on&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt;  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cost&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;43&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;30516&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;09&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;851752&lt;/span&gt; &lt;span class="n"&gt;width&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;29&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;actual&lt;/span&gt; &lt;span class="nb"&gt;time&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;475&lt;/span&gt;&lt;span class="p"&gt;..&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;480&lt;/span&gt; &lt;span class="k"&gt;rows&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;20&lt;/span&gt; &lt;span class="n"&gt;loops&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;Index&lt;/span&gt; &lt;span class="n"&gt;Cond&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;845923&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;Planning&lt;/span&gt; &lt;span class="nb"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;129&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;
&lt;span class="n"&gt;Execution&lt;/span&gt; &lt;span class="nb"&gt;Time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mi"&gt;503&lt;/span&gt; &lt;span class="n"&gt;ms&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same table. Same 20 rows back. &lt;strong&gt;121.603 ms → 0.503 ms&lt;/strong&gt; — roughly 242x, and it's not a rounding error, it's a different cost curve entirely.&lt;/p&gt;

&lt;p&gt;What changed underneath: instead of walking the leaf chain from the start, the engine descends the tree directly — root, branch, leaf — to &lt;code&gt;id = 845923&lt;/code&gt;, then reads 20 rows backward from there. A seek, not a scan. That's the entire mechanism. Nothing about the table changed, nothing about the index changed — only the shape of the question changed, from "what's at position N" to "what comes after this value."&lt;/p&gt;

&lt;p&gt;This is also why Instagram says "load more" and X says "load older posts" instead of showing page numbers. Under the hood, that's a cursor — &lt;code&gt;after=&amp;lt;id&amp;gt;&lt;/code&gt; — not a position.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this breaks silently
&lt;/h2&gt;

&lt;p&gt;Keyset pagination only holds up if the sort key is unique and strictly ordered — a primary key like &lt;code&gt;id&lt;/code&gt; qualifies. The moment you sort by something that can repeat, like &lt;code&gt;created_at&lt;/code&gt;, where two rows can land on the exact same millisecond, a single-column cursor can silently skip a row or hand back a duplicate. Nothing errors. It just quietly returns the wrong set.&lt;/p&gt;

&lt;p&gt;The fix is comparing a pair of columns, not one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="n"&gt;videos&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="p"&gt;(:&lt;/span&gt;&lt;span class="n"&gt;last_created_at&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;last_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;created_at&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;20&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line is the gap between "works in the demo" and "works in production."&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check before you reach for either
&lt;/h2&gt;

&lt;p&gt;In order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Does the page number itself need to exist?&lt;/strong&gt; If users are scrolling a feed, not jumping to "page 4,721," you don't need OFFSET's random access at all — cursor pagination is strictly better here, no tradeoff to weigh.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Do you actually need random access?&lt;/strong&gt; Admin dashboards, audit tools, support panels — anywhere someone legitimately needs "go to page 42" — OFFSET's random access is the feature, not the bug, and the fix is usually capping how deep it's allowed to go rather than replacing it outright.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If you're building a public API, assume someone will request the deepest possible page.&lt;/strong&gt; &lt;a href="https://www.elastic.co/guide/en/elasticsearch/reference/current/paginate-search-results.html" rel="noopener noreferrer"&gt;Elasticsearch refuses to paginate past 10,000 results by default&lt;/a&gt; and points you at &lt;code&gt;search_after&lt;/code&gt; instead — the same fix GitHub ships, in a different system. Capping the depth is cheaper than absorbing the cost of someone finding it by accident.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Not a universal fix
&lt;/h2&gt;

&lt;p&gt;OFFSET isn't wrong. It's the right tool when random access matters more than depth — small tables, internal tools, anywhere nobody's going past page 20 anyway. Cursor pagination isn't strictly "better," it trades away the ability to jump anywhere for a flat cost curve at any depth. The mistake isn't picking one — it's not knowing there's a choice, and finding out which one you picked at the exact moment your table crosses a few million rows.&lt;/p&gt;




&lt;p&gt;If this was useful, the video walks through the same benchmark live — GitHub's actual API response, the terminal running both queries against the same 5M-row table, &lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; output as it prints. Drop a comment if you want the follow-up on why &lt;code&gt;COUNT(*)&lt;/code&gt; gets slow on the exact same kind of table — different query, same root cause.&lt;/p&gt;

</description>
      <category>database</category>
      <category>programming</category>
      <category>sql</category>
      <category>backend</category>
    </item>
    <item>
      <title>The npm `overrides` Snippet Everyone Copy-Pastes — And What It Actually Does</title>
      <dc:creator>Mayank Srivastava</dc:creator>
      <pubDate>Mon, 03 Aug 2026 20:13:35 +0000</pubDate>
      <link>https://dev.to/mayank7924/the-npm-overrides-snippet-everyone-copy-pastes-and-what-it-actually-does-3flg</link>
      <guid>https://dev.to/mayank7924/the-npm-overrides-snippet-everyone-copy-pastes-and-what-it-actually-does-3flg</guid>
      <description>&lt;p&gt;You've seen this error. &lt;code&gt;npm install&lt;/code&gt; fails with &lt;code&gt;ERESOLVE&lt;/code&gt;, you search it, find a GitHub issue, copy an &lt;code&gt;overrides&lt;/code&gt; snippet into &lt;code&gt;package.json&lt;/code&gt;, and the red text disappears on your next run.&lt;/p&gt;

&lt;p&gt;Nobody asks what that block actually did.&lt;/p&gt;

&lt;p&gt;I hit this exact error yesterday setting up a cloned repo, and instead of moving on once the install went green, I went and looked. Full video below, written breakdown after it for anyone who'd rather read.&lt;/p&gt;

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

&lt;h2&gt;
  
  
  The error
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="go"&gt;npm error code ERESOLVE
npm error ERESOLVE unable to resolve dependency tree
npm error
npm error While resolving: forced-error-demo@1.0.0
npm error Found: react@19.2.8
npm error node_modules/react
npm error   react@"^19.0.0" from the root project
npm error
npm error Could not resolve dependency:
npm error peer react@"^16.9.0 || ^17.0.0" from @testing-library/react-hooks@8.0.1
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two lines in here matter, and they're not making the same kind of claim.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;Found: react@19.2.8&lt;/code&gt;&lt;/strong&gt; — this is just what's already resolved, because it's what my own &lt;code&gt;package.json&lt;/code&gt; asked for (&lt;code&gt;^19.0.0&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;peer react@"^16.9.0 || ^17.0.0" from @testing-library/react-hooks@8.0.1&lt;/code&gt;&lt;/strong&gt; — this is a different thing entirely: a peer dependency.&lt;/p&gt;

&lt;h2&gt;
  
  
  What a peer dependency actually is
&lt;/h2&gt;

&lt;p&gt;Straight from &lt;code&gt;@testing-library/react-hooks&lt;/code&gt;'s own &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@testing-library/react-hooks"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"8.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"peerDependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"^16.9.0 || ^17.0.0"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;peerDependencies&lt;/code&gt; entry is a dependency the package needs, but expects the &lt;em&gt;consumer&lt;/em&gt; to provide — the package isn't bundling its own copy of React, it's reaching into whatever React is already running in your project, and declaring the only versions it was built to reach into safely.&lt;/p&gt;

&lt;p&gt;Compare that to a normal &lt;code&gt;dependencies&lt;/code&gt; entry, which npm installs as its own separate copy in &lt;code&gt;node_modules&lt;/code&gt;. A peer dependency installs nothing. It just checks.&lt;/p&gt;

&lt;p&gt;npm has enforced that check by default since v7. Before that, npm 3 through 6 mostly let mismatches like this through silently — which is exactly why the &lt;code&gt;--legacy-peer-deps&lt;/code&gt; flag is named what it is: it tells npm to go back to that old, unchecked behavior.&lt;/p&gt;

&lt;p&gt;So this error isn't npm being broken. It's npm accurately reporting that a package in your tree is making a claim about your React version that isn't true.&lt;/p&gt;

&lt;h2&gt;
  
  
  The fix everyone copy-pastes
&lt;/h2&gt;

&lt;p&gt;Here's the snippet you'll actually find on GitHub issues for this exact situation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@testing-library/react-hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$react"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;$react&lt;/code&gt; syntax means "whatever version I already have installed as a direct dependency, use that." So this line says: wherever &lt;code&gt;@testing-library/react-hooks&lt;/code&gt; declares a requirement on &lt;code&gt;react&lt;/code&gt; — including its peer requirement — force it to match root's &lt;code&gt;react@19.0.0&lt;/code&gt; instead.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;npm install&lt;/code&gt; again. It succeeds. No red text. Looks fixed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What actually changed (and what didn't)
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;overrides&lt;/code&gt; didn't touch a single line of code inside &lt;code&gt;@testing-library/react-hooks&lt;/code&gt;. It didn't check whether that package's internals can actually run against React 19. It went into the resolved dependency graph and rewrote the version number the peer check compares against — from &lt;code&gt;^16.9.0 || ^17.0.0&lt;/code&gt; to &lt;code&gt;19.0.0&lt;/code&gt; — so the check has nothing left to disagree with.&lt;/p&gt;

&lt;p&gt;That's the entire mechanism. &lt;code&gt;overrides&lt;/code&gt; doesn't resolve incompatibility. It rewrites the number two packages are being compared on, whether or not the code underneath agrees.&lt;/p&gt;

&lt;p&gt;What did &lt;strong&gt;not&lt;/strong&gt; happen:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No code inside the overridden package ran&lt;/li&gt;
&lt;li&gt;No check that its React-16/17-era internals still work against React 19's scheduling model&lt;/li&gt;
&lt;li&gt;No test suite executed to confirm any of it&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Why the gap matters
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;@testing-library/react-hooks&lt;/code&gt; was built before hook-testing support existed in &lt;code&gt;@testing-library/react&lt;/code&gt; itself, and it's effectively frozen at how React 16 and 17 scheduled renders. React 19's internals have moved since then. The override doesn't close that gap — it just moves &lt;em&gt;when&lt;/em&gt; you find out about it.&lt;/p&gt;

&lt;p&gt;Without the override: you read two lines of log, thirty seconds, and you're acting on real information.&lt;/p&gt;

&lt;p&gt;With the override: install succeeds clean, ships to CI, ships to prod — and the actual discovery happens whenever a hook test misbehaves or a render warning shows up with no line in your own code pointing back to it. Same incompatibility, if it's real. Just a much more expensive place to find it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What to check before you reach for &lt;code&gt;overrides&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;In order:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Does the conflicting package still need to exist?&lt;/strong&gt; In this case — no. &lt;code&gt;@testing-library/react&lt;/code&gt; has supported hook testing natively for a long time now, which makes &lt;code&gt;@testing-library/react-hooks&lt;/code&gt; a dependency that's outlived its reason for being. Deleting it is a better fix than patching around it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Can it be upgraded instead?&lt;/strong&gt; A newer major of the conflicting package may not have this peer conflict at all. You might have to touch your own code too, since not every upgrade is backward-compatible — but that's still cheaper than a silent runtime bug.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;If it's genuinely unavoidable, scope the override.&lt;/strong&gt; Don't apply it globally:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;unscoped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;—&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;forces&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;every&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;package&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;in&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;tree&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;that&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;touches&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;react&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$react"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;scoped&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;—&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;forces&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;it&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;only&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;where&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;the&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;actual&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;conflict&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="nl"&gt;"overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"@testing-library/react-hooks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"react"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$react"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything else in your tree keeps the version npm originally resolved. Smaller blast radius, easier to reason about, easier to remove later.&lt;/p&gt;

&lt;h2&gt;
  
  
  A temporary patch. Not permanent config.
&lt;/h2&gt;

&lt;p&gt;If you do add an override, treat it like debt, not like settings. Leave a note on why it's there — &lt;code&gt;package.json&lt;/code&gt; doesn't support comments, so put it in a &lt;code&gt;README&lt;/code&gt; or an &lt;code&gt;OVERRIDES.md&lt;/code&gt;: what it's patching, when to revisit it, who added it. Put it on your dependency-audit schedule, whatever that already is for your team, and pull it out the moment the upstream package makes it unnecessary.&lt;/p&gt;

&lt;p&gt;An override with no expiry date doesn't leave. It just becomes the next person's mystery to reverse-engineer.&lt;/p&gt;




&lt;p&gt;If this was useful, the video covers the same ground with the terminal running live — installing with and without the override, reading the actual &lt;code&gt;npm ls&lt;/code&gt; output, watching what changes. Drop a comment if you want the follow-up on npm's resolution algorithm itself — why these conflicts happen before any override enters the picture.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>npm</category>
      <category>nodejs</category>
      <category>react</category>
    </item>
    <item>
      <title>How I Beat a 16-Click Anti-Bot Ad Trap</title>
      <dc:creator>Mayank Srivastava</dc:creator>
      <pubDate>Wed, 20 May 2026 16:50:04 +0000</pubDate>
      <link>https://dev.to/mayank7924/how-i-beat-a-16-click-anti-bot-ad-trap-4o4l</link>
      <guid>https://dev.to/mayank7924/how-i-beat-a-16-click-anti-bot-ad-trap-4o4l</guid>
      <description>&lt;h3&gt;
  
  
  🎯 I Got Tired of Closing Popups. So I Reverse-Engineered the Entire Ad Stack.
&lt;/h3&gt;

&lt;p&gt;We’ve all been there.&lt;br&gt;
You open a streaming site.&lt;br&gt;&lt;br&gt;
You click play.&lt;/p&gt;

&lt;p&gt;💥 A new tab appears.&lt;/p&gt;

&lt;p&gt;You close it. Click again.&lt;/p&gt;

&lt;p&gt;💥 Another popup.&lt;/p&gt;

&lt;p&gt;Some sites are so aggressive that watching a single episode feels like defusing a bomb through &lt;strong&gt;16 consecutive popups&lt;/strong&gt; while trying not to accidentally download malware from 2007.&lt;/p&gt;

&lt;p&gt;Most people tolerate it.&lt;br&gt;
I couldn’t.&lt;/p&gt;

&lt;p&gt;As a developer, the moment software starts fighting the user this aggressively, it stops being an inconvenience and becomes a challenge.&lt;br&gt;
So I decided to break it.&lt;/p&gt;


&lt;h3&gt;
  
  
  🧪 Phase 1 — The “This Should Work” Delusion
&lt;/h3&gt;

&lt;p&gt;My first thought was simple:&lt;br&gt;
Override &lt;code&gt;window.open&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;open&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Done. Right?&lt;br&gt;
Nope.&lt;br&gt;
The site completely ignored it.&lt;br&gt;
That’s when I realized this wasn’t normal popup logic anymore.&lt;br&gt;
This was engineered hostility.&lt;/p&gt;
&lt;h3&gt;
  
  
  👻 Phase 2 — Chasing Invisible Click Traps
&lt;/h3&gt;

&lt;p&gt;I opened DevTools and started tracing the DOM during every interaction.&lt;br&gt;
Something strange kept happening.&lt;br&gt;
Inside the video player container, the final &lt;/p&gt; kept rapidly changing after every click.&lt;br&gt;
Then it clicked.&lt;br&gt;
The site was dynamically generating transparent overlay layers directly above the player.&lt;br&gt;
The actual video button wasn’t receiving my clicks.&lt;br&gt;
The invisible overlay was.&lt;br&gt;
Every physical mouse interaction got hijacked by a temporary fullscreen layer designed to trigger an ad redirect using a trusted human click.&lt;br&gt;
The sequence looked something like this:&lt;br&gt;

&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌───────────────────────────────┐
│ 🖱️ User Click                │
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│ 👻 Invisible Overlay Captures│
│    the Trusted Click Event   │
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│ 💥 Popup / Redirect Triggered│
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│ 🧹 Overlay Self-Deletes      │
└──────────────┬────────────────┘
               │
               ▼
┌───────────────────────────────┐
│ ♻️ New Overlay Injected      │
└──────────────┬────────────────┘
               │
               └─────── loops ───────▶
&lt;/code&gt;&lt;/pre&gt;


&lt;p&gt;Ridiculously clever.&lt;br&gt;
Because technically I initiated the click, the browser trusted the action.&lt;br&gt;
So I escalated.&lt;/p&gt;
&lt;h3&gt;
  
  
  ⚔️ Phase 3 — Fighting Back With a Chrome Extension
&lt;/h3&gt;

&lt;p&gt;At this point, I realized manual debugging alone wasn't enough anymore.&lt;/p&gt;

&lt;p&gt;So I decided to escalate.&lt;/p&gt;

&lt;p&gt;I created a custom Chrome extension specifically for this battle.&lt;/p&gt;

&lt;p&gt;The first version was primitive — a DOM-level interceptor designed to watch the page in realtime and surgically remove the invisible overlay traps before they could hijack clicks.&lt;/p&gt;

&lt;p&gt;I used a &lt;code&gt;MutationObserver&lt;/code&gt; to monitor the page for dynamically injected elements:&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;observer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;MutationObserver&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelectorAll&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.suspicious-overlay&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;el&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;el&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="nx"&gt;observer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;childList&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subtree&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;For a brief moment…&lt;br&gt;
…it worked.&lt;br&gt;
Then the site hit back.&lt;/p&gt;

&lt;p&gt;🤖 It was at this moment I realized I was fighting an Anti-Bot System&lt;/p&gt;

&lt;p&gt;Buried deep inside the bundled scripts was an aggressive anti-fraud service called Adscore.&lt;/p&gt;

&lt;p&gt;The behavior suddenly changed:&lt;/p&gt;

&lt;p&gt;🧹 Console logs started disappearing&lt;br&gt;
🔄 The player entered infinite click loops&lt;br&gt;
🕵️ Hardware fingerprinting routines activated&lt;br&gt;
🎮 WebGL context checks triggered&lt;br&gt;
📏 Screen dimension math got evaluated&lt;br&gt;
🧠 Browser behavior started getting profiled&lt;/p&gt;

&lt;p&gt;The site wasn’t just showing ads anymore.&lt;br&gt;
It was actively detecting interference.&lt;br&gt;
And then I realized my mistake:&lt;/p&gt;

&lt;p&gt;I was still fighting inside the webpage sandbox.&lt;/p&gt;

&lt;p&gt;That was their territory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧠 Phase 4 — Stop Fighting the UI. Attack the Network.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;If the frontend is weaponized…&lt;br&gt;
…you stop fighting visuals.&lt;/p&gt;

&lt;p&gt;You cut the supply lines.&lt;/p&gt;

&lt;p&gt;Instead of targeting the popup elements themselves, I started tracing the network behavior behind them.&lt;br&gt;
During tiny windows before the console got wiped, I managed to capture the redirect domains responsible for the popup chains.&lt;br&gt;
That changed everything.&lt;/p&gt;

&lt;p&gt;Because once you identify the routing infrastructure the illusion collapses.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🔥 The Final Architecture&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I rebuilt the extension entirely around Chrome’s native browser APIs:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;declarativeNetRequest&lt;/li&gt;
&lt;li&gt;tabs&lt;/li&gt;
&lt;li&gt;background service workers&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;No more DOM fighting.&lt;br&gt;
No more chasing invisible overlays.&lt;br&gt;
No more playing inside their sandbox.&lt;br&gt;
The browser itself would now intercept requests before the scripts could fully execute.&lt;/p&gt;

&lt;p&gt;📦 manifest.json&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"manifest_version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Native Ad Network Guard"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Drops ad-network connections and instantly auto-closes leaked popups."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"declarativeNetRequest"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"tabs"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"host_permissions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;all_urls&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"background"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"service_worker"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"background.js"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;⚡ background.js&lt;br&gt;
&lt;/p&gt;

&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Domains responsible for popup routing,&lt;/span&gt;
&lt;span class="c1"&gt;// ad redirects, tracking, and anti-user behavior.&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;BLOCKED_DOMAINS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;xadsmart.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;adsco.re&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;


&lt;span class="c1"&gt;// ---------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;// 1. Create browser-level blocking rules&lt;/span&gt;
&lt;span class="c1"&gt;// ---------------------------------------------------&lt;/span&gt;

&lt;span class="c1"&gt;// Chrome's declarativeNetRequest API works by defining&lt;/span&gt;
&lt;span class="c1"&gt;// static rule objects that the browser engine enforces&lt;/span&gt;
&lt;span class="c1"&gt;// BEFORE requests fully execute.&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;BLOCKED_DOMAINS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;index&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;priority&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

    &lt;span class="c1"&gt;// Action to perform when matched.&lt;/span&gt;
    &lt;span class="c1"&gt;// In this case: completely block the request.&lt;/span&gt;
    &lt;span class="na"&gt;action&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;block&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;

    &lt;span class="c1"&gt;// Conditions that trigger the rule.&lt;/span&gt;
    &lt;span class="na"&gt;condition&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// Match any request containing this domain.&lt;/span&gt;
        &lt;span class="na"&gt;urlFilter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// Types of browser resources to intercept.&lt;/span&gt;
        &lt;span class="na"&gt;resourceTypes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
            &lt;span class="c1"&gt;// Entire page navigations&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;main_frame&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="c1"&gt;// Embedded iframes&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;sub_frame&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="c1"&gt;// External JavaScript files&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;script&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="c1"&gt;// AJAX/fetch/XHR requests&lt;/span&gt;
            &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;xmlhttprequest&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}));&lt;/span&gt;


&lt;span class="c1"&gt;// ---------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;// 2. Register rules when extension installs&lt;/span&gt;
&lt;span class="c1"&gt;// ---------------------------------------------------&lt;/span&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;runtime&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onInstalled&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;declarativeNetRequest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateDynamicRules&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="c1"&gt;// Remove previously existing rules&lt;/span&gt;
        &lt;span class="c1"&gt;// to avoid duplicates during reinstalls.&lt;/span&gt;
        &lt;span class="na"&gt;removeRuleIds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;

        &lt;span class="c1"&gt;// Inject the new blocking rules.&lt;/span&gt;
        &lt;span class="na"&gt;addRules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;


&lt;span class="c1"&gt;// ---------------------------------------------------&lt;/span&gt;
&lt;span class="c1"&gt;// 3. Instant popup execution kill-switch&lt;/span&gt;
&lt;span class="c1"&gt;// ---------------------------------------------------&lt;/span&gt;

&lt;span class="c1"&gt;// Even if a popup somehow bypasses the network block,&lt;/span&gt;
&lt;span class="c1"&gt;// this listener acts as a secondary defense layer.&lt;/span&gt;
&lt;span class="c1"&gt;// It watches every tab URL update in realtime.&lt;/span&gt;

&lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onUpdated&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addListener&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;changeInfo&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

    &lt;span class="c1"&gt;// Ignore updates that don't contain a URL.&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;changeInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

        &lt;span class="c1"&gt;// Normalize URL for safer comparisons.&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;changeInfo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toLowerCase&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// Check whether the tab matches&lt;/span&gt;
        &lt;span class="c1"&gt;// known popup patterns or redirect signatures.&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shouldKill&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;

            &lt;span class="c1"&gt;// Known blocked domains&lt;/span&gt;
            &lt;span class="nx"&gt;BLOCKED_DOMAINS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
                &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;domain&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;// Common ad-network query patterns&lt;/span&gt;
            &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zoneid=&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

            &lt;span class="c1"&gt;// Aggressive redirect handler pattern&lt;/span&gt;
            &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;afu.php&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="c1"&gt;// If detected:&lt;/span&gt;
        &lt;span class="c1"&gt;// instantly terminate the tab.&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shouldKill&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nx"&gt;chrome&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tabs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tabId&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;



&lt;p&gt;&lt;strong&gt;🚀 The Result&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The transformation was surreal.&lt;br&gt;
The site still attempts the same aggressive click-hijacking flow.&lt;br&gt;
Invisible overlays still spawn.&lt;br&gt;
Scripts still try redirect chains.&lt;br&gt;
But now?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;💀 The tabs die instantly.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;No chaos.&lt;br&gt;
No cleanup.&lt;br&gt;
No losing immersion every five seconds.&lt;br&gt;
Just a silent war happening underneath the browser while the video plays normally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;🧩 What This Actually Taught Me&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The interesting part wasn’t blocking ads.&lt;/p&gt;

&lt;p&gt;It was understanding how modern websites are willing to weaponize the browser itself:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Event hijacking&lt;/li&gt;
&lt;li&gt;Trusted-click exploitation&lt;/li&gt;
&lt;li&gt;Fingerprinting&lt;/li&gt;
&lt;li&gt;Anti-debugging&lt;/li&gt;
&lt;li&gt;Console wiping&lt;/li&gt;
&lt;li&gt;Behavioral analysis&lt;/li&gt;
&lt;li&gt;Redirect chaining&lt;/li&gt;
&lt;li&gt;Dynamic overlay injection&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;👨‍💻 Final Thought&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;This entire project started because I got annoyed clicking “Close Tab” fifteen times.&lt;br&gt;
It ended with me reverse-engineering popup infrastructure, anti-bot systems, and browser-level request interception pipelines.&lt;br&gt;
And honestly?&lt;br&gt;
That escalation path perfectly summarizes what I enjoy most about software engineering.&lt;/p&gt;

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

&lt;p&gt;And refusing to accept that a system is “unbeatable.”&lt;/p&gt;

</description>
      <category>programming</category>
      <category>chromeextensions</category>
      <category>cybersecurity</category>
      <category>browsers</category>
    </item>
  </channel>
</rss>
