<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0">
<channel>
<title>Coder Legion - Recent posts tagged npm</title>
<link>https://coderlegion.com/tag/npm</link>
<description>Powered by Question2Answer</description>
<item>
<title>Who Can Change Your npm Release Tags?</title>
<link>https://coderlegion.com/29397/who-can-change-your-npm-release-tags</link>
<description>&lt;h2 id=&quot;who-should-be-allowed-to-change-your-npm-release-tags-&quot;&gt;&lt;strong&gt;Who Should Be Allowed to Change Your npm Release Tags?&lt;/strong&gt;&lt;/h2&gt;
&lt;p&gt;Your CI/CD pipeline may already be able to &lt;strong&gt;publish an npm package&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;But here’s the more important question:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Should every workflow that can publish also be allowed to change &lt;code&gt;latest&lt;/code&gt;, &lt;code&gt;next&lt;/code&gt;, or &lt;code&gt;beta&lt;/code&gt; tags?&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;GitHub’s recent npm trusted publishing update makes this separation much clearer.&lt;/p&gt;
&lt;p&gt;Workflows can now receive &lt;strong&gt;explicit permission&lt;/strong&gt; to manage npm distribution tags using &lt;strong&gt;short-lived OIDC credentials&lt;/strong&gt; instead of keeping long-lived automation tokens around.&lt;/p&gt;
&lt;h3 id=&quot;why-this-matters&quot;&gt;Why this matters&lt;/h3&gt;
&lt;p&gt;Release tags can directly affect &lt;strong&gt;which version users install&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Changing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;latest
next
beta
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;may look like a small metadata change, but in practice it can influence production adoption, prerelease testing, and package distribution.&lt;/p&gt;
&lt;p&gt;That means tag management deserves its &lt;strong&gt;own permission boundary&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;what-development-teams-should-review&quot;&gt;What development teams should review&lt;/h3&gt;
&lt;p&gt;Before changing anything in production, I’d check these first:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Which workflow actually needs to manage release tags?&lt;/li&gt;
&lt;li&gt;Does that workflow really need broader publishing permissions too?&lt;/li&gt;
&lt;li&gt;Can the operation be handled through &lt;strong&gt;OIDC-based trusted publishing&lt;/strong&gt;?&lt;/li&gt;
&lt;li&gt;Have you tested tag updates before removing existing credentials?&lt;/li&gt;
&lt;li&gt;Are old automation tokens still required anywhere else?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A good principle here is:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Give automation only the permissions it actually needs.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The tag-management permission is &lt;strong&gt;off by default&lt;/strong&gt;, which is a sensible security choice.&lt;/p&gt;
&lt;p&gt;And importantly, moving to short-lived credentials does not remove the need for:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;release testing&lt;/li&gt;
&lt;li&gt;permission reviews&lt;/li&gt;
&lt;li&gt;CI/CD controls&lt;/li&gt;
&lt;li&gt;rollback planning&lt;/li&gt;
&lt;li&gt;software supply-chain security&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;discussion&quot;&gt;Discussion&lt;/h3&gt;
&lt;p&gt;I think this is a good example of where &lt;strong&gt;least privilege&lt;/strong&gt; becomes practical, not theoretical.&lt;/p&gt;
&lt;p&gt;Publishing a package and deciding which release becomes &lt;code&gt;latest&lt;/code&gt; are related actions—but they are not necessarily the same responsibility.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;How are you handling this in your npm workflows today?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Do you keep publishing and tag management in one workflow, or would you separate them?&lt;/p&gt;
&lt;p&gt; #NPM #GitHubActions #DevSecOps #CICD #OIDC #SoftwareEngineering #DevOps #SoftwareSupplyChain #PackageManagement #OpenSource #DeveloperTools #Automation #NodeJS #WaqasAhmad&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/29397/who-can-change-your-npm-release-tags</guid>
<pubDate>Thu, 01 Oct 2026 11:37:54 +0000</pubDate>
</item>
<item>
<title>@seosiri/developer-ui-kit — a zero-dependency React licensing guard for SaaS dashboards</title>
<link>https://coderlegion.com/28404/seosiri-developer-ui-kit-a-zero-dependency-react-licensing-guard-for-saas-dashboards</link>
<description>&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; &lt;code&gt;@seosiri/developer-ui-kit&lt;/code&gt; (v1.0.1, MIT, 0 dependencies) is a framework-agnostic React component library with a built-in Flexible Licensing Guard for gating PRO&lt;em&gt;/ENT&lt;/em&gt; features in SaaS dashboards. Works with Vite, Next.js, Electron, and Atlassian Forge out of the box.&lt;/p&gt;
&lt;h4 id=&quot;why-this-exists&quot;&gt;Why this exists&lt;/h4&gt;
&lt;p&gt;Every SaaS dashboard eventually needs to answer: &lt;em&gt;is this user allowed to see this feature?&lt;/em&gt; Usually that turns into a one-off component, duplicated across every internal tool a team ships. This package makes that component reusable and auditable instead of reinvented per project.&lt;/p&gt;
&lt;h4 id=&quot;how-it-works&quot;&gt;How it works&lt;/h4&gt;
&lt;p&gt;The &lt;code&gt;UniversalSaaSWidget&lt;/code&gt; component takes a license token, checks its prefix (&lt;code&gt;PRO_&lt;/code&gt; or &lt;code&gt;ENT_&lt;/code&gt;) either locally or against a backend gateway, and renders the corresponding tier — with a real-time telemetry state (verifying / verified / error) so the UI never sits in an ambiguous loading state.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install @seosiri/developer-ui-kit
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;import { UniversalSaaSWidget } from '@seosiri/developer-ui-kit';

&amp;lt;UniversalSaaSWidget
  productName=&quot;SEOSiri Analytics Panel&quot;
  productVersion=&quot;1.0.1&quot;
  accentColor=&quot;#0284c7&quot;
  theme=&quot;dark&quot;
  onProTask={() =&amp;gt; alert('Executing secure enterprise pipeline task...')}
/&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;what-s-actually-notable-here&quot;&gt;What's actually notable here&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;0 dependencies&lt;/strong&gt; — verifiable on the &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/@seosiri/developer-ui-kit&quot;&gt;npm page&lt;/a&gt; itself, not a marketing claim.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Framework-agnostic by construction&lt;/strong&gt; — no CSS provider, no context wrapper; it's pure React with encapsulated inline styles, which is why it compiles across Vite/Next.js/Electron/Forge without extra config.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MIT licensed&lt;/strong&gt;, source on &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/SEOSiri-Official/developer-ui-kit&quot;&gt;GitHub&lt;/a&gt; — open core, with the licensing guard controlling &lt;em&gt;feature access&lt;/em&gt;, not &lt;em&gt;code access&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;worth-discussing&quot;&gt;Worth discussing&lt;/h4&gt;
&lt;p&gt;If you're doing something similar — gating features by license tier in a React dashboard — how are you currently handling it? Custom hook, context provider, or something like this baked into a component? Curious what other teams have converged on.&lt;/p&gt;
&lt;p&gt;Full technical breakdown (with architecture diagram and a build-vs-buy comparison table): &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.seosiri.com/2026/09/developer-ui-kit.html&quot;&gt;https://www.seosiri.com/2026/09/developer-ui-kit.html&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/28404/seosiri-developer-ui-kit-a-zero-dependency-react-licensing-guard-for-saas-dashboards</guid>
<pubDate>Thu, 24 Sep 2026 03:38:24 +0000</pubDate>
</item>
<item>
<title>agentsmyth</title>
<link>https://coderlegion.com/27555/agentsmyth</link>
<description>&lt;p&gt;Picture the last thing an AI agent built for you. The diff landed. It probably worked. Now answer three questions without scrolling back: which requirement did line 40 satisfy, what did you decide against and why, and what evidence proved it worked.&lt;/p&gt;
&lt;p&gt;If you cannot, you did not lose the code. You lost everything around it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;agentsmyth exists to keep everything around it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;It is a portable AI engineering lifecycle, shipped as an npm package. You run one command, say one sentence to your agent, and the agent installs a gated seven-phase workflow into your repository. From then on, every non-trivial change moves through the same loop:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Requirements captured&lt;/li&gt;
&lt;li&gt;Plan written&lt;/li&gt;
&lt;li&gt;Code built&lt;/li&gt;
&lt;li&gt;Reviewed&lt;/li&gt;
&lt;li&gt;Tested&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Shipped,&lt;br&gt;
and reflected on, each phase leaving a&lt;/p&gt;
&lt;p&gt;&quot;durable file on disk&quot;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Those files are the point. &lt;br&gt;
Chat memory is smoke. It looks solid, then it is gone.&lt;/p&gt;
&lt;p&gt;An artifact is steel: cold, inspectable, and still there when a different agent picks up the work three weeks later.&lt;/p&gt;
&lt;p&gt;It is not a framework, not a linter, not an agent. It is a contract that the agent you already use agrees to follow.&lt;/p&gt;
&lt;p&gt;Drop it into any repo; your agent drives it and leaves durable artifacts behind, not chat smoke.&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/27555/agentsmyth</guid>
<pubDate>Thu, 17 Sep 2026 19:31:38 +0000</pubDate>
</item>
<item>
<title>I Kept Rebuilding PDF Features in Vue 3, So I Built vue-pdf-export</title>
<link>https://coderlegion.com/26179/i-kept-rebuilding-pdf-features-in-vue-3-so-i-built-vue-pdf-export</link>
<description>&lt;p&gt;Hey everyone 👋&lt;/p&gt;
&lt;p&gt;I recently released &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/vue-pdf-export&quot;&gt;&lt;code&gt;vue-pdf-export&lt;/code&gt;&lt;/a&gt;, an open-source Vue 3 + TypeScript library for generating PDFs directly in the browser.&lt;/p&gt;
&lt;p&gt;I built it after repeatedly needing the same PDF features across different Vue projects and wanting one reusable solution instead of rebuilding the same logic each time.&lt;/p&gt;
&lt;h3 id=&quot;what-it-supports&quot;&gt;What it supports&lt;/h3&gt;
&lt;p&gt;Some of the main features include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Canvas and selectable-text PDF rendering&lt;/li&gt;
&lt;li&gt;Automatic and manual pagination&lt;/li&gt;
&lt;li&gt;Preview, download, and browser print&lt;/li&gt;
&lt;li&gt;Headers and footers&lt;/li&gt;
&lt;li&gt;Watermarks and page targeting&lt;/li&gt;
&lt;li&gt;Clickable PDF links&lt;/li&gt;
&lt;li&gt;Password protection and permissions&lt;/li&gt;
&lt;li&gt;PDF metadata and compression&lt;/li&gt;
&lt;li&gt;Progress reporting and cancellation&lt;/li&gt;
&lt;li&gt;Large-document rendering&lt;/li&gt;
&lt;li&gt;Blob, ArrayBuffer, Base64, and PDF data URL outputs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;data-pdf-ignore&lt;/code&gt; for excluding UI elements from exports&lt;/li&gt;
&lt;li&gt;Export-only page and body styling&lt;/li&gt;
&lt;li&gt;Nuxt 3 and Nuxt 4 SSR-safe package imports&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The package also has compatibility coverage for npm, pnpm, Yarn, Yarn Plug'n'Play, and Bun.&lt;/p&gt;
&lt;h3 id=&quot;installation&quot;&gt;Installation&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install vue-pdf-export
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;basic-usage&quot;&gt;Basic usage&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;script setup lang=&quot;ts&quot;&amp;gt;
import { ref } from 'vue'
import { Html2Pdf } from 'vue3-pdf-export'
import 'vue3-pdf-export/style.css'

const pdf = ref&amp;lt;InstanceType&amp;lt;typeof Html2Pdf&amp;gt; | null&amp;gt;(null)

async function generate() {
  await pdf.value?.generatePdf()
}
&amp;lt;/script&amp;gt;

&amp;lt;template&amp;gt;
  &amp;lt;button @click=&quot;generate&quot;&amp;gt;
    Generate PDF
  &amp;lt;/button&amp;gt;

  &amp;lt;Html2Pdf
    ref=&quot;pdf&quot;
    filename=&quot;example.pdf&quot;
    :preview-modal=&quot;true&quot;
  &amp;gt;
    &amp;lt;template #pdf-content&amp;gt;
      &amp;lt;main&amp;gt;
        &amp;lt;h1&amp;gt;Hello from Vue&amp;lt;/h1&amp;gt;
        &amp;lt;p&amp;gt;This content will be exported to PDF.&amp;lt;/p&amp;gt;
      &amp;lt;/main&amp;gt;
    &amp;lt;/template&amp;gt;
  &amp;lt;/Html2Pdf&amp;gt;
&amp;lt;/template&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;canvas-or-selectable-text&quot;&gt;Canvas or selectable text&lt;/h3&gt;
&lt;p&gt;Use Canvas rendering when HTML/CSS fidelity is the priority:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;Html2Pdf render-mode=&quot;canvas&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or use selectable-text rendering when users need to select, search, and copy text from the generated PDF:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-vue&quot;&gt;&amp;lt;Html2Pdf render-mode=&quot;text&quot;&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes the package useful for both highly visual layouts and text-heavy documents such as reports, invoices, statements, and documentation.&lt;/p&gt;
&lt;p&gt;The idea is to let developers choose between maximum browser-style fidelity and real selectable PDF text depending on their use case.&lt;/p&gt;
&lt;h3 id=&quot;programmatic-output&quot;&gt;Programmatic output&lt;/h3&gt;
&lt;p&gt;The generated PDF can also be used directly in your application:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const blob = await pdf.value?.generatePdf()
const buffer = await pdf.value?.generatePdfArrayBuffer()
const base64 = await pdf.value?.generatePdfBase64()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is useful when you want to upload, store, preview, or pass the PDF to another API instead of immediately downloading it.&lt;/p&gt;
&lt;h3 id=&quot;nuxt-support&quot;&gt;Nuxt support&lt;/h3&gt;
&lt;p&gt;The package is designed so importing it does not crash Nuxt SSR.&lt;/p&gt;
&lt;p&gt;Actual PDF generation still happens in the browser, so a typical Nuxt integration can use &lt;code&gt;ClientOnly&lt;/code&gt; or lazy client-side rendering when needed.&lt;/p&gt;
&lt;h3 id=&quot;pagination&quot;&gt;Pagination&lt;/h3&gt;
&lt;p&gt;The package supports both automatic and manual pagination.&lt;/p&gt;
&lt;p&gt;For an explicit manual page break:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;div class=&quot;html2pdf__page-break&quot;&amp;gt;&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are also options for keeping elements together and avoiding unwanted page breaks.&lt;/p&gt;
&lt;h3 id=&quot;large-document-rendering&quot;&gt;Large-document rendering&lt;/h3&gt;
&lt;p&gt;Large PDFs were one of the areas I wanted to improve significantly.&lt;/p&gt;
&lt;p&gt;Canvas rendering can become expensive when a browser attempts to create one extremely tall canvas for an entire document.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;vue3-pdf-export&lt;/code&gt; can now render paginated Canvas documents incrementally, page by page, instead of depending on one giant full-document capture.&lt;/p&gt;
&lt;p&gt;In one benchmark fixture, the previous long-document capture surface was around:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;99.96 million pixels
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;while incremental rendering kept the maximum capture surface around:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;1.74 million pixels per page
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That is approximately a 98.26% reduction in peak capture-surface geometry for that fixture.&lt;/p&gt;
&lt;p&gt;This is specifically a rendering-surface measurement, not a claim that total browser memory always drops by the same percentage.&lt;/p&gt;
&lt;p&gt;The current regression suite includes documents across:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;10 / 25 / 40 / 50 / 60 / 75 / 100 pages
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;50-page and 100-page Canvas documents are part of the current verified test range.&lt;/p&gt;
&lt;p&gt;100 pages is not a hard package limit. Practical limits still depend on the document, images, browser memory, rendering mode, security settings, and the device being used.&lt;/p&gt;
&lt;h3 id=&quot;cancellation&quot;&gt;Cancellation&lt;/h3&gt;
&lt;p&gt;Long-running PDF generation can now be cancelled.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;pdf.value?.cancelGeneration()
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cancellation is cooperative and works across the generation pipeline.&lt;/p&gt;
&lt;p&gt;The library also tests recovery after cancellation, so a new PDF can be generated after an earlier operation was cancelled.&lt;/p&gt;
&lt;p&gt;Some already-running synchronous browser operations cannot always be stopped immediately, but the pipeline checks for cancellation between major stages.&lt;/p&gt;
&lt;h3 id=&quot;page-aware-progress&quot;&gt;Page-aware progress&lt;/h3&gt;
&lt;p&gt;Progress reporting now includes more than a simple percentage.&lt;/p&gt;
&lt;p&gt;The package can expose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;current progress&lt;/li&gt;
&lt;li&gt;current page&lt;/li&gt;
&lt;li&gt;total pages&lt;/li&gt;
&lt;li&gt;current processing stage&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Stages include:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;pagination
images
preparing
rendering
watermark
header
footer
metadata
serializing
complete
error
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This makes it possible to build UI such as:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Rendering page 18 of 50...
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;instead of showing an indefinite spinner.&lt;/p&gt;
&lt;h3 id=&quot;headers&quot;&gt;Headers&lt;/h3&gt;
&lt;p&gt;Headers can contain:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;plain text&lt;/li&gt;
&lt;li&gt;trusted HTML&lt;/li&gt;
&lt;li&gt;optional logos&lt;/li&gt;
&lt;li&gt;custom backgrounds and text colors&lt;/li&gt;
&lt;li&gt;configurable logo dimensions&lt;/li&gt;
&lt;li&gt;selected-page targeting&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A header can appear on:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;all pages&lt;/li&gt;
&lt;li&gt;the first page only&lt;/li&gt;
&lt;li&gt;the last page only&lt;/li&gt;
&lt;li&gt;every page except the first&lt;/li&gt;
&lt;li&gt;explicit page numbers&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Selective header targeting also uses page-aware spacing, so header space is reserved only where the header is actually rendered.&lt;/p&gt;
&lt;h3 id=&quot;footers&quot;&gt;Footers&lt;/h3&gt;
&lt;p&gt;Footers support:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HTML mode&lt;/li&gt;
&lt;li&gt;text mode&lt;/li&gt;
&lt;li&gt;trusted HTML content&lt;/li&gt;
&lt;li&gt;optional logos&lt;/li&gt;
&lt;li&gt;configurable logo dimensions&lt;/li&gt;
&lt;li&gt;background and text colors&lt;/li&gt;
&lt;li&gt;page-number templates&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For example:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;Page {n} of {total}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Header and footer logos preserve their aspect ratio instead of being stretched into a fixed box.&lt;/p&gt;
&lt;h4 id=&quot;footer-performance-improvements&quot;&gt;Footer performance improvements&lt;/h4&gt;
&lt;p&gt;Static HTML footers are now rendered once and reused across pages instead of being rasterized repeatedly.&lt;/p&gt;
&lt;p&gt;In project benchmark fixtures:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;HTML footer without logo
7.82s -&amp;gt; 1.43s

HTML footer with logo
11.81s -&amp;gt; 1.29s

Footer-heavy PDF size
~46.7 MB -&amp;gt; ~1.0 MB
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These numbers are specific to the benchmark fixtures and should not be treated as universal performance guarantees.&lt;/p&gt;
&lt;h3 id=&quot;repeated-image-optimization&quot;&gt;Repeated image optimization&lt;/h3&gt;
&lt;p&gt;Selectable-text rendering now caches repeated images within a single generation.&lt;/p&gt;
&lt;p&gt;If the same image is reused across many pages, the package can avoid repeatedly loading and encoding the same source.&lt;/p&gt;
&lt;p&gt;The cache is scoped to one PDF generation and is cleared afterward.&lt;/p&gt;
&lt;p&gt;This is especially useful for documents containing repeated logos, icons, product images, or other shared assets.&lt;/p&gt;
&lt;h3 id=&quot;resource-cleanup&quot;&gt;Resource cleanup&lt;/h3&gt;
&lt;p&gt;A lot of work also went into cleaning temporary resources earlier.&lt;/p&gt;
&lt;p&gt;The generation pipeline now handles cleanup of things such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;temporary cloned DOM nodes&lt;/li&gt;
&lt;li&gt;temporary canvases&lt;/li&gt;
&lt;li&gt;encoded image data&lt;/li&gt;
&lt;li&gt;per-generation image caches&lt;/li&gt;
&lt;li&gt;rendering state&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Canvas backing stores are also released when possible after they are no longer needed.&lt;/p&gt;
&lt;p&gt;Repeated-generation and isolation tests were added to help detect regressions in cleanup behavior.&lt;/p&gt;
&lt;h3 id=&quot;browser-responsiveness&quot;&gt;Browser responsiveness&lt;/h3&gt;
&lt;p&gt;Large PDF generation is naturally expensive, especially in Canvas mode.&lt;/p&gt;
&lt;p&gt;The pipeline now yields between selected expensive operations so the browser gets opportunities to process UI work while generation is running.&lt;/p&gt;
&lt;p&gt;Combined with cancellation and page-aware progress, this makes long-running generation easier to integrate into real applications.&lt;/p&gt;
&lt;h3 id=&quot;watermarks&quot;&gt;Watermarks&lt;/h3&gt;
&lt;p&gt;Watermarks can be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;text or image&lt;/li&gt;
&lt;li&gt;foreground or background&lt;/li&gt;
&lt;li&gt;single or repeated&lt;/li&gt;
&lt;li&gt;rotated&lt;/li&gt;
&lt;li&gt;positioned&lt;/li&gt;
&lt;li&gt;configured with opacity&lt;/li&gt;
&lt;li&gt;targeted to selected PDF pages&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;pdf-security&quot;&gt;PDF security&lt;/h3&gt;
&lt;p&gt;You can optionally generate password-protected PDFs with:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;user/open password&lt;/li&gt;
&lt;li&gt;owner password&lt;/li&gt;
&lt;li&gt;configurable permissions&lt;/li&gt;
&lt;li&gt;password construction from multiple password parts&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Supported permission controls include:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;print
modify
copy
annot-forms
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;metadata-and-compression&quot;&gt;Metadata and compression&lt;/h3&gt;
&lt;p&gt;PDF metadata can include:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;title&lt;/li&gt;
&lt;li&gt;author&lt;/li&gt;
&lt;li&gt;subject&lt;/li&gt;
&lt;li&gt;keywords&lt;/li&gt;
&lt;li&gt;creator&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;jsPDF stream compression can also be enabled when needed.&lt;/p&gt;
&lt;h3 id=&quot;clickable-links&quot;&gt;Clickable links&lt;/h3&gt;
&lt;p&gt;PDF link annotations are also supported, allowing links in generated documents to remain interactive instead of becoming plain visual text.&lt;/p&gt;
&lt;h3 id=&quot;safer-filenames&quot;&gt;Safer filenames&lt;/h3&gt;
&lt;p&gt;Generated filenames are normalized before download.&lt;/p&gt;
&lt;p&gt;This includes handling invalid filename characters, whitespace, reserved Windows names, trailing dots or spaces, and overly long filenames.&lt;/p&gt;
&lt;p&gt;Unicode filenames are still preserved.&lt;/p&gt;
&lt;h3 id=&quot;a-note-about-encrypted-raster-pdfs&quot;&gt;A note about encrypted raster PDFs&lt;/h3&gt;
&lt;p&gt;While profiling larger secured documents, I found that encrypted raster PDFs could retain significantly more browser memory than equivalent unencrypted PDFs.&lt;/p&gt;
&lt;p&gt;I reproduced the behavior directly with jsPDF outside the main &lt;code&gt;vue3-pdf-export&lt;/code&gt; rendering pipeline.&lt;/p&gt;
&lt;p&gt;That means a substantial part of this behavior appears to come from the underlying PDF library rather than from the Vue component itself.&lt;/p&gt;
&lt;p&gt;I opened an upstream jsPDF issue with the reproduction and benchmark details:&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/parallax/jsPDF/issues/4017&quot;&gt;https://github.com/parallax/jsPDF/issues/4017&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;For large secured documents, selectable-text mode can sometimes be a useful alternative because text-based PDFs are generally much smaller than equivalent rasterized documents.&lt;/p&gt;
&lt;p&gt;This is still workload-dependent, so applications generating large encrypted PDFs should test their own representative content.&lt;/p&gt;
&lt;h3 id=&quot;testing-and-performance-work&quot;&gt;Testing and performance work&lt;/h3&gt;
&lt;p&gt;The project now includes automated testing around:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;unit behavior&lt;/li&gt;
&lt;li&gt;Chromium&lt;/li&gt;
&lt;li&gt;Firefox&lt;/li&gt;
&lt;li&gt;WebKit&lt;/li&gt;
&lt;li&gt;large documents&lt;/li&gt;
&lt;li&gt;Canvas rendering&lt;/li&gt;
&lt;li&gt;selectable-text rendering&lt;/li&gt;
&lt;li&gt;cancellation and recovery&lt;/li&gt;
&lt;li&gt;image-heavy documents&lt;/li&gt;
&lt;li&gt;repeated images&lt;/li&gt;
&lt;li&gt;footer optimization&lt;/li&gt;
&lt;li&gt;browser cleanup&lt;/li&gt;
&lt;li&gt;security behavior&lt;/li&gt;
&lt;li&gt;memory measurements&lt;/li&gt;
&lt;li&gt;repeated generation&lt;/li&gt;
&lt;li&gt;stress testing&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The current unit suite contains:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;19 test files
106 tests
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are also dedicated benchmark and Playwright workflows for performance-heavy scenarios.&lt;/p&gt;
&lt;p&gt;For heavy PDF tests, I intentionally run browser work sequentially where appropriate so multiple workers do not distort memory measurements.&lt;/p&gt;
&lt;h3 id=&quot;why-i-built-a-vue-layer-instead-of-replacing-the-underlying-libraries&quot;&gt;Why I built a Vue layer instead of replacing the underlying libraries&lt;/h3&gt;
&lt;p&gt;The goal was never to pretend that &lt;code&gt;html2pdf.js&lt;/code&gt;, &lt;code&gt;html2canvas&lt;/code&gt;, or &lt;code&gt;jsPDF&lt;/code&gt; do not exist.&lt;/p&gt;
&lt;p&gt;Those libraries still provide important pieces of the Canvas rendering pipeline.&lt;/p&gt;
&lt;p&gt;The goal of &lt;code&gt;vue3-pdf-export&lt;/code&gt; is to provide a Vue-oriented layer around PDF generation so common production requirements do not need to be rebuilt for every application.&lt;/p&gt;
&lt;p&gt;At the same time, developers can still pass advanced html2pdf.js and jsPDF options when the higher-level API is not enough.&lt;/p&gt;
&lt;p&gt;The selectable-text renderer provides another option for cases where real PDF text matters more than exact Canvas-style browser rendering.&lt;/p&gt;
&lt;h3 id=&quot;current-release&quot;&gt;Current release&lt;/h3&gt;
&lt;p&gt;The current release is:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;vue-pdf-export v1.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The project has grown significantly since the original release, including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Canvas and selectable-text rendering modes&lt;/li&gt;
&lt;li&gt;incremental page-by-page Canvas generation&lt;/li&gt;
&lt;li&gt;large-document regression coverage&lt;/li&gt;
&lt;li&gt;cancellation and recovery&lt;/li&gt;
&lt;li&gt;current-page / total-page progress&lt;/li&gt;
&lt;li&gt;repeated-image caching&lt;/li&gt;
&lt;li&gt;footer rendering optimization&lt;/li&gt;
&lt;li&gt;earlier DOM and Canvas cleanup&lt;/li&gt;
&lt;li&gt;browser responsiveness improvements&lt;/li&gt;
&lt;li&gt;PDF security&lt;/li&gt;
&lt;li&gt;metadata and compression&lt;/li&gt;
&lt;li&gt;clickable links&lt;/li&gt;
&lt;li&gt;trusted HTML headers and footers&lt;/li&gt;
&lt;li&gt;logo support&lt;/li&gt;
&lt;li&gt;watermarks&lt;/li&gt;
&lt;li&gt;filename normalization&lt;/li&gt;
&lt;li&gt;expanded unit, browser, stress, and performance testing&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;try-it&quot;&gt;Try it&lt;/h3&gt;
&lt;p&gt;🧪 &lt;strong&gt;Live Playground&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://vue-pdf-export-playground.vercel.app/&quot;&gt;https://vue-pdf-export-playground.vercel.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📖 &lt;strong&gt;Documentation&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://vue-pdf-export-docs.vercel.app/&quot;&gt;https://vue-pdf-export-docs.vercel.app/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;📦 &lt;strong&gt;npm&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/vue-pdf-export&quot;&gt;https://www.npmjs.com/package/vue-pdf-export&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;💬 &lt;strong&gt;Feature requests &amp;amp; feedback&lt;/strong&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://vue3-pdf-export.canny.io/&quot;&gt;https://vue3-pdf-export.canny.io/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;I’d really appreciate feedback from Vue and Nuxt developers, especially around real-world PDF edge cases, API ergonomics, pagination, large documents, or features you would expect from a Vue PDF library.&lt;/p&gt;
&lt;p&gt;If you are building a Vue application that needs client-side PDF generation, I would love to hear about the kinds of documents you are generating and the edge cases you run into.&lt;/p&gt;
&lt;p&gt;The project is still evolving, and real-world feedback is one of the most useful inputs for deciding what to improve next. If you find a bug, performance cases, or have an idea for an API improvement, feel free to let me know.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/26179/i-kept-rebuilding-pdf-features-in-vue-3-so-i-built-vue-pdf-export</guid>
<pubDate>Fri, 04 Sep 2026 00:10:12 +0000</pubDate>
</item>
<item>
<title>SEOSiri IAIG: An open MCP API control plane to stop AI agents from breaking production infrastructur</title>
<link>https://coderlegion.com/25300/seosiri-iaig-open-mcp-control-plane-stop-agents-from-breaking-production-infrastructur</link>
<description>&lt;p&gt;Hey devs,&lt;/p&gt;
&lt;p&gt;As agentic workflows expand from sandboxed chat into production systems and IoT hardware, prompt engineering alone isn't a sufficient safety barrier. If an agent hallucinates a payload or triggers a cascading loop, traditional error-handling often catches it too late.&lt;/p&gt;
&lt;p&gt;We launched SEOSiri Industrial AI Gateway (IAIG), an MCP-compliant API gateway that acts as a deterministic boundary between agent decision-making and physical/API endpoints.&lt;/p&gt;
&lt;p&gt;Quick Specs:&lt;/p&gt;
&lt;p&gt;License: MIT (Open Source)&lt;br&gt;
Distribution: Live on npm&lt;br&gt;
Tools Included: 21 documented tools (iaig_firewall_inspect, iaig_budget_gate, iaig_queue_proposal_hmi, iaig_audit_ledger, etc.)&lt;br&gt;
Tier: Free tier available&lt;/p&gt;
&lt;p&gt;Check out the architecture overview and get API access here:&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.seosiri.com/2026/08/industrial-ai-api.html&quot;&gt;https://www.seosiri.com/2026/08/industrial-ai-api.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Feedback and tool requests are welcome!&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25300/seosiri-iaig-open-mcp-control-plane-stop-agents-from-breaking-production-infrastructur</guid>
<pubDate>Mon, 24 Aug 2026 14:17:52 +0000</pubDate>
</item>
<item>
<title>Understanding exec() in FSCSS — Two Completely Different Jobs</title>
<link>https://coderlegion.com/25158/understanding-exec-in-fscss-two-completely-different-jobs</link>
<description>&lt;h3 id=&quot;understanding-exec-in-fscss&quot;&gt;Understanding &lt;code&gt;exec()&lt;/code&gt; in FSCSS&lt;/h3&gt;
&lt;p&gt;In FSCSS, &lt;strong&gt;&lt;code&gt;exec()&lt;/code&gt;&lt;/strong&gt; serves two completely different purposes depending on where you write it:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Inside your stylesheet: as a utility / debugging function  &lt;/li&gt;
&lt;li&gt;In JavaScript / HTML: to load and compile the styles&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Let’s break both down clearly.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;1-inside-stylesheets-debugging-amp-logging&quot;&gt;1. Inside Stylesheets: Debugging &amp;amp; Logging&lt;/h3&gt;
&lt;p&gt;When used inside an &lt;code&gt;@event&lt;/code&gt; block or after processing an array, &lt;code&gt;exec()&lt;/code&gt; lets you run small JavaScript helpers directly from your CSS.&lt;br&gt;
Its main job here is &lt;strong&gt;printing debug information&lt;/strong&gt; to the browser console so you can check if your logic is working.&lt;/p&gt;
&lt;h4 id=&quot;console-logging&quot;&gt;Console Logging&lt;/h4&gt;
&lt;p&gt;You can pass flags like &lt;code&gt;_log&lt;/code&gt;, &lt;code&gt;_warn&lt;/code&gt;, &lt;code&gt;_info&lt;/code&gt;, or &lt;code&gt;_error&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;@event scaleSize(e) {
  if e &amp;gt;= 8 {
    return| num(8*2)em exec(_log, &quot;Scaling to 16em for optimal readability&quot;)
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;checking-array-data&quot;&gt;Checking Array Data&lt;/h4&gt;
&lt;p&gt;You can also inspect the current values inside an FSCSS array:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-css&quot;&gt;@arr fruits[ apple, orange, banana ]

/* Prints: apple orange banana straight to your browser console */
exec(_log, &quot;@arr.fruits&quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is extremely useful while building complex logic with &lt;code&gt;@event&lt;/code&gt;, &lt;code&gt;@arr&lt;/code&gt;, or calculated values.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;2-in-javascript-amp-imports-compilation-engine&quot;&gt;2. In JavaScript &amp;amp; Imports: Compilation Engine&lt;/h3&gt;
&lt;p&gt;Outside the stylesheet, &lt;code&gt;exec&lt;/code&gt; is the &lt;strong&gt;core engine&lt;/strong&gt; that compiles and runs FSCSS source code.&lt;/p&gt;
&lt;h4 id=&quot;cdn-initialization-html-&quot;&gt;CDN Initialization (HTML)&lt;/h4&gt;
&lt;p&gt;The compiler file loaded via CDN is named &lt;code&gt;exec.min.js&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-html&quot;&gt;&amp;lt;script src=&quot;https://cdn.jsdelivr.net/npm/fscss@1.1.25/exec.min.js&quot; defer&amp;gt;&amp;lt;/script&amp;gt;

&amp;lt;style&amp;gt;
  /* Tells the engine to execute the initialization for a module */
  @import(exec(_init circle-progress))
&amp;lt;/style&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;programmatic-javascript-usage&quot;&gt;Programmatic JavaScript Usage&lt;/h4&gt;
&lt;p&gt;You can also import and control the compiler directly from JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;import { exec } from &quot;https://cdn.jsdelivr.net/npm/fscss@1.1.25/e/xfscss.min.js&quot;;

new exec({
  type: &quot;URL&quot;,
  content: &quot;styles.fscss&quot;,
  onSuccess: &quot;Styles loaded successfully!&quot;,
  onError: &quot;Oops, style compilation failed!&quot;
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This gives you full control over loading, success messages, and error handling.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;quick-summary&quot;&gt;Quick Summary&lt;/h3&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Location              &lt;/th&gt;&lt;th&gt;Purpose                              &lt;/th&gt;&lt;th&gt;Common Use Case&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Inside &lt;code&gt;.fscss&lt;/code&gt;       &lt;/td&gt;&lt;td&gt;Debugging &amp;amp; console logging          &lt;/td&gt;&lt;td&gt;Check &lt;code&gt;@event&lt;/code&gt; logic or array values&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;In HTML / JavaScript  &lt;/td&gt;&lt;td&gt;Compile and run FSCSS                &lt;/td&gt;&lt;td&gt;Load styles or initialize modules&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Two different jobs. Same name.&lt;br&gt;
Once you know the context, &lt;code&gt;exec()&lt;/code&gt; becomes much clearer.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Useful links&lt;/strong&gt;  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Docs → &lt;a rel=&quot;nofollow&quot; href=&quot;https://fscss.devtem.org&quot;&gt;fscss.devtem.org&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;vscode extention &lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=Figsh.fscss&quot;&gt;FSCSS Support&lt;/a&gt; &lt;/li&gt;
&lt;li&gt;npm → &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/fscss&quot;&gt;npmjs.com/package/fscss&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
</description>
<category>Tutorials</category>
<guid isPermaLink="true">https://coderlegion.com/25158/understanding-exec-in-fscss-two-completely-different-jobs</guid>
<pubDate>Sat, 22 Aug 2026 22:45:11 +0000</pubDate>
</item>
<item>
<title>Developer Weekly Briefing — August 21, 2026</title>
<link>https://coderlegion.com/25085/developer-weekly-briefing-august-21-2026</link>
<description>&lt;p&gt;A busy week across tooling, supply chain security, and governance. Two stories have immediate action items attached. Here's what mattered.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;github-copilot-bets-on-model-choice-not-model-loyalty&quot;&gt;GitHub Copilot bets on model choice, not model loyalty&lt;/h3&gt;
&lt;p&gt;GitHub's latest Copilot update, covering the week of August 10, adds two new models — Kimi K3 across all plan tiers and MAI-Code-1.1-Flash with native image understanding — and enables per-turn model switching in VS Code 1.133. Developers can now move between Claude BYOK and built-in Copilot models mid-session without restarting or losing context.&lt;/p&gt;
&lt;p&gt;The direction is clear: GitHub is building Copilot as a multi-model orchestration layer, not a tool that wins by having the best single model. Agent Plugins 1.0 is now GA and works the same way across VS Code, Copilot CLI, the Copilot app, and the SDK — meaning internal tooling built for one surface no longer has to be rebuilt for another. JetBrains adds Copilot Memory for cross-session context retention and Ollama support for local model access.&lt;/p&gt;
&lt;p&gt;Copilot CLI got the most substantial updates: a /tasks command for subagent management, prompt queuing while agents are mid-task, headless autopilot combining --plan and --mode flags, and /rewind for undoing Copilot changes without touching git.&lt;/p&gt;
&lt;p&gt;The governance gap worth flagging: Copilot doesn't record which model contributed to which line of code. Per-turn model switching makes that gap wider, not smaller. By the time something breaks in production, the decision about which model wrote the relevant code is invisible in the build record. That's an audit and incident-response problem waiting to happen for any team that needs to reconstruct what happened and why.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/github-copilots-latest-update-bets-on-model-choice-not-model-loyalty/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;vs-code-decouples-ai-agents-from-the-editor-window&quot;&gt;VS Code decouples AI agents from the editor window&lt;/h3&gt;
&lt;p&gt;Microsoft separated AI agent sessions from the VS Code editor itself in the latest release, meaning agent workloads can run independently of whatever the developer has open in the editor. For teams running parallel agent tasks or background automation, this is a meaningful architectural change — agents no longer compete with active editing sessions for the same window context.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/microsoft-decouples-ai-agents-from-the-vs-code-editor-in-latest-release/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;claude-code-s-temporary-usage-boost-expires-tonight&quot;&gt;Claude Code's temporary usage boost expires tonight&lt;/h3&gt;
&lt;p&gt;If you're a Claude Code user on the temporary expanded usage tier, the boost ends tonight. This is worth checking before you kick off anything long-running this evening. Anthropic hasn't announced what the new baseline looks like post-expiry, so it's worth knowing where you stand before the session caps reset.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/claude-codes-temporary-usage-boost-expires-tonight-heres-what-actually-changes/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;npm-v12-shuts-down-a-popular-malware-trick-the-threat-isn-t-gone&quot;&gt;npm v12 shuts down a popular malware trick — the threat isn't gone&lt;/h3&gt;
&lt;p&gt;npm v12 is now generally available, and the install script changes from July are in effect. The specific malware technique it blocks: postinstall scripts that run automatically on package installation, which have been a primary vector for supply chain attacks across the JavaScript ecosystem. That particular trick is now off by default.&lt;/p&gt;
&lt;p&gt;But it's worth being clear about what v12 doesn't fix. Attackers will adapt. The next wave of npm-based attacks will use whatever path npm v12 left open. The value of the upgrade is closing a well-documented, heavily exploited vector — not ending the supply chain threat category.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/npm-v12-shuts-down-a-popular-malware-trick-but-the-threat-isnt-going-away/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;langchain-s-dcode-isn-t-new-its-governance-play-for-sensitive-code-is&quot;&gt;LangChain's dcode isn't new — its governance play for sensitive code is&lt;/h3&gt;
&lt;p&gt;LangChain has been shipping dcode for a while. What's new is a governance layer that lets organizations enforce policies around what happens when AI agents interact with sensitive code — access controls, audit trails, and restrictions on what can be passed to external models. For teams that've held back on agentic coding tools specifically because of sensitive IP concerns, this addresses the gap more directly than most tools in the space.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/langchains-dcode-isnt-new-its-governance-play-for-sensitive-code-is/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;github-sharpens-codeql-for-actions-workflows-and-modern-javascript&quot;&gt;GitHub sharpens CodeQL for Actions workflows and modern JavaScript&lt;/h3&gt;
&lt;p&gt;CodeQL now covers GitHub Actions workflow files and modern JavaScript patterns that the previous ruleset wasn't catching. If you run CodeQL as part of your CI/CD security scanning, the coverage improvements apply automatically. Worth a scan run after the update to see what surfaces.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/github-sharpens-codeqls-eye-on-actions-workflows-and-modern-javascript/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;tessl-is-betting-on-standards-to-close-the-ai-code-review-gap&quot;&gt;Tessl is betting on standards to close the AI code review gap&lt;/h3&gt;
&lt;p&gt;AI agents write code faster than anyone can review it — that's the established problem. Tessl's argument is that the solution isn't better tooling, it's standards: a shared specification layer that defines what AI-generated code is expected to do, so review can be partially automated against a known target rather than always requiring a human to reconstruct intent from the output. An interesting architectural bet worth tracking.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/ai-agents-are-writing-code-faster-than-anyone-can-review-it-tessl-is-betting-on-standards-to-close-that-gap/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;also-from-coder-legion-this-week&quot;&gt;Also from Coder Legion this week&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;One retiring developer nearly broke a retailer's core system.&lt;/strong&gt; Hypercubic built AI to catch that kind of undocumented institutional knowledge before it walks out the door. &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/24851/one-retiring-developer-nearly-broke-this-retailers-core-system-hypercubic-built-ai-stop&quot;&gt;Read more&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Harness GM Rahul Sood: most &quot;AI remediation&quot; claims don't actually work.&lt;/strong&gt; A candid breakdown of where vendor promises outrun what the tools can actually deliver — and what real remediation looks like in production. &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/24833/harness-gm-rahul-sood-most-ai-remediation-claims-dont-actually-work-heres-what-does&quot;&gt;Read more&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Crogl's Monzy Merza: the SOC agent that refuses to say it can replace you.&lt;/strong&gt; An honest conversation about where AI agents belong in security operations and where they don't. &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/24945/crogls-monzy-merza-the-soc-agent-that-refuses-to-say-it-can-replace-you&quot;&gt;Read more&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Blumira's Hearth turns security investigations from hours into minutes.&lt;/strong&gt; AI reasoning applied to the investigation workflow, not just the alert triage layer. &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/24927/blumiras-hearth-turns-security-investigations-from-hours-into-minutes-with-ai-reasoning&quot;&gt;Read more&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Detectify's Rickard Carlsson: attackers don't have new tricks, they have a faster clock.&lt;/strong&gt; A grounded take on why the threat landscape feels more urgent even when the techniques haven't fundamentally changed. &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/24781/detectifys-rickard-carlsson-attackers-dont-have-new-tricks-they-have-a-faster-clock&quot;&gt;Read more&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS is betting $500M that students will learn cloud by building.&lt;/strong&gt; The payoff is still unproven, but the investment signals where Amazon thinks developer education is heading. &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/25032/aws-is-betting-500m-that-students-will-learn-cloud-by-building-the-payoff-still-unproven&quot;&gt;Read more&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Windows Server still has gravity. Kubernetes hasn't escaped its pull.&lt;/strong&gt; A look at why the migration away from Windows Server in enterprise environments keeps moving slower than the cloud-native narrative suggests. &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/25030/windows-server-still-has-gravity-kubernetes-hasnt-escaped-its-pull&quot;&gt;Read more&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The through-line this week: the tooling is getting faster and more capable across the board — model switching, agent decoupling, automated code standards — and the governance layer is still catching up. The Copilot model attribution gap and the LangChain sensitive code controls are both responses to the same underlying tension: moving fast with AI tools is getting easier, and knowing exactly what happened and why is getting harder.&lt;/p&gt;
&lt;p&gt;See you next Friday.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Developer Weekly Briefing is published every Friday on &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com&quot;&gt;Coder Legion&lt;/a&gt;. Written by &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/user/Tom+Smith&quot;&gt;Tom Smith&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/25085/developer-weekly-briefing-august-21-2026</guid>
<pubDate>Fri, 21 Aug 2026 12:36:02 +0000</pubDate>
</item>
<item>
<title>The evolution of Shai-Hulud-style worms</title>
<link>https://coderlegion.com/24620/the-evolution-of-shai-hulud-style-worms</link>
<description>&lt;p&gt;The software supply chain landscape changed forever in early-to-mid 2026. What began as a series of isolated credential-theft incidents quickly mutated into a highly coordinated, multi-registry, self-propagating worm campaign.&lt;/p&gt;
&lt;p&gt;By abusing trusted relationships, hijacking CI workflows, poisoning build caches, and even embedding execution scripts inside compiled native binaries, the threat actors behind this campaign (tracked as TeamPCP) bypassed traditional static scanner defences.&lt;/p&gt;
&lt;p&gt;As of the time of writing, this is a somewhat complete, chronological teardown of the three distinct waves of this campaign: Shai-Hulud, Hades, and Miasma.&lt;/p&gt;
&lt;h3 id=&quot;shai-hulud-mini-shai-hulud-and-similar-worms&quot;&gt;Shai-Hulud, Mini Shai-Hulud and similar worms&lt;/h3&gt;
&lt;p&gt;The Shai-Hulud worm lineage first surfaced in late 2025 as a direct credential stealer targeting the npm ecosystem. However, on May 11th, 2026, a highly automated, modernised variant called Mini Shai-Hulud struck with vastly expanded scope. While it heavily impacted npm, compromising more than 170 packages across the TanStack, Mistral AI, UiPath, and OpenSearch ecosystems, the worm simultaneously breached the broader software supply chain, orchestrating coordinated attacks against PyPI, RubyGems, Docker Hub, and even malicious extensions within VS Code.&lt;/p&gt;
&lt;p&gt;Unlike the earliest waves of Shai-Hulud that relied specifically on stolen static developer credentials, Mini Shai-Hulud (believed to have been created by a different group to TeamPCP), gained entry to these diverse environments via entirely new approaches, like pull request workflow misconfigurations in GitHub Actions (as seen in the TanStack compromise).&lt;/p&gt;
&lt;p&gt;The basic workflow looked like this. An attacker submitted a pull request from a fork. This triggers a workflow that grants write access to the base repository's cache. The attacker proceeds to poison the cache with malicious payloads. Hours later, a legitimate maintainer merges a commit, triggering the standard release workflow. And this all concludes with the release run pulling the poisoned cache and executing the malicious payload invisibly.&lt;/p&gt;
&lt;p&gt;Rather than exposing static tokens, the worm scraped live tokens directly from the GitHub Actions runner’s active memory (the &lt;code&gt;Runner.Worker&lt;/code&gt; process via &lt;code&gt;/proc/&amp;lt;pid&amp;gt;/mem&lt;/code&gt;). Depending on the target ecosystem, it then exchanged these temporary workflow credentials for registry-specific publish tokens, using OIDC token exchange endpoints for npm and PyPI, or harvesting raw API keys for RubyGems and container registries like Docker Hub.&lt;/p&gt;
&lt;p&gt;This allowed the published packages, container images, and extensions to carry valid SLSA Build track provenance signatures. To defenders, the updates appeared completely legitimate and officially attested across all affected registries. It’s the equivalent of deepfakes for the software supply chain.&lt;/p&gt;
&lt;p&gt;Perhaps the most malicious aspect of this multi-registry wave was its defence mechanism. Once inside a developer's local machine or build server (whether pulled via a Python pip install, a Ruby gem, an npm package, or a VS Code extension) the payload established a background daemon named &lt;code&gt;gh-token-monitor&lt;/code&gt;. This daemon monitored whether its stolen tokens had been rotated or revoked, acting as a dead man’s switch. If a defender revoked the token, the daemon immediately triggered a local script that executed &lt;code&gt;rm -rf ~/&lt;/code&gt; and &lt;code&gt;rm -rf ~/Documents&lt;/code&gt;, wiping the victim's entire home directory to eliminate forensic traces and punish the developer.&lt;/p&gt;
&lt;h3 id=&quot;miasma&quot;&gt;Miasma&lt;/h3&gt;
&lt;p&gt;Discovered on June 4th, 2026, Miasma represents the third evolution of the Shai-Hulud family of worm attacks, compromising hundreds of npm components (including packages within the &lt;code&gt;@redhat-cloud-services&lt;/code&gt; and &lt;code&gt;@asyncapi&lt;/code&gt; namespaces).&lt;/p&gt;
&lt;p&gt;In this workflow, a simple npm install forces a node-gyp rebuild which executes a binding.gyp command that drops the Miasma payload. Again, the tactics and techniques of this Shai-Hulud worm have changed multiple times so far this year. Most security scanners and manual reviewers focus closely on scripts blocks in &lt;code&gt;package.json&lt;/code&gt; (watching for &lt;code&gt;preinstall&lt;/code&gt; or &lt;code&gt;postinstall&lt;/code&gt; triggers).&lt;/p&gt;
&lt;p&gt;Miasma bypassed this completely by abusing &lt;code&gt;binding.gyp&lt;/code&gt;. The &lt;code&gt;binding.gyp&lt;/code&gt;, for context, is a legitimate config file used to compile native C++ Node.js add-ons. When a package containing this file is installed, the npm client automatically spawns node-gyp rebuild. The attackers embedded direct command execution wrappers inside the &lt;code&gt;binding.gyp&lt;/code&gt; target configs. The system executes the malicious payload during compilation, completely avoiding the need for suspicious &lt;code&gt;package.json&lt;/code&gt; lifecycle scripts.&lt;/p&gt;
&lt;p&gt;Miasma focused heavily on capturing cloud identities instead of static keys, implementing collectors specifically designed to steal active GCP and Azure SSO credentials. Furthermore, if the infected CI container had access to the host's Docker socket (&lt;code&gt;/var/run/docker.sock&lt;/code&gt;), the malware would’ve performed a Docker escape. It identifies and terminates security monitoring containers (such as Step Security’s &lt;code&gt;harden-runner&lt;/code&gt;), creating a privileged Alpine container to write passwordless sudo access to the host's &lt;code&gt;/etc/sudoers.d&lt;/code&gt;, and altered local DNS resolution to block telemetry reporting domains.&lt;/p&gt;
&lt;h3 id=&quot;hades&quot;&gt;Hades&lt;/h3&gt;
&lt;p&gt;Discovered on June 8th, 2026, straight after the Miasma variant, the campaign expanded to the Python ecosystem, targeting critical academic genomics and phenotype analysis packages on PyPI (&lt;code&gt;ensmallen&lt;/code&gt;, &lt;code&gt;embiggen&lt;/code&gt;, &lt;code&gt;pyphetools&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;In this attack, a simple Python Import loads a .so extension which executes embedded javascript. The embedded javascript code executes a final Bun payload which exfiltrates to a GitHub endpoint.&lt;/p&gt;
&lt;p&gt;In a major evasion upgrade, the attackers did not modify any Python source files. Instead, they embedded the malicious JavaScript loader inside the compiled Rust/C++ binary extension (&lt;code&gt;.abi3.so&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;When a researcher imports the package via Python (&lt;code&gt;import ensmallen&lt;/code&gt;), it triggers &lt;code&gt;dlopen()&lt;/code&gt; on the trojanised &lt;code&gt;.so&lt;/code&gt; file. The shared object discreetly initialises and runs an embedded JavaScript engine to execute the payload. This makes the new attack completely invisible to code-reviewers looking at Python source files.&lt;/p&gt;
&lt;p&gt;The payload's primary file, &lt;code&gt;_index.js&lt;/code&gt;, contains a massive block of comment text representing a fake LLM jailbreak prompt. The decoy simulated classified briefings, asking for technical steps on weaponising biological agents and designing nuclear devices. This was a deliberate attempt to pollute static analysers and AI-assisted tooling, causing them to choke, flag false positives on safety boundaries, or even worse, misclassify the file as a prompt-injection experiment rather than a credential-stealing worm.&lt;/p&gt;
&lt;p&gt;Instead of a standard server, the Hades variant registered dozens of GitHub repositories using the compromise-controlled user account &lt;code&gt;felixEvora&lt;/code&gt;. The repositories used underworld-themed naming conventions (like &lt;code&gt;lethean-tartarus-61322&lt;/code&gt; and &lt;code&gt;abyssal-acheron-97481&lt;/code&gt;) and were described as &lt;strong&gt;Hades – The End for the Damned&lt;/strong&gt;. The exfiltrated data was stored in an encrypted envelope format, signed with GPG keys.&lt;/p&gt;
&lt;h3 id=&quot;keyv-and-cachable-packages-compromised&quot;&gt;keyv and cachable packages compromised&lt;/h3&gt;
&lt;p&gt;In August 2026, the open-sourced Mini Shai-Hulud codebase was weaponised in one of the most widespread multi-registry campaigns to date. The outbreak began with the compromise of npm maintainer &lt;strong&gt;Jared Wray&lt;/strong&gt;'s account. This was the primary developer behind popular, heavily downloaded packages like keyv and cachable.&lt;/p&gt;
&lt;p&gt;From this single entry point, the worm exponentially expanded into over 444 packages across 2,000+ published releases, hit 100 packages in the ServiceTitan ecosystem alone, and aggressively turned every infected machine into an automated propagation node.&lt;/p&gt;
&lt;p&gt;The execution chain relies on the preinstall hook inside the package.json (addressed in npm v.12) to trigger a setup.mjs dropper. To evade standard JavaScript static analysis tuned for &lt;code&gt;Node.js&lt;/code&gt; patterns, the &lt;code&gt;setup.mjs&lt;/code&gt; file detected the host architecture, downloaded a bundled &lt;code&gt;Bun 1.3.13&lt;/code&gt; runtime, and proceeded to execute a massive, heavily obfuscated payload named &lt;code&gt;math_init.js&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Rather than stopping at passive credential dumping, this variant actively probed and harvested deeper infrastructure credentials: It actively queried AWS IMDSv2 and ECS endpoints, enumerated AWS SecretsManager paths, read mounted Kubernetes ServiceAccount tokens, and extracted HashiCorp Vault secrets across KV v1 &amp;amp; v2 mounts. It even hunted local crypto wallets like bitcoin wallet files. It was observed that exfiltration calls were routed to a dynamic domain endpoint. Crucially, if the server returned an execute payload in its JSON response, the worm immediately executed it using eval, giving the threat actors live remote code execution on infected dev machines and CI runners. Finally, if the primary C2 server failed, the worm fell back to using stolen GitHub tokens to automatically create public GitHub repos containing base64-encoded stolen data, tagging the repo descriptions with a distinctive campaign marker. This time &lt;strong&gt;Shai-Hulud: Here We Go Again&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;To ensure propagation, the worm scraped npm creds or even exchanged GitHub Actions OIDC tokens to auto-publish patch updates of packages the maintainer controlled. It inserted &lt;code&gt;@opensearch/setup&lt;/code&gt; as an optional dependency, allowing the payload to silently cascade into downstream installs without causing build failures. Furthermore, it even expanded on the infection vectors directly inside dev workflows by writing malicious GitHub Actions workflows disguised with the name &quot;&lt;strong&gt;Run Copilot&lt;/strong&gt;&quot;, as well as altering AI-related dev tooling via poisoned &lt;code&gt;.claude&lt;/code&gt; and &lt;code&gt;.vscode&lt;/code&gt; config files.&lt;/p&gt;
&lt;h3 id=&quot;shai-hulud-style-worms-will-continue-to-change-shape&quot;&gt;Shai-Hulud-style worms will continue to change shape&lt;/h3&gt;
&lt;p&gt;The open-sourcing or leaking of a complex piece of malware like the Shai-Hulud worm typically triggers a significant shift in the threat landscape. When a sophisticated framework (originally tightly controlled by an advanced group like TeamPCP) becomes publicly accessible, it lowers the barrier to entry for a much wider, more disorganised pool of actors. Independent operators, smaller cybercriminal syndicates, and even security researchers looking to understand its mechanics can dissect the original codebase. This democratisation of advanced tech means that features previously requiring high-level engineering, such as automated supply-chain propagation or complex credential harvesting, become plug-and-play modules for anyone with basic development skills.&lt;/p&gt;
&lt;p&gt;Without a centralised command structure like TeamPCP directing its deployment, the evolution of Shai-Hulud has naturally fragmented into highly specialised or experimental variants (such as &lt;code&gt;IronWorm&lt;/code&gt;). Newer threat actors are combining Shai-Hulud’s core self-propagating mechanisms with different payloads or evasion techniques tailored to their specific objectives. For instance, some variants strip away the heavier components to create lightweight stealth tools, while others swap out the exfiltration methods to target different developer ecosystems beyond npm or GitHub, such as PyPI and Cargo. This fragmentation complicates defense strategies, as defenders can no longer rely on a single set of behavioural signatures or specific infrastructure indicators associated with the original group.&lt;/p&gt;
&lt;p&gt;Furthermore, this post-TeamPCP era accelerated the timeline of dual-use engineering, where threat actors repurpose and optimise techniques from Shai-Hulud in unexpected ways (as seen in the &lt;strong&gt;Suno breach&lt;/strong&gt;). Independent devs often modify the codebase to patch flaws left by the original authors (such as fixing loud fallback routines or refining how the worm handles OIDC token exchanges). Because these new variants are built and deployed by completely unrelated clusters of users, the attribution of attacks becomes incredibly muddy. The signature architecture of Shai-Hulud is no longer a reliable indicator of who is attacking, but rather a reflection of a shared, open-source repository of commodity supply-chain threats. This is glaringly the case with alleged supply chain competitions organised by BreachForums.&lt;/p&gt;
&lt;h3 id=&quot;quick-recap-of-what-you-ve-read&quot;&gt;Quick recap of what you’ve read&lt;/h3&gt;
&lt;p&gt;If the evolution of Shai-Hulud proves anything, it's that attackers are exceptionally creative and relying purely on reactive remediation is a losing strategy. When malware can pass SLSA attestations, bypass package.json inspection via native builds, and hide inside compiled binaries, you simply cannot predict every vector. It only takes one compromised token to trigger a massive downstream disaster. While rapid incident response remains essential, putting heavy effort into proactive preventions, such as universal package cooldown policies and strict pipeline boundaries is the only way to neutralise these threats before they execute. Stopping the attack at the gate will always be easier than cleaning up the damage after a worm gets through.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24620/the-evolution-of-shai-hulud-style-worms</guid>
<pubDate>Fri, 14 Aug 2026 08:32:08 +0000</pubDate>
</item>
<item>
<title>🚀 Built an MCP Developer Ecosystem at SEOSiri</title>
<link>https://coderlegion.com/24444/built-an-mcp-developer-ecosystem-at-seosiri</link>
<description>&lt;p&gt;I’ve been working on the SEOSiri MCP Suite—an evolving infrastructure layer for developers building with AI agents.&lt;/p&gt;
&lt;p&gt;Current architecture:&lt;/p&gt;
&lt;p&gt;⚙️ 16 MCP Servers&lt;br&gt;
🧠 163 AI Tools&lt;br&gt;
🌐 11 Edge Gateways&lt;br&gt;
🗺️ Interactive MCP Topology&lt;br&gt;
📦 Developer packages &amp;amp; integrations&lt;/p&gt;
&lt;p&gt;The goal isn’t simply to create more AI tools.&lt;/p&gt;
&lt;p&gt;It’s to build an ecosystem where AI agents can discover, connect with, and work through real tools and services.&lt;/p&gt;
&lt;p&gt;I’d especially love feedback from developers working with MCP, AI agents, LLM applications, and developer infrastructure.&lt;/p&gt;
&lt;p&gt;🔗 Explore the architecture:&lt;br&gt;
SEOSiri Developer Portal- &lt;a rel=&quot;nofollow&quot; href=&quot;https://developers.seosiri.com/&quot;&gt;https://developers.seosiri.com/&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;Research → Build → Connect → Verify.&lt;/p&gt;
&lt;h2 id=&quot;mcp-modelcontextprotocol-aiagents-llm-aiengineering-developertools-aiinfrastructure-softwarearchitecture-seosiri&quot;&gt;MCP #ModelContextProtocol #AIAgents #LLM #AIEngineering #DeveloperTools #AIInfrastructure #SoftwareArchitecture #SEOSiri&lt;/h2&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/24444/built-an-mcp-developer-ecosystem-at-seosiri</guid>
<pubDate>Tue, 11 Aug 2026 13:12:11 +0000</pubDate>
</item>
<item>
<title>Giving an AI Agent a Benchmark Tool It Can Install and Actually Trust</title>
<link>https://coderlegion.com/23699/giving-an-ai-agent-a-benchmark-tool-it-can-install-and-actually-trust</link>
<description>&lt;h4 id=&quot;part-2-of-2-the-npm-to-uv-to-pypi-bridge-that-closes-the-installation-gap-and-the-adversarial-re-verification-discipline-that-makes-the-output-worth-acting-on-autonomously&quot;&gt;Part 2 of 2: the npm-to-uv-to-PyPI bridge that closes the installation gap, and the adversarial re-verification discipline that makes the output worth acting on autonomously&lt;/h4&gt;
&lt;p&gt;&lt;em&gt;Co-authored by &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul&quot;&gt;Rudrendu Paul&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Sourav-nandy-ai&quot;&gt;Sourav Nandy&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul/memtrust&quot;&gt;github.com/RudrenduPaul/memtrust&lt;/a&gt;, an independent, reproducible benchmark harness for agent-memory backends. &lt;code&gt;pip install memtrust-cli&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Quick summary:&lt;/strong&gt; &lt;code&gt;memtrust-cli&lt;/code&gt; is now on npm. &lt;code&gt;npx memtrust-cli&lt;/code&gt; runs a Python benchmark tool automatically using &lt;code&gt;uv&lt;/code&gt;, with no Python toolchain required. Below is the full bridge design, a concrete temporal-KG boundary bug the eval caught, and the verification discipline that makes the output worth acting on autonomously.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;In &lt;a rel=&quot;nofollow&quot; href=&quot;https://dev.to/rudrendu_paul/giving-an-ai-agent-a-benchmark-tool-it-can-install-and-actually-trust-25de&quot;&gt;Part 1&lt;/a&gt; of this series, we found our own MemPalace adapter calling a fabricated API method. Every result it had ever reported for that backend was fiction. Closing that gap meant an adversarial, from-scratch re-verification of every adapter in the project. That story stands on its own, and this one picks up right after it to answer a related question: when an autonomous agent runs this tool, what does &quot;trustworthy&quot; actually mean?&lt;/p&gt;
&lt;p&gt;Here's the real command surface memtrust ships today. This is the output an agent or a CI script parses.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/RudrenduPaul/memtrust/main/docs/assets/dev-to-demos/demo-3-cli-surface.gif&quot; alt=&quot;Terminal recording of `memtrust run --help`, showing the real flag list: --backends, --eval, --output, --locomo-dataset-path, and --sign&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Every flag shown here is real, pulled straight from &lt;code&gt;memtrust run --help&lt;/code&gt;. This GIF shows the live output.&lt;/em&gt;&lt;/p&gt;
&lt;h3 id=&quot;defining-agent-native-requirements&quot;&gt;Defining &quot;agent-native&quot; requirements&lt;/h3&gt;
&lt;p&gt;&quot;Agent-native&quot; gets used loosely enough that it needs a strict definition. A tool that's actually agent-native has to satisfy two conditions at the same time:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Installable automatically.&lt;/strong&gt; An agent or a CI job needs to go from &quot;nothing installed&quot; to &quot;tool running&quot; using a single programmatically constructed command, and it needs to run on whatever runtime the environment already has.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Machine-legible.&lt;/strong&gt; Every response has to be structured data a program can parse and act on directly.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Most developer tools, including memtrust's earlier releases, miss both marks. Running &lt;code&gt;pip install memtrust-cli&lt;/code&gt; assumes the system already has a specific Python version provisioned, a virtual environment configured, and a writable path for &lt;code&gt;pip&lt;/code&gt;. A CI runner or an agent sandbox shipping only Node hits a dead end there. Early memtrust CLI output also targeted human readers only. We printed colored tables, progress bars, and summary lines.&lt;/p&gt;
&lt;p&gt;The npm side of this project closes the installation gap. The rest of this article focuses on the legibility problem, which turned out to be the harder challenge of the two.&lt;/p&gt;
&lt;h3 id=&quot;live-on-both-registries&quot;&gt;Live on both registries&lt;/h3&gt;
&lt;p&gt;You can install &lt;code&gt;memtrust-cli&lt;/code&gt; via &lt;code&gt;npx memtrust-cli&lt;/code&gt; right now. It's on the npm registry. Running &lt;code&gt;npm view memtrust-cli version&lt;/code&gt; against the registry today returns &lt;code&gt;0.3.3&lt;/code&gt;, which matches the version on PyPI.&lt;/p&gt;
&lt;p&gt;We shipped a complete, tested npm package at &lt;code&gt;npm/memtrust-cli/&lt;/code&gt; in the repo alongside six per-platform companion packages (&lt;code&gt;@memtrust-cli/darwin-arm64&lt;/code&gt;, &lt;code&gt;@memtrust-cli/darwin-x64&lt;/code&gt;, &lt;code&gt;@memtrust-cli/linux-arm64&lt;/code&gt;, &lt;code&gt;@memtrust-cli/linux-x64&lt;/code&gt;, &lt;code&gt;@memtrust-cli/win32-arm64&lt;/code&gt;, &lt;code&gt;@memtrust-cli/win32-x64&lt;/code&gt;). The publish process followed a strict dry-run discipline: &lt;code&gt;npm pack --dry-run&lt;/code&gt; produced a correct tarball, then &lt;code&gt;npm publish --dry-run&lt;/code&gt; walked through the whole publish flow with registry auth included, and only after that did the real &lt;code&gt;npm publish&lt;/code&gt; run. Running a dry run before the real thing is the same verification habit we apply to eval claims. We just applied it to a release instead.&lt;/p&gt;
&lt;p&gt;The live path on PyPI is &lt;code&gt;pip install memtrust-cli&lt;/code&gt;, version 0.3.3, confirmed against PyPI's own API on 2026-07-21, the date of the npm publish. A mirror package named &lt;code&gt;memtrust&lt;/code&gt; currently sits at the same version for anyone who reaches for that shorter name, kept in sync by a manual release step rather than an automated one. Every example below works today through either &lt;code&gt;pip&lt;/code&gt; or &lt;code&gt;npx&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Here's the full implementation of &lt;code&gt;bin/memtrust.js&lt;/code&gt;. The entire design fits in well under a hundred lines:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;// Pinned to this npm package's own version, not left floating: &quot;uv tool run
// --from memtrust&quot; with no version qualifier always fetches whatever is
// currently newest on PyPI. Pinning to this package's own version keeps the
// two registries in lockstep: bump this package.json's version when PyPI's
// memtrust ships a new release, and every subsequent npm install/npx run
// resolves to that exact pinned release, not whatever is newest at run time.
const PACKAGE_VERSION = require(path.join(__dirname, &quot;..&quot;, &quot;package.json&quot;)).version;
...
const result = spawnSync(
  uvPath,
  [&quot;tool&quot;, &quot;run&quot;, &quot;--from&quot;, `memtrust==${PACKAGE_VERSION}`, &quot;memtrust&quot;, ...process.argv.slice(2)],
  { stdio: &quot;inherit&quot; }
);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The script resolves a platform-specific &lt;code&gt;uv&lt;/code&gt; binary from &lt;code&gt;require.resolve()&lt;/code&gt;, hands off every argument unchanged, and delegates all memtrust logic to the binary, pinning only the PyPI release to its own package version. Reading the whole file top to bottom takes about a minute, which was the point: we wanted a small bridge you can audit at a glance, one that gives you both determinism and simplicity without hiding anything.&lt;/p&gt;
&lt;p&gt;We spent a good amount of time deciding where to actually source the &lt;code&gt;uv&lt;/code&gt; binary from. Each platform package's &lt;code&gt;prepack&lt;/code&gt; script fetches the matching &lt;code&gt;uv&lt;/code&gt; release archive directly from Astral's GitHub releases, along with the per-archive &lt;code&gt;.sha256&lt;/code&gt; checksum file &lt;code&gt;uv&lt;/code&gt; publishes alongside it, and the script requires an exact match before it will proceed. The maintainer running the release does this verification once, at &lt;code&gt;npm publish&lt;/code&gt; time. We chose that tradeoff on purpose: a maintainer verifying the checksum with full visibility into the supply chain gives a stronger guarantee than repeating the check on every install, and downstream &lt;code&gt;npm install&lt;/code&gt; runs simply rely on that already-verified state. An offline or network-restricted install never has to reach out to Astral's servers at all, since the &lt;code&gt;uv&lt;/code&gt; binary already sits in &lt;code&gt;node_modules&lt;/code&gt; ready to run.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;uv tool run --from memtrust==&amp;lt;pinned version&amp;gt; memtrust &amp;lt;args&amp;gt;&lt;/code&gt; does the heavy lifting after that. It provisions a Python interpreter if the system doesn't have one, installs that pinned &lt;code&gt;memtrust&lt;/code&gt; release from PyPI into an isolated tool environment on first use, then caches and runs the tool. An agent or a CI job with Node available ends up with a fully working Python benchmark tool, no extra setup required.&lt;/p&gt;
&lt;p&gt;Node's footprint is big enough to justify closing this gap on its own. Sonatype's 2026 State of the Software Supply Chain Report puts npm at 7.97 trillion package downloads across 2025, up 65.43% year over year, against 804.97 billion for PyPI in the same period (Sonatype, 2026). Take that as a rough proxy for what's likely sitting in CI images: Node-based tooling reaches a huge share of environments by default, which raises the odds that Node is already present even where a curated Python toolchain isn't. A Node wrapper bridges that gap directly.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;uv&lt;/code&gt; tool itself has real adoption behind it. Astral's repository sits at 87,595 GitHub stars as of 2026-07-17 (GitHub API, &lt;code&gt;astral-sh/uv&lt;/code&gt;). pypistats.org reports 165.67 million downloads in the trailing month. The Python Developers Survey run by the PSF and JetBrains found &lt;code&gt;uv&lt;/code&gt; usage went from 0% to 11% adoption in the single year it was introduced, reported in JetBrains' &quot;The State of Python 2025.&quot; Stack Overflow's 2025 Developer Survey puts &lt;code&gt;uv&lt;/code&gt; at 9.5% adoption against pip's 40.9% and npm's 56.8%. Building a bootstrap step on top of &lt;code&gt;uv&lt;/code&gt; was an easy call given those numbers.&lt;/p&gt;
&lt;h3 id=&quot;generating-structured-json-output&quot;&gt;Generating structured JSON output&lt;/h3&gt;
&lt;p&gt;Every &lt;code&gt;memtrust run&lt;/code&gt; invocation writes a full JSON report to disk by default, saving to &lt;code&gt;./memtrust-report-&amp;lt;date&amp;gt;.json&lt;/code&gt; unless you point it elsewhere with &lt;code&gt;--output&lt;/code&gt;. The command produces that JSON report on top of its console summary, so you never have to remember a &lt;code&gt;--json&lt;/code&gt; switch. The report's &lt;code&gt;results&lt;/code&gt; block breaks down per-backend, per-eval outcomes using the same signal taxonomy the underlying evals use internally. The &lt;code&gt;memtrust run --eval&lt;/code&gt; command currently exposes seventeen registered evals, from &lt;code&gt;longmemeval&lt;/code&gt; through &lt;code&gt;temporal_kg_boundary&lt;/code&gt;. A program reading the file gets the same verdict a person reading the console table would get.&lt;/p&gt;
&lt;p&gt;Here's the actual payload an agent or CI job parses. We pulled this directly from a real &lt;code&gt;memtrust run --backends mempalace,mem0,zep,openviking --eval all&lt;/code&gt; invocation against &lt;code&gt;memtrust&lt;/code&gt; 0.3.3. The run used zero configured credentials, and we left the output completely unedited:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;mempalace&quot;: {
    &quot;status&quot;: &quot;skipped&quot;,
    &quot;reason&quot;: &quot;mempalace is not configured: environment variable MEMPALACE_STORAGE_PATH is not set. Skipping this backend. See docs/methodology.md for setup instructions.&quot;,
    &quot;missing_env_var&quot;: &quot;MEMPALACE_STORAGE_PATH&quot;
  },
  &quot;mem0&quot;: {
    &quot;status&quot;: &quot;skipped&quot;,
    &quot;reason&quot;: &quot;mem0 is not configured: environment variable MEM0_API_KEY is not set. Skipping this backend. See docs/methodology.md for setup instructions.&quot;,
    &quot;missing_env_var&quot;: &quot;MEM0_API_KEY&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The output is strict key-value pairs. The &lt;code&gt;status&lt;/code&gt; and &lt;code&gt;missing_env_var&lt;/code&gt; fields give programs machine-checkable data to act on right away, which leads to a design choice worth calling out.&lt;/p&gt;
&lt;p&gt;We added a &lt;code&gt;memtrust report &amp;lt;path&amp;gt;&lt;/code&gt; subcommand for reading a prior run's JSON back and printing a formatted summary. That's useful when the agent running the eval and the person or system reviewing it are two separate processes.&lt;/p&gt;
&lt;p&gt;There's an easy way for a parser to get this wrong, though. A backend missing a configured credential environment variable keeps the run alive: it prints &lt;code&gt;SKIPPED&lt;/code&gt; in that backend's section and continues on. A CLI running unattended in CI needs to behave this way, since a missing API key for one backend shouldn't stop the rest of the eval suite from finishing. But that means a program parsing the JSON has to check each backend's own &lt;code&gt;status&lt;/code&gt; field. If it relies only on the command's exit code, it'll wrongly assume a skipped backend actually passed.&lt;/p&gt;
&lt;p&gt;The CLI also includes a &lt;code&gt;--sign&lt;/code&gt; flag backed by an Ed25519 keypair generated with &lt;code&gt;memtrust keygen&lt;/code&gt;. This writes a signed receipt alongside the JSON report, and the signature proves which key produced the file and that the contents haven't been altered. We built this for agent-to-agent consumption: structured output only means something if the agent reading it can verify where it came from, and an unsigned JSON file is trivial to spoof. Signing stays optional for standard runs, but it's there for reports that need to cross a trust boundary.&lt;/p&gt;
&lt;h3 id=&quot;current-approaches-to-agent-tool-distribution&quot;&gt;Current approaches to agent tool distribution&lt;/h3&gt;
&lt;p&gt;Several existing practices try to close the agent-tool distribution gap. It's worth being precise about what each one actually solves, and where each one stops short.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Protocol-level standards for agent-tool-calling&lt;/strong&gt; give a direct answer for tool invocation. The Model Context Protocol, introduced in November 2024 and donated in December 2025 to the newly formed Agentic AI Foundation, a directed fund under the Linux Foundation, reported over 97 million monthly SDK downloads and more than 10,000 active public servers at the time of that announcement. A protocol standard solves the invocation contract: it defines how a tool describes its own capabilities and how an agent calls them in a predictable shape. But the underlying tool still bears full responsibility for returning truthful values. Standardizing the envelope leaves the contents completely unverified.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Traditional package-manager install flows&lt;/strong&gt;, including pip and npm's default behavior, need a human reading terminal output to make judgment calls. The user has to accept prompts, notice warnings, and interpret stack traces. A default &lt;code&gt;pip install&lt;/code&gt; or &lt;code&gt;npm install&lt;/code&gt; outputs unstructured text, because the people who designed these tools built them for a person watching the terminal, not a program parsing it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Benchmark and leaderboard self-reporting&lt;/strong&gt; in the broader ML ecosystem has a well-documented gaming problem. A 2025 analysis found that one major model provider privately tested 27 model variants against a popular crowdsourced arena benchmark before release. Proprietary models received access to 54.3% to 70.1% of the benchmark's evaluation data, while openly available models got far less. That additional arena data access alone produced score gains of up to 112% relative to a baseline (Singh, Nan, Wang, et al., &quot;The Leaderboard Illusion,&quot; NeurIPS 2025). A separate survey documented the broader pattern of train/test contamination undermining leaderboard comparisons across the field. A self-reported number that relies entirely on trust is easy to optimize toward instead of actually earning.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Detection-capable claims in vulnerability and bug-finding tooling&lt;/strong&gt; frequently get conflated with live-reproduced claims. The industry has started to recognize this distinction directly. Recent work building automated exploit-verification pipelines highlights the difference: identifying a plausible flaw is a categorically easier claim than producing a working reproduction against a real running system, and treating the two as equivalent overstates what the detection tool actually found (a 2025 paper on automated exploit generation and validation makes this argument in those terms).&lt;/p&gt;
&lt;p&gt;There's a gap running through all four of these categories. Each one lets a tool output well-formatted or plausible data without proving it's true. A protocol standardizes the call. A package manager gets the bits onto disk. A leaderboard reports a number. A detection tool flags a candidate. What none of them do is the adversarial re-verification this project treats as mandatory. An agent-native benchmark tool has to add that on top of everything those categories already handle well: install cleanly, call predictably, report a number, flag a candidate, and then prove that number or flag actually holds up when an independent engineer checks the work.&lt;/p&gt;
&lt;h3 id=&quot;verifying-the-tool-s-internal-claims&quot;&gt;Verifying the tool's internal claims&lt;/h3&gt;
&lt;p&gt;The distribution mechanics only matter if the underlying tool is trustworthy. An agent calling &lt;code&gt;memtrust run&lt;/code&gt; autonomously parses the JSON and decides what to do next, with no visibility into any silent failures behind it. Part 1 laid out exactly what's at stake here: a fictional API call inside that adapter forced every downstream consumer to act on claims nobody had actually verified.&lt;/p&gt;
&lt;p&gt;The discipline from that re-verification keeps running on repeat. Every time the project's backlog surfaces a missing capability, an engineer looks at the actual gap, builds the smallest piece of it that can genuinely be closed, and re-verifies the result independently before calling it done. We scope partial fixes to their real boundaries on purpose, to keep the backlog honest rather than padded. A recent knowledge-graph boundary bug is a good example of this loop in action, and the memtrust eval suite missed it entirely until a few days ago.&lt;/p&gt;
&lt;h4 id=&quot;fixing-the-temporal-kg-boundary-bug&quot;&gt;Fixing the temporal-KG boundary bug&lt;/h4&gt;
&lt;p&gt;The bug lives upstream, inside the vendor's own knowledge-graph store. Issue &lt;code&gt;MemPalace/mempalace#1913&lt;/code&gt; reported a race condition: querying a knowledge-graph fact with an &lt;code&gt;as_of&lt;/code&gt; point-in-time filter at the exact instant that fact was invalidated returned both the old value and the new value at once. The code used a closed-interval SQL comparison (&lt;code&gt;valid_to &amp;gt;= as_of&lt;/code&gt;) where it needed a half-open interval (&lt;code&gt;valid_to &amp;gt; as_of&lt;/code&gt;). A fact ending exactly at the query instant still matched, and if its replacement started at that same instant, a single-valued predicate like &lt;code&gt;uses_model&lt;/code&gt; reported two contradictory answers at once with no warning at all. The pattern that triggers this is invalidating a fact and adding its replacement at the same shared timestamp, and the vendor's own pre-fix onboarding guidance told every agent to use exactly that pattern. The maintainers fixed it in merged PR &lt;code&gt;#1914&lt;/code&gt;, switching to the half-open interval and adding a new &lt;code&gt;supersede()&lt;/code&gt; primitive as the preferred atomic replacement.&lt;/p&gt;
&lt;p&gt;The memtrust backlog had flagged the capability to detect this class of bug months earlier. We'd bundled it together with a second, unrelated capability (drawer neighbor-expansion leak scoping) into one item, and that item sat open because it was too big for a single scoped fix. Forcing it through just to close the backlog line would have meant shipping something half-built, or quietly widening what counts as &quot;done,&quot; and the project rules rule out both of those. The item stayed open until we could shrink it down to a manageable unit.&lt;/p&gt;
&lt;p&gt;The scope-expanding investigation is where the real work happened. Splitting the bundled item meant reading the adapter code directly, and that reading turned up something useful: the wiring for &lt;code&gt;kg_query(entity, as_of=..., direction=&quot;both&quot;)&lt;/code&gt; already existed post-rewrite and passed straight through to the confirmed &lt;code&gt;tool_kg_query&lt;/code&gt;. The evals were simply passing a zero value for &lt;code&gt;as_of&lt;/code&gt;. So the actual capability gap was building a classifier and a test to exercise the path that already worked, which was a much smaller job than the original bundled item made it look. You only find that kind of distinction by reading the actual code with the goal of shrinking the claim down to what's true. Re-estimating a vague item over and over just hides these details instead of surfacing them.&lt;/p&gt;
&lt;p&gt;We built a new &lt;code&gt;TemporalBoundarySignal&lt;/code&gt; enum (&lt;code&gt;CLEAN&lt;/code&gt;, &lt;code&gt;DOUBLE_COUNT&lt;/code&gt;, &lt;code&gt;NOT_APPLICABLE&lt;/code&gt;) in &lt;code&gt;adapters/base.py&lt;/code&gt;, and added a dedicated eval at &lt;code&gt;evals/temporal_kg_boundary.py&lt;/code&gt;. This eval seeds a fact, invalidates it, and adds its replacement at the same boundary instant to match the shape of &lt;code&gt;#1913&lt;/code&gt;. It queries the database at that instant and classifies the response two separate ways: first, it checks the adapter's own self-reported signal, and second, it computes an independent classification straight from the raw list of facts the query actually returned. That second check is what forces the eval to verify the raw data on its own rather than trusting the adapter's word for it. A disagreement between the two signals points to a bug in the adapter's own classification logic, which is a different failure from a backend double-count, and the eval catches both separately.&lt;/p&gt;
&lt;p&gt;The test suite behind this holds to the same standard. It runs against two from-scratch fake implementations that faithfully reproduce the real pre-&lt;code&gt;#1914&lt;/code&gt; closed-interval comparison and the real post-&lt;code&gt;#1914&lt;/code&gt; half-open comparison, computing a real answer from real logic each time rather than leaning on hardcoded stubs.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://raw.githubusercontent.com/RudrenduPaul/memtrust/main/docs/assets/dev-to-demos/demo-4-temporal-kg-tests.gif&quot; alt=&quot;Terminal recording of `pytest tests/test_temporal_kg_boundary.py -q --no-cov` passing 15 tests&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Fifteen tests, all passing, against fakes that implement both the buggy closed-interval logic and the fixed half-open logic in full.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;We'll lay out the limitations here as plainly as the eval's own docstring does. First, the real &lt;code&gt;mempalace&lt;/code&gt; package is still absent from the build environment. PR &lt;code&gt;#1914&lt;/code&gt; is merged to the vendor's &lt;code&gt;develop&lt;/code&gt; branch, but it's absent from the &lt;code&gt;3.5.0&lt;/code&gt; release this adapter was built and live-verified against. That makes this a detection-capable harness: we proved it correct against faithful synthetic reproductions of both the buggy and fixed comparison logic, but it currently only runs against synthetic targets. The eval should flag &lt;code&gt;#1913&lt;/code&gt;'s failure shape the moment we point it at a pre-fix deployment, but we still need to actually run that live test before we call this done. Second, &lt;code&gt;temporal_kg_boundary&lt;/code&gt; is now wired into the CLI's eval registry as the seventeenth entry &lt;code&gt;memtrust run --eval&lt;/code&gt; can invoke by name. It reports &lt;code&gt;not_applicable&lt;/code&gt; against any backend other than &lt;code&gt;mempalace&lt;/code&gt;, since MemPalace is the only adapter implementing the knowledge-graph calls this eval needs.&lt;/p&gt;
&lt;p&gt;Before this change merged, a second engineer independently re-ran the full suite and confirmed every test passed. &lt;code&gt;ruff check&lt;/code&gt;, &lt;code&gt;ruff format --check&lt;/code&gt;, and &lt;code&gt;mypy --strict&lt;/code&gt; all ran clean against the actual merge commit. The suite has grown since then: we're at 590 tests passing and 8 skipped, at 93% overall coverage as of this writing. We applied the same discipline from Part 1's re-verification here, on a much smaller change, because the habit has to hold on boring changes just as much as it held on the dramatic one.&lt;/p&gt;
&lt;p&gt;The drawer neighbor-expansion half of that original bundled backlog item remains open and untouched on purpose. Splitting the item let us close only the piece we could actually verify, and we left the other half exactly where it was.&lt;/p&gt;
&lt;h4 id=&quot;catching-regressions-in-mem0-during-a-live-run&quot;&gt;Catching regressions in mem0 during a live run&lt;/h4&gt;
&lt;p&gt;The same habit produced a second example a few days later, from a real live benchmark run. The &lt;code&gt;mem0_direct&lt;/code&gt; adapter uses self-hosted &lt;code&gt;mem0ai&lt;/code&gt; running in-process against a local Qdrant instance and the OpenAI API. It ran contradiction detection, compression/round-trip fidelity, and extraction quality. Contradiction detection came back with 7 for 7 cases silently overwritten and 0% flagged: if you tell it your meeting moved from 2pm to 3pm, it stores the new fact and reports zero changes. Compression/round-trip fidelity landed at a 31.6% mean literal fidelity across 5 cases, which we expected, since mem0's design goal is semantic extraction, not literal fidelity. Extraction quality ran 15 cases, 12 deliberately junk inputs and 3 deliberately valid ones. The system correctly rejected all 12 junk inputs, but lost all 3 valid ones on retrieval. That's a small sample, and we're treating it as a signal worth investigating further rather than a conclusion.&lt;/p&gt;
&lt;p&gt;Getting any of those numbers required fixing a bug first: the gpt-5-mini/gpt-5o-mini single-character mismatch described in Part 1, which mem0ai/mem0#6085 had already documented independently, 16 days earlier. That's the value of an automated npm install path in practice: a CI job that pulls this tool in on a schedule, with almost no setup effort, catches real regressions automatically.&lt;/p&gt;
&lt;h3 id=&quot;unifying-distribution-and-verification&quot;&gt;Unifying distribution and verification&lt;/h3&gt;
&lt;p&gt;An agent that installs your CLI automatically but gets back false data hasn't actually solved anything. It still needs a human to check the result behind it, which means a benchmark tool with airtight verification discipline that only runs for a person with a curated Python environment never reaches the audience it's meant for. Agents and CI pipelines make decisions with no human supervision in the loop, so a tool built for autonomous execution has to hold its own claims to the same evidentiary bar it applies to the systems it's benchmarking.&lt;/p&gt;
&lt;p&gt;We still have real limitations here. We've wired &lt;code&gt;temporal_kg_boundary&lt;/code&gt; into the CLI, but we've only pointed it at the synthetic fakes described above, and we still need to test it against a live, pre-fix MemPalace deployment before it's more than a detection-capable claim. A &lt;code&gt;Kuzu&lt;/code&gt; graph-database driver stays unbuilt on purpose, since the upstream project backing it formally deprecated that path and dropped default test coverage for it, and building against an abandoned dependency is a bad trade regardless of what it might buy us. &quot;Detection-capable&quot; and &quot;field-tested against a live deployment of the exact bug&quot; are two different claims, and security and eval tooling generally blur that line. We're calling it out directly so the softer claim has to stand on its own instead of borrowing credibility from the stronger one.&lt;/p&gt;
&lt;p&gt;Installation and verification both have to work, together, for any of this to mean something. An agent that can install your tool in one command but can't trust what it prints back hasn't gained much over the tools that came before it.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul/memtrust&quot;&gt;github.com/RudrenduPaul/memtrust&lt;/a&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;references&quot;&gt;References&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;Sonatype. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.sonatype.com/state-of-the-software-supply-chain/2026/software-infrastructure-growth&quot;&gt;&quot;2026 State of the Software Supply Chain.&quot;&lt;/a&gt; 2026.&lt;/li&gt;
&lt;li&gt;GitHub API, &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/astral-sh/uv&quot;&gt;&lt;code&gt;astral-sh/uv&lt;/code&gt;&lt;/a&gt; repository metadata, fetched 2026-07-17.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://pypistats.org/packages/uv&quot;&gt;pypistats.org: &lt;code&gt;uv&lt;/code&gt; package download statistics.&lt;/a&gt; Fetched 2026-07-17.&lt;/li&gt;
&lt;li&gt;JetBrains. &lt;a rel=&quot;nofollow&quot; href=&quot;https://blog.jetbrains.com/pycharm/2025/08/the-state-of-python-2025/&quot;&gt;&quot;The State of Python 2025.&quot;&lt;/a&gt; August 2025, based on the Python Developers Survey (PSF + JetBrains).&lt;/li&gt;
&lt;li&gt;Stack Overflow. &lt;a rel=&quot;nofollow&quot; href=&quot;https://survey.stackoverflow.co/2025/technology&quot;&gt;2025 Developer Survey, Technology section.&lt;/a&gt; 2025.&lt;/li&gt;
&lt;li&gt;MemPalace. &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MemPalace/mempalace/issues/1913&quot;&gt;&lt;code&gt;mempalace#1913&lt;/code&gt;, &quot;kg query --as-of returns two values for a single-valued fact at a supersession boundary.&quot;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;MemPalace. &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/MemPalace/mempalace/pull/1914&quot;&gt;&lt;code&gt;mempalace#1914&lt;/code&gt;, &quot;fix: half-open as-of interval + supersede() to end KG boundary double-count.&quot;&lt;/a&gt; Merged.&lt;/li&gt;
&lt;li&gt;Anthropic. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.anthropic.com/news/donating-the-model-context-protocol-and-establishing-of-the-agentic-ai-foundation&quot;&gt;&quot;Donating the Model Context Protocol and Establishing the Agentic AI Foundation.&quot;&lt;/a&gt; December 2025.&lt;/li&gt;
&lt;li&gt;Linux Foundation. &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation&quot;&gt;&quot;Linux Foundation Announces the Formation of the Agentic AI Foundation.&quot;&lt;/a&gt; December 2025.&lt;/li&gt;
&lt;li&gt;Singh, Nan, Wang, et al. &lt;a rel=&quot;nofollow&quot; href=&quot;https://arxiv.org/abs/2504.20879&quot;&gt;&quot;The Leaderboard Illusion.&quot;&lt;/a&gt; NeurIPS 2025, arXiv:2504.20879.&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://arxiv.org/html/2406.04244v1&quot;&gt;&quot;Benchmark Data Contamination of LLMs: A Survey.&quot;&lt;/a&gt; arXiv:2406.04244, June 2024.&lt;/li&gt;
&lt;li&gt;&quot;CVE-Genie: From CVE Entries to Verifiable Exploits.&quot; &lt;a rel=&quot;nofollow&quot; href=&quot;https://arxiv.org/html/2509.01835v1&quot;&gt;arXiv:2509.01835.&lt;/a&gt; 2025.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Co-authored by &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul&quot;&gt;Rudrendu Paul&lt;/a&gt; and &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/Sourav-nandy-ai&quot;&gt;Sourav Nandy&lt;/a&gt;. Rudrendu is an engineer building agent-native open-source tooling, including &lt;code&gt;memtrust&lt;/code&gt; and a companion suite of AI-agent infrastructure projects. Find the code at &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/RudrenduPaul&quot;&gt;github.com/RudrenduPaul&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/23699/giving-an-ai-agent-a-benchmark-tool-it-can-install-and-actually-trust</guid>
<pubDate>Tue, 28 Jul 2026 18:15:44 +0000</pubDate>
</item>
<item>
<title>They Asked for My AI Rules. But I Could Not Just Hand Them Over.</title>
<link>https://coderlegion.com/22525/they-asked-for-my-ai-rules-but-i-could-not-just-hand-them-over</link>
<description>&lt;p&gt;A team lead announces that the team will start using AI-assisted development. Everyone nods. Nobody asks what that actually means on Monday morning.&lt;/p&gt;
&lt;p&gt;Some times ago I was in that position. A project I was working on needed to start using AI-assisted development, and the team was new to it. Nobody had rules written down for an agent to follow. Nobody had skills defined for it to load. There was no shared idea of how this should work inside our specific repo. Someone had to go first. That someone was me.&lt;/p&gt;
&lt;h3 id=&quot;the-rules-worked-because-i-built-them-for-one-repo&quot;&gt;The rules worked because I built them for one repo&lt;/h3&gt;
&lt;p&gt;I spent time curating a set of rules and skills for that project. Not generic ones. I shaped them tightly around how that repo was actually structured, its conventions, its layout, the things a new engineer usually has to learn by asking around. I wanted an agent working inside that codebase to already know what a human teammate would have picked up in the first two weeks.&lt;/p&gt;
&lt;p&gt;I gave a demo. It landed well. Well enough that it got shared further across team, as something other teams could learn from.&lt;/p&gt;
&lt;p&gt;I gave the demo again. Same reaction. Then a few developers reached out for the actual rules and skills files.&lt;/p&gt;
&lt;p&gt;I said sure, and then I actually looked at what I would be handing them.&lt;/p&gt;
&lt;h3 id=&quot;the-problem-showed-up-the-moment-other-people-wanted-in&quot;&gt;The problem showed up the moment other people wanted in&lt;/h3&gt;
&lt;p&gt;It was not copy-paste-able. The rules referenced folder names, module boundaries, and patterns specific to one repo. Handing them over as-is would have meant handing over advice that was wrong for their project, dressed up as a shortcut.&lt;/p&gt;
&lt;p&gt;So I told them to use it as a reference. Look at the structure, understand the reasoning, adapt it to your own repo.&lt;/p&gt;
&lt;p&gt;That is correct advice. I watched people nod at it and then quietly missing it.&lt;/p&gt;
&lt;h3 id=&quot;i-was-solving-the-wrong-problem-the-whole-time&quot;&gt;I was solving the wrong problem the whole time&lt;/h3&gt;
&lt;p&gt;I had been thinking about this as a documentation problem. Write good rules, explain them well, let people copy the idea. What I actually had was a generation problem. The rules that worked were the ones rendered specifically for a stack, not the ones written generically and left for someone to translate.&lt;/p&gt;
&lt;p&gt;The thing that was supposed to spread across other teams did not land as expected. What reached them was a reference nobody had time to turn into anything real. That gap sat there whether I looked at it or not.&lt;/p&gt;
&lt;p&gt;What I wanted to build was not another generic harness. I wanted something that looked at a repo's actual stack and produced rules and skills that worked from day one, not rules that needed a translation step before they were useful.&lt;/p&gt;
&lt;h3 id=&quot;nobody-on-a-team-agrees-on-one-ai-tool&quot;&gt;Nobody on a team agrees on one AI tool&lt;/h3&gt;
&lt;p&gt;Once I sat with that problem, a second one showed up right behind it, and this one turned out to matter just as much.&lt;/p&gt;
&lt;p&gt;Every adoption plan I had seen quietly assumed one AI tool per team. In practice, that assumption breaks almost immediately. One engineer wants Cursor. Another has already built muscle memory in Claude Code. Someone else is testing Windsurf, or is stuck on Copilot because that is what the company licensed, or wants to try Antigravity because something new just came out.&lt;/p&gt;
&lt;p&gt;Nobody wants to be told which editor or which agent to use, and forcing a single choice on a team is its own kind of friction, separate from the rules problem entirely. An adoption effort that only works for one tool is not really team adoption. It is one person's adoption, dressed up as a rollout.&lt;/p&gt;
&lt;h3 id=&quot;what-the-tool-had-to-actually-do&quot;&gt;What the tool had to actually do&lt;/h3&gt;
&lt;p&gt;That is the shape the problem ended up taking. Not &quot;write better rules.&quot; Generate rules and skills that are rendered for a specific stack, and make sure they work no matter which of the tools a given engineer has already chosen.&lt;/p&gt;
&lt;p&gt;That is what became &lt;strong&gt;FARE&lt;/strong&gt; and &lt;strong&gt;BARE&lt;/strong&gt;, frontend and backend AI starter recipes, published as npm CLI tools. Point either one at a project and it generates rules, skills, and workflow context shaped around that project's actual stack, with adapters for five tools out of the box: Cursor, Claude Code, Windsurf, Copilot, and Antigravity. The output is meant to be used immediately, not adapted first.&lt;/p&gt;
&lt;p&gt;The part worth being honest about is that this did not remove the original judgment call. Something still has to decide what &quot;good rules for this stack&quot; means because it's always well opinionated. The CLI just means that decision gets made once, by the tool, instead of once per engineer, per project, under deadline pressure, with wildly inconsistent results.&lt;/p&gt;
&lt;h3 id=&quot;i-did-not-build-everything-i-wanted-to&quot;&gt;I did not build everything I wanted to&lt;/h3&gt;
&lt;p&gt;The scope I first sketched out was larger than what shipped. There were ideas around deeper project-specific customization, additional integrations, and more automated context tracking that I dropped or pushed to later releases. Some of that was a reasonable v1 boundary. Some of it was me wanting to build the interesting version instead of the useful one, and catching myself doing it.&lt;/p&gt;
&lt;p&gt;Keeping it simple was not the easy default. It was a decision, and one I had to make more than once while writing it.&lt;/p&gt;
&lt;p&gt;I do not know yet whether this is the right shape for other teams' problems or just the right shape for the one I kept running into. If you try FARE or BARE on your own project, I would genuinely like to know where it holds up and where it does not.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Awaiting your feedback!&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://jeelvankhede.github.io/frontend-ai-starter-recipes/&quot;&gt;&lt;strong&gt;F&lt;/strong&gt;rontend &lt;strong&gt;A&lt;/strong&gt;i starter &lt;strong&gt;RE&lt;/strong&gt;cipes&lt;/a&gt;&lt;br&gt;
&lt;a rel=&quot;nofollow&quot; href=&quot;https://jeelvankhede.github.io/backend-ai-starter-recipes/&quot;&gt;&lt;strong&gt;B&lt;/strong&gt;ackend &lt;strong&gt;A&lt;/strong&gt;i starter &lt;strong&gt;RE&lt;/strong&gt;cipes&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22525/they-asked-for-my-ai-rules-but-i-could-not-just-hand-them-over</guid>
<pubDate>Mon, 13 Jul 2026 18:56:49 +0000</pubDate>
</item>
<item>
<title> FNPM Just Became Invisible (In the Good Way): Transparent Alias, Better Tests &amp; a Smoother Experience</title>
<link>https://coderlegion.com/22063/fnpm-just-became-invisible-the-good-transparent-alias-better-tests-smoother-experience</link>
<description>&lt;p&gt;There are moments when you stop, look at your terminal, and think:&lt;br&gt;
&lt;em&gt;“Why does something as simple as managing packages in JavaScript has to be… so dramatic?”&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;If you've been around the Node.js ecosystem for more than 5 minutes, you know exactly what I mean.&lt;/p&gt;
&lt;p&gt;Well, &lt;strong&gt;FNPM just got its biggest update ever&lt;/strong&gt;, and no, it’s not “just another technical improvement.” This update is about giving you the experience we all secretly wanted: using your favorite package manager &lt;em&gt;while something way smarter runs behind the scenes&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;Let me explain.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;before-npm-pnpm-yarn-drama-&quot;&gt;Before: npm, pnpm, yarn… drama.&lt;/h3&gt;
&lt;h3 id=&quot;now-use-whatever-you-want-fnpm-does-the-hard-work-&quot;&gt;Now: use whatever you want. FNPM does the hard work.&lt;/h3&gt;
&lt;p&gt;The biggest change (and my personal favorite) is the new &lt;strong&gt;transparent alias system&lt;/strong&gt;.&lt;br&gt;
This means you can now type:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;pnpm install
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;…and behind the scenes, &lt;strong&gt;FNPM is actually doing the job&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Why is this so massive?&lt;/p&gt;
&lt;p&gt;Because for the first time, an alternative package manager can &lt;em&gt;sneak into&lt;/em&gt; (yeah, sounds weird but it’s beautiful) your workflow &lt;strong&gt;without asking you to change anything&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If your team uses &lt;code&gt;pnpm&lt;/code&gt;, perfect.&lt;br&gt;
If you prefer FNPM’s model and how it handles things, also perfect.&lt;br&gt;
If your environment is mixed… well, guess what? It works too.&lt;/p&gt;
&lt;p&gt;FNPM now behaves as a &lt;strong&gt;real drop-in replacement&lt;/strong&gt;, like &lt;em&gt;for real real&lt;/em&gt;.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;how-does-this-alias-magic-works-&quot;&gt;How does this alias magic works?&lt;/h3&gt;
&lt;p&gt;FNPM now includes a smart &lt;strong&gt;hook system&lt;/strong&gt; that intercepts the traditional package manager commands and transforms them into FNPM operations.&lt;/p&gt;
&lt;p&gt;In short:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It detects what you want (&lt;code&gt;install&lt;/code&gt;, &lt;code&gt;add&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, &lt;code&gt;run&lt;/code&gt;, etc.).  &lt;/li&gt;
&lt;li&gt;It maps it to FNPM’s internal engine.  &lt;/li&gt;
&lt;li&gt;It keeps the CLI experience you already know.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All without breaking scripts, without changing your mental model, and without asking you to learn new commands (because, honestly, nobody wants that anymore ).&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;better-test-coverage-and-now-actually-cross-platform-&quot;&gt;Better test coverage (and now &lt;em&gt;actually&lt;/em&gt; cross-platform)&lt;/h3&gt;
&lt;p&gt;Another big step forward is the &lt;strong&gt;improved test coverage&lt;/strong&gt;, which now behaves consistently across systems:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Linux  &lt;/li&gt;
&lt;li&gt;macOS  &lt;/li&gt;
&lt;li&gt;Windows (yes, Windows got some love this time )&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Error messages, symlink behavior, path formatting… all those cross-platform headaches?&lt;br&gt;
FNPM now handles them.&lt;/p&gt;
&lt;p&gt;This means FNPM isn’t just “usable” on Windows —&lt;br&gt;
it’s &lt;strong&gt;reliable&lt;/strong&gt;, &lt;strong&gt;predictable&lt;/strong&gt;, and &lt;strong&gt;CI-friendly&lt;/strong&gt; across all platforms.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-other-cool-improvements-worth-mentioning&quot;&gt;️ Other cool improvements worth mentioning&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;New &lt;code&gt;fnpm source&lt;/code&gt; command for shell integration  &lt;/li&gt;
&lt;li&gt;GitHub Workflows + issue templates for better contributing  &lt;/li&gt;
&lt;li&gt;Updated dependencies (thanks Dependabot )  &lt;/li&gt;
&lt;li&gt;Cleaner project structure with better dev tooling  &lt;/li&gt;
&lt;li&gt;A more complete test suite for future expansions  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;FNPM keeps growing with a focus on simplicity + power, not complexity.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;a-real-example-turning-pnpm-into-fnpm-without-anyone-noticing-&quot;&gt;A real example: turning &lt;code&gt;pnpm&lt;/code&gt; into FNPM (without anyone noticing)&lt;/h3&gt;
&lt;p&gt;Here’s a real example of how easy this is.&lt;br&gt;
I swear the first time you run it, you’ll feel that “oh wow” moment .&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(base) ➜  awesome-project fnpm setup pnpm  
Selected package manager: pnpm  

 FNPM hooks created successfully!

Setup Instructions:  
Unix/Linux/macOS:  
  1. Source the setup script:  
     source .fnpm/setup.sh  
  2. Or add to your shell profile (~/.bashrc, ~/.zshrc):  
     echo 'eval &quot;$(fnpm source)&quot;' &amp;gt;&amp;gt; ~/.zshrc  

Usage:  
  • Now you can use pnpm directly:  
    pnpm add some-package  
    pnpm install  
    pnpm run my-script  
  • Commands will be automatically redirected to fnpm  

Note:  
  • Hooks only work in directories with .fnpm configuration  
  • Use full path to bypass: $(which pnpm) command  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Reload your shell:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(base) ➜  awesome-project echo 'eval &quot;$(fnpm source)&quot;' &amp;gt;&amp;gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now try any &lt;code&gt;pnpm&lt;/code&gt; command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt;(base) ➜  awesome-project pnpm --help
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You’ll see something like:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-sh&quot;&gt; This pnpm command is intercepted by FNPM  
Available commands:  
  install, add, remove, run, list, update, cache, clean  

Use 'fnpm --help' for more information  
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt; Boom.&lt;br&gt;
From that moment on, you keep using &lt;code&gt;pnpm&lt;/code&gt; like always…&lt;br&gt;
but FNPM is the one doing the heavy lifting behind the curtain.&lt;/p&gt;
&lt;p&gt;No workflow changes.&lt;br&gt;
No broken scripts.&lt;br&gt;
No new habits.&lt;br&gt;
Just smooth developer life.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-in-short-fnpm-is-no-longer-used-it-s-felt&quot;&gt;✨ In short: FNPM is no longer “used”… it’s &lt;em&gt;felt&lt;/em&gt;&lt;/h3&gt;
&lt;p&gt;With this transparent alias system, FNPM stops being a command you remember to type, and becomes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fast  &lt;/li&gt;
&lt;li&gt;consistent  &lt;/li&gt;
&lt;li&gt;cross-platform  &lt;/li&gt;
&lt;li&gt;and completely invisible when you want it to be  &lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If it was useful before, now it feels… &lt;em&gt;kinda magical&lt;/em&gt;, honestly.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;ps-we-used-ai-to-help-us-write-this-article-because-great-tools-deserve-great-storytelling-&quot;&gt;PS: we used AI to help us write this article — because great tools deserve great storytelling.&lt;/h4&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://fnpm-docs.vercel.app/&quot;&gt;Documentation here&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/22063/fnpm-just-became-invisible-the-good-transparent-alias-better-tests-smoother-experience</guid>
<pubDate>Tue, 07 Jul 2026 17:12:02 +0000</pubDate>
</item>
<item>
<title>I Built a QR Label Designer That Connects Directly to Your Data Tables — Here's How It Works</title>
<link>https://coderlegion.com/21717/i-built-a-qr-label-designer-that-connects-directly-to-your-data-tables-heres-how-it-works</link>
<description>&lt;p&gt;Ever had to print 200 warehouse bin labels and realized there's no clean way to do it from a web app?&lt;/p&gt;
&lt;p&gt;That's what pushed me to build &lt;code&gt;qrlayout-core&lt;/code&gt; and &lt;code&gt;qrlayout-ui&lt;/code&gt; — two open source npm packages for designing QR label templates and bulk-printing them from React. This post walks through the full workflow with real code.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;Printing QR labels from a web app usually means one of these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generating them one at a time manually&lt;/li&gt;
&lt;li&gt;Hardcoding the label layout in your codebase&lt;/li&gt;
&lt;li&gt;Exporting a CSV and using a desktop app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these scale. And none of them give non-technical users control over the label design.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-solution-two-packages-one-pipeline&quot;&gt;The Solution: Two Packages, One Pipeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;qrlayout-ui&lt;/code&gt;&lt;/strong&gt; — a drag-and-drop label designer your users can embed directly in your app&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;qrlayout-core&lt;/code&gt;&lt;/strong&gt; — the headless rendering engine that turns saved templates + data into PNG, PDF, or ZPL&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install qrlayout-core qrlayout-ui
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The workflow looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Labels Tab   →  Design a template, save it with a title + entity type
Master Table →  Select that saved layout from a dropdown
Table Rows   →  Check the records you want
Export       →  PNG · PDF · ZPL (thermal printer)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key idea: &lt;strong&gt;template and data stay separate&lt;/strong&gt;. One layout generates labels for hundreds of records. And because each template is tagged to an entity (&lt;code&gt;employee&lt;/code&gt;, &lt;code&gt;machine&lt;/code&gt;, &lt;code&gt;storage&lt;/code&gt;), the master table only shows layouts relevant to it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-1-define-entity-schemas&quot;&gt;Step 1: Define Entity Schemas&lt;/h3&gt;
&lt;p&gt;You tell the designer what fields each entity has. This powers the live preview — every &lt;code&gt;{{field}}&lt;/code&gt; placeholder dropped on the canvas renders with real sample data instantly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const SAMPLE_SCHEMAS = {
  storage: {
    label: &quot;Storage Master&quot;,
    fields: [
      { name: &quot;binCode&quot;,     label: &quot;BIN Code&quot; },
      { name: &quot;storageType&quot;, label: &quot;Storage Type&quot; },
      { name: &quot;aisle&quot;,       label: &quot;Aisle&quot; },
      { name: &quot;rack&quot;,        label: &quot;Rack Number&quot; },
    ],
    sampleData: {
      binCode: &quot;BIN-A1-R4&quot;, storageType: &quot;Pallet Rack&quot;,
      aisle: &quot;Aisle 01&quot;,    rack: &quot;R-44&quot;
    }
  },
  // employee and machine follow the same shape
};
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-2-mount-the-designer-in-react&quot;&gt;Step 2: Mount the Designer in React&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;qrlayout-ui&lt;/code&gt; is framework-agnostic so you mount it with a &lt;code&gt;ref&lt;/code&gt; + &lt;code&gt;useEffect&lt;/code&gt;. Two things matter most — passing &lt;code&gt;onSave&lt;/code&gt; to capture the layout JSON, and calling &lt;code&gt;destroy()&lt;/code&gt; on unmount to prevent memory leaks.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;useEffect(() =&amp;gt; {
  if (!containerRef.current) return;

  designerRef.current = new QRLayoutDesigner({
    element: containerRef.current,
    entitySchemas: SAMPLE_SCHEMAS,
    initialLayout: editingLayout || { ...DEFAULT_NEW_LAYOUT, id: crypto.randomUUID() },
    onSave: (layout) =&amp;gt; {
      storage.addLabel(layout);        // persist the layout JSON
      setLabels(storage.getLabels());
      setSubView('list');
    }
  });

  return () =&amp;gt; {
    designerRef.current?.destroy();    // always clean up
    designerRef.current = null;
  };
}, [subView, editingLayout]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the user clicks Save inside the designer, &lt;code&gt;onSave&lt;/code&gt; fires with the complete &lt;code&gt;StickerLayout&lt;/code&gt; JSON — name, target entity, dimensions, and all elements. That single JSON object is what flows through the entire rest of the system.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-3-the-master-table-select-rows-pick-layout-export&quot;&gt;Step 3: The Master Table — Select Rows, Pick Layout, Export&lt;/h3&gt;
&lt;p&gt;Each master table (Employees, Machines, Storage BINs) filters layouts by &lt;code&gt;targetEntity&lt;/code&gt; so users only see relevant templates in the dropdown.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;// Only load layouts relevant to this entity
const binLabels = storage.getLabels().filter(l =&amp;gt; l.targetEntity === 'storage');

const getSelectedBins = () =&amp;gt; bins.filter(b =&amp;gt; selectedBinIds.includes(b.id));
const getActiveLayout = () =&amp;gt; labels.find(l =&amp;gt; l.id === selectedLayoutId);

const handleExportPDF = async () =&amp;gt; {
  const layout = getActiveLayout();
  const selected = getSelectedBins();
  if (!layout) return;
  await exportToBatchPDF({
    layout,
    items: selected,
    printer: printer.current,
    baseFilename: 'batch-bin-labels'
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When rows are selected, the export toolbar appears with three options. Until then the page shows a step-by-step guide so users know what to do.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-4-three-export-formats&quot;&gt;Step 4: Three Export Formats&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// PNG — one file per item
printer.renderToDataURL(layout, item, { format: 'png' })

// PDF — all items in one document
import { exportToPDF } from 'qrlayout-core/pdf';
const pdf = await exportToPDF(layout, items);
pdf.save('labels.pdf');

// ZPL — for Zebra and other thermal printers
const zplCommands = printer.exportToZPL(layout, items);
// In production: send over TCP port 9100 directly to the printer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;exportToPDF&lt;/code&gt; is a separate optional import to keep the core bundle light. ZPL output is an array of strings — one per label — which you can download as a &lt;code&gt;.txt&lt;/code&gt; file or pipe straight to a Zebra printer over TCP.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;try-it-pre-loaded-demo-data&quot;&gt;Try It — Pre-Loaded Demo Data&lt;/h3&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://qr-layout-designer.netlify.app/&quot;&gt;live demo&lt;/a&gt; seeds everything on first load:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3 layouts&lt;/strong&gt; — Professional ID Badge, Equipment Asset Tag, Storage Location Label&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3 employees&lt;/strong&gt; — Arjun Mehta, Priya Sharma, Kiran Patel&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3 machines&lt;/strong&gt; — CNC Router X1, Industrial 3D Printer, Hydraulic Press&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3 bins&lt;/strong&gt; — BIN-A1-R1 (Pallet Rack), BIN-A1-R2 (Shelf), BIN-B2-R1 (Cold Storage)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Go to Storage → check all bins → click PDF. Done in 30 seconds.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;this-is-a-frontend-demo-here-s-the-production-path&quot;&gt;This is a Frontend Demo — Here's the Production Path&lt;/h3&gt;
&lt;p&gt;Everything in the demo runs in the browser using localStorage. In a real project you'd swap each layer with a backend equivalent:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Demo &lt;/th&gt;&lt;th&gt;Production&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;localStorage&lt;/code&gt; (layouts) &lt;/td&gt;&lt;td&gt;DB table with a &lt;code&gt;layout_json&lt;/code&gt; column&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;localStorage&lt;/code&gt; (bins/machines) &lt;/td&gt;&lt;td&gt;Your existing ERP or database&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Client-side filter by &lt;code&gt;targetEntity&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;WHERE target_entity = 'storage'&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Browser PDF download &lt;/td&gt;&lt;td&gt;Node.js server-side render → stream to client&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;.txt&lt;/code&gt; ZPL download &lt;/td&gt;&lt;td&gt;TCP socket → Zebra printer (port 9100)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;qrlayout-core&lt;/code&gt; has zero browser dependencies — &lt;code&gt;exportToPDF&lt;/code&gt; and &lt;code&gt;printer.exportToZPL&lt;/code&gt; work identically in Node.js. The full production flow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;React Frontend
  └── qrlayout-ui  →  user designs label → onSave fires layout JSON
  └── POST /api/layouts  →  save to DB

Node.js Backend
  └── GET /api/layouts?entity=storage  →  filtered layouts to frontend
  └── POST /api/labels/print
        ├── fetch layout from DB
        ├── fetch selected records from ERP / DB
        ├── exportToPDF(layout, items)  →  stream PDF to client
        └── printer.exportToZPL(layout, items)  →  TCP → Zebra printer
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;links&quot;&gt;Links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;npm: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/qrlayout-core&quot;&gt;qrlayout-core&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/qrlayout-ui&quot;&gt;qrlayout-ui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/shashi089/qr-code-layout-generate-tool&quot;&gt;GitHub — full source + React demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Live demos: &lt;a rel=&quot;nofollow&quot; href=&quot;https://qr-layout-designer.netlify.app/&quot;&gt;React&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://qr-layout-designer-svelte.netlify.app/&quot;&gt;Svelte&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://qr-layout-designer-vue.netlify.app/&quot;&gt;Vue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy to answer questions about the implementation in the comments.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21717/i-built-a-qr-label-designer-that-connects-directly-to-your-data-tables-heres-how-it-works</guid>
<pubDate>Wed, 01 Jul 2026 21:00:59 +0000</pubDate>
</item>
<item>
<title>I Built a QR Label Designer That Connects Directly to Your Data Tables — Here's How It Works</title>
<link>https://coderlegion.com/21716/i-built-a-qr-label-designer-that-connects-directly-to-your-data-tables-heres-how-it-works</link>
<description>&lt;p&gt;Ever had to print 200 warehouse bin labels and realized there's no clean way to do it from a web app?&lt;/p&gt;
&lt;p&gt;That's what pushed me to build &lt;code&gt;qrlayout-core&lt;/code&gt; and &lt;code&gt;qrlayout-ui&lt;/code&gt; — two open source npm packages for designing QR label templates and bulk-printing them from React. This post walks through the full workflow with real code.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-problem-1&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;Printing QR labels from a web app usually means one of these:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Generating them one at a time manually&lt;/li&gt;
&lt;li&gt;Hardcoding the label layout in your codebase&lt;/li&gt;
&lt;li&gt;Exporting a CSV and using a desktop app&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;None of these scale. And none of them give non-technical users control over the label design.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-solution-two-packages-one-pipeline-1&quot;&gt;The Solution: Two Packages, One Pipeline&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;qrlayout-ui&lt;/code&gt;&lt;/strong&gt; — a drag-and-drop label designer your users can embed directly in your app&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;qrlayout-core&lt;/code&gt;&lt;/strong&gt; — the headless rendering engine that turns saved templates + data into PNG, PDF, or ZPL&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install qrlayout-core qrlayout-ui
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The workflow looks like this:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Labels Tab   →  Design a template, save it with a title + entity type
Master Table →  Select that saved layout from a dropdown
Table Rows   →  Check the records you want
Export       →  PNG · PDF · ZPL (thermal printer)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The key idea: &lt;strong&gt;template and data stay separate&lt;/strong&gt;. One layout generates labels for hundreds of records. And because each template is tagged to an entity (&lt;code&gt;employee&lt;/code&gt;, &lt;code&gt;machine&lt;/code&gt;, &lt;code&gt;storage&lt;/code&gt;), the master table only shows layouts relevant to it.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-1-define-entity-schemas-1&quot;&gt;Step 1: Define Entity Schemas&lt;/h3&gt;
&lt;p&gt;You tell the designer what fields each entity has. This powers the live preview — every &lt;code&gt;{{field}}&lt;/code&gt; placeholder dropped on the canvas renders with real sample data instantly.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;const SAMPLE_SCHEMAS = {
  storage: {
    label: &quot;Storage Master&quot;,
    fields: [
      { name: &quot;binCode&quot;,     label: &quot;BIN Code&quot; },
      { name: &quot;storageType&quot;, label: &quot;Storage Type&quot; },
      { name: &quot;aisle&quot;,       label: &quot;Aisle&quot; },
      { name: &quot;rack&quot;,        label: &quot;Rack Number&quot; },
    ],
    sampleData: {
      binCode: &quot;BIN-A1-R4&quot;, storageType: &quot;Pallet Rack&quot;,
      aisle: &quot;Aisle 01&quot;,    rack: &quot;R-44&quot;
    }
  },
  // employee and machine follow the same shape
};
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-2-mount-the-designer-in-react-1&quot;&gt;Step 2: Mount the Designer in React&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;qrlayout-ui&lt;/code&gt; is framework-agnostic so you mount it with a &lt;code&gt;ref&lt;/code&gt; + &lt;code&gt;useEffect&lt;/code&gt;. Two things matter most — passing &lt;code&gt;onSave&lt;/code&gt; to capture the layout JSON, and calling &lt;code&gt;destroy()&lt;/code&gt; on unmount to prevent memory leaks.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;useEffect(() =&amp;gt; {
  if (!containerRef.current) return;

  designerRef.current = new QRLayoutDesigner({
    element: containerRef.current,
    entitySchemas: SAMPLE_SCHEMAS,
    initialLayout: editingLayout || { ...DEFAULT_NEW_LAYOUT, id: crypto.randomUUID() },
    onSave: (layout) =&amp;gt; {
      storage.addLabel(layout);        // persist the layout JSON
      setLabels(storage.getLabels());
      setSubView('list');
    }
  });

  return () =&amp;gt; {
    designerRef.current?.destroy();    // always clean up
    designerRef.current = null;
  };
}, [subView, editingLayout]);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When the user clicks Save inside the designer, &lt;code&gt;onSave&lt;/code&gt; fires with the complete &lt;code&gt;StickerLayout&lt;/code&gt; JSON — name, target entity, dimensions, and all elements. That single JSON object is what flows through the entire rest of the system.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-3-the-master-table-select-rows-pick-layout-export-1&quot;&gt;Step 3: The Master Table — Select Rows, Pick Layout, Export&lt;/h3&gt;
&lt;p&gt;Each master table (Employees, Machines, Storage BINs) filters layouts by &lt;code&gt;targetEntity&lt;/code&gt; so users only see relevant templates in the dropdown.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-tsx&quot;&gt;// Only load layouts relevant to this entity
const binLabels = storage.getLabels().filter(l =&amp;gt; l.targetEntity === 'storage');

const getSelectedBins = () =&amp;gt; bins.filter(b =&amp;gt; selectedBinIds.includes(b.id));
const getActiveLayout = () =&amp;gt; labels.find(l =&amp;gt; l.id === selectedLayoutId);

const handleExportPDF = async () =&amp;gt; {
  const layout = getActiveLayout();
  const selected = getSelectedBins();
  if (!layout) return;
  await exportToBatchPDF({
    layout,
    items: selected,
    printer: printer.current,
    baseFilename: 'batch-bin-labels'
  });
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When rows are selected, the export toolbar appears with three options. Until then the page shows a step-by-step guide so users know what to do.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;step-4-three-export-formats-1&quot;&gt;Step 4: Three Export Formats&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;// PNG — one file per item
printer.renderToDataURL(layout, item, { format: 'png' })

// PDF — all items in one document
import { exportToPDF } from 'qrlayout-core/pdf';
const pdf = await exportToPDF(layout, items);
pdf.save('labels.pdf');

// ZPL — for Zebra and other thermal printers
const zplCommands = printer.exportToZPL(layout, items);
// In production: send over TCP port 9100 directly to the printer
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;exportToPDF&lt;/code&gt; is a separate optional import to keep the core bundle light. ZPL output is an array of strings — one per label — which you can download as a &lt;code&gt;.txt&lt;/code&gt; file or pipe straight to a Zebra printer over TCP.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;try-it-pre-loaded-demo-data-1&quot;&gt;Try It — Pre-Loaded Demo Data&lt;/h3&gt;
&lt;p&gt;The &lt;a rel=&quot;nofollow&quot; href=&quot;https://qr-layout-designer.netlify.app/&quot;&gt;live demo&lt;/a&gt; seeds everything on first load:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;3 layouts&lt;/strong&gt; — Professional ID Badge, Equipment Asset Tag, Storage Location Label&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3 employees&lt;/strong&gt; — Arjun Mehta, Priya Sharma, Kiran Patel&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3 machines&lt;/strong&gt; — CNC Router X1, Industrial 3D Printer, Hydraulic Press&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;3 bins&lt;/strong&gt; — BIN-A1-R1 (Pallet Rack), BIN-A1-R2 (Shelf), BIN-B2-R1 (Cold Storage)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Go to Storage → check all bins → click PDF. Done in 30 seconds.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;this-is-a-frontend-demo-here-s-the-production-path-1&quot;&gt;This is a Frontend Demo — Here's the Production Path&lt;/h3&gt;
&lt;p&gt;Everything in the demo runs in the browser using localStorage. In a real project you'd swap each layer with a backend equivalent:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Demo &lt;/th&gt;&lt;th&gt;Production&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;localStorage&lt;/code&gt; (layouts) &lt;/td&gt;&lt;td&gt;DB table with a &lt;code&gt;layout_json&lt;/code&gt; column&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;localStorage&lt;/code&gt; (bins/machines) &lt;/td&gt;&lt;td&gt;Your existing ERP or database&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Client-side filter by &lt;code&gt;targetEntity&lt;/code&gt; &lt;/td&gt;&lt;td&gt;&lt;code&gt;WHERE target_entity = 'storage'&lt;/code&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Browser PDF download &lt;/td&gt;&lt;td&gt;Node.js server-side render → stream to client&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;.txt&lt;/code&gt; ZPL download &lt;/td&gt;&lt;td&gt;TCP socket → Zebra printer (port 9100)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;qrlayout-core&lt;/code&gt; has zero browser dependencies — &lt;code&gt;exportToPDF&lt;/code&gt; and &lt;code&gt;printer.exportToZPL&lt;/code&gt; work identically in Node.js. The full production flow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;React Frontend
  └── qrlayout-ui  →  user designs label → onSave fires layout JSON
  └── POST /api/layouts  →  save to DB

Node.js Backend
  └── GET /api/layouts?entity=storage  →  filtered layouts to frontend
  └── POST /api/labels/print
        ├── fetch layout from DB
        ├── fetch selected records from ERP / DB
        ├── exportToPDF(layout, items)  →  stream PDF to client
        └── printer.exportToZPL(layout, items)  →  TCP → Zebra printer
&lt;/code&gt;&lt;/pre&gt;
&lt;hr&gt;
&lt;h3 id=&quot;links-1&quot;&gt;Links&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;npm: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/qrlayout-core&quot;&gt;qrlayout-core&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/qrlayout-ui&quot;&gt;qrlayout-ui&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/shashi089/qr-code-layout-generate-tool&quot;&gt;GitHub — full source + React demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Live demos: &lt;a rel=&quot;nofollow&quot; href=&quot;https://qr-layout-designer.netlify.app/&quot;&gt;React&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://qr-layout-designer-svelte.netlify.app/&quot;&gt;Svelte&lt;/a&gt; · &lt;a rel=&quot;nofollow&quot; href=&quot;https://qr-layout-designer-vue.netlify.app/&quot;&gt;Vue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Happy to answer questions about the implementation in the comments.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/21716/i-built-a-qr-label-designer-that-connects-directly-to-your-data-tables-heres-how-it-works</guid>
<pubDate>Wed, 01 Jul 2026 17:06:32 +0000</pubDate>
</item>
<item>
<title>I built a free IDE extension to catch malicious npm packages before they wreck your project</title>
<link>https://coderlegion.com/20889/i-built-free-ide-extension-to-catch-malicious-npm-packages-before-they-wreck-your-project</link>
<description>&lt;p&gt;Supply-chain attacks via npm are up year-over-year — packages like &lt;code&gt;event-stream&lt;/code&gt;, &lt;br&gt;
the Lazarus group drops, and AI-hallucinated typosquats keep landing in real codebases. &lt;br&gt;
I got tired of finding out &lt;em&gt;after&lt;/em&gt; the fact, so I built &lt;strong&gt;NPM Safety Guard&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;what-it-does&quot;&gt;What it does&lt;/h3&gt;
&lt;p&gt;It scans your &lt;code&gt;package.json&lt;/code&gt; and lockfiles right inside your editor — no separate CLI step.&lt;/p&gt;
&lt;p&gt;Here's what it currently catches across &lt;strong&gt;22 detection layers&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Known malicious packages&lt;/strong&gt; — DPRK RAT drops, Lazarus-linked packages, event-stream clones&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;CVEs&lt;/strong&gt; — via OSV.dev, cached locally (free, no API key needed)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Typosquatting &amp;amp; homoglyph attacks&lt;/strong&gt; — catches &lt;code&gt;lodahs&lt;/code&gt;, &lt;code&gt;reàct&lt;/code&gt;, and AI-hallucinated package names&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Install script hooks&lt;/strong&gt; — flags &lt;code&gt;preinstall&lt;/code&gt;/&lt;code&gt;postinstall&lt;/code&gt; before you run them&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Deep tarball AST scan&lt;/strong&gt; — detects obfuscation, &lt;code&gt;eval&lt;/code&gt;, and payload patterns in the actual source&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency confusion&lt;/strong&gt; — scoped packages planted on public npm to hijack private installs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Exposed secrets&lt;/strong&gt; — API keys, tokens, private keys accidentally left in &lt;code&gt;.env&lt;/code&gt;, &lt;code&gt;.npmrc&lt;/code&gt;, &lt;code&gt;.pem&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;MCP server config scanner&lt;/strong&gt; — catches typosquatted or malicious MCP transport configs&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Supply chain graph&lt;/strong&gt; — interactive force-directed graph with risk overlay from your lockfile&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;OSSF Scorecard + Socket.dev score&lt;/strong&gt; — security hygiene at a glance&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;where-to-get-it&quot;&gt;Where to get it&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;VS Code / Cursor / Windsurf →&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://marketplace.visualstudio.com/items?itemName=jomynn.npm-safety-guard&quot;&gt;VS Code Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;VS Codium / open-source IDEs →&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://open-vsx.org/extension/jomynn/npm-safety-guard&quot;&gt;Open VSX Registry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;WebStorm / IntelliJ IDEA / all JetBrains IDEs →&lt;/strong&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://plugins.jetbrains.com/plugin/com.sendwavehub.npmsafetyguard&quot;&gt;JetBrains Marketplace&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;All free. No account required for the core layers. MIT licensed on the VS Code side.&lt;/p&gt;
&lt;h3 id=&quot;under-the-hood&quot;&gt;Under the hood&lt;/h3&gt;
&lt;p&gt;The VS Code extension is TypeScript. The JetBrains plugin is Kotlin. They share the same &lt;br&gt;
detection signatures bundled at build time — no cloud dependency for the core scan.&lt;/p&gt;
&lt;p&gt;CVE lookups hit OSV.dev with a 24-hour local cache so you're not waiting on a network &lt;br&gt;
call every keystroke.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Have you been burned by a supply-chain attack before? Or do you have a detection layer &lt;br&gt;
you wish existed? Drop it in the comments — I'm actively adding new signatures.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20889/i-built-free-ide-extension-to-catch-malicious-npm-packages-before-they-wreck-your-project</guid>
<pubDate>Thu, 18 Jun 2026 08:44:52 +0000</pubDate>
</item>
<item>
<title>Developer Weekly Briefing — June 13, 2026</title>
<link>https://coderlegion.com/20401/developer-weekly-briefing-june-13-2026</link>
<description>&lt;p&gt;Security dominated this week. A worm hit GitHub's own repos, npm is closing attack vectors that have been open for years, and the developer environment itself is now officially part of the attack surface. But there was plenty of forward-looking news too. Here's what mattered.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-miasma-worm-hit-73-microsoft-repos-and-your-ide-was-the-entry-point&quot;&gt;The Miasma worm hit 73 Microsoft repos — and your IDE was the entry point&lt;/h3&gt;
&lt;p&gt;On June 5, GitHub disabled 73 Microsoft-owned repositories after the Miasma worm used a compromised contributor account to plant a credential-harvesting payload. The infected repos spanned Azure, Azure-Samples, Microsoft, and MicrosoftDocs — including well-used projects like azure-search-openai-demo and the Durable Task libraries. GitHub's automated systems contained it in 105 seconds. But the detail that should get your attention: the payload triggered when a developer opened the repo in an IDE or AI coding tool — Claude Code, Gemini CLI, Cursor, or VS Code. Just opening a trusted repo was enough.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/github-takes-down-73-microsoft-repos-after-miasma-worm-attack/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;npm-v12-ships-in-july-install-scripts-stop-running-by-default&quot;&gt;npm v12 ships in July — install scripts stop running by default&lt;/h3&gt;
&lt;p&gt;This is the one with a hard deadline. Starting in July, npm v12 will no longer auto-execute install scripts, resolve Git dependencies, or pull from remote URLs. All of it becomes opt-in. It's a direct response to the wave of supply chain attacks that hit the JavaScript ecosystem over the past year — including the hijacking of 18 popular packages with 2.6 billion combined weekly downloads. The average npm project pulls in 79 transitive dependencies. Any one of them could carry a malicious script. After July, those scripts won't run unless you say so. Run &lt;code&gt;npm approve-scripts --allow-scripts-pending&lt;/code&gt; now to find out what's in your dependency chain before the upgrade forces your hand.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/npm-v12-is-coming-in-july-heres-what-developers-need-to-do-now/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;vs-code-1-123-adds-1m-token-context-windows-session-sync-and-a-research-agent&quot;&gt;VS Code 1.123 adds 1M token context windows, session sync, and a research agent&lt;/h3&gt;
&lt;p&gt;The headline feature is support for 1-million-token context windows with compatible models, including Claude Opus 4.7 and GPT-5.5 — useful for complex projects where losing context mid-session is a real workflow problem. But the more interesting addition might be session sync: an automatic backup of chat sessions to GitHub that captures conversation history, files touched, and repo context. The companion Chronicle feature lets you query that history in natural language, generate standup reports, and search past work by topic or file. The parallel agents view and a new research agent (preview) round out an update that continues Microsoft's push to make VS Code the center of agentic development.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://devops.com/visual-studio-code-1-123-brings-deeper-ai-integration-and-smarter-agent-tools/&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;ai-coding-tools-are-making-engineers-faster-but-the-review-bottleneck-is-real&quot;&gt;AI coding tools are making engineers faster — but the review bottleneck is real&lt;/h3&gt;
&lt;p&gt;Todd Fisher, CEO of CTM, makes a point worth sitting with: if your team becomes 10x more productive with AI, so does your competitor's. The baseline is shifting for everyone. His candid observation about where it actually breaks down: code generation is fast now, but reviewing AI-generated output is still slow, and that's the new bottleneck. Features are nearly complete but stalled due to insufficient time to review everything individually. Speed without oversight isn't a win.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/20394/ai-coding-tools-are-making-engineers-10x-faster-heres-the-catch&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;agentic-ai-in-production-3-3m-in-savings-and-the-failure-patterns-nobody-talks-about&quot;&gt;Agentic AI in production: $3.3M in savings and the failure patterns nobody talks about&lt;/h3&gt;
&lt;p&gt;From Info-Tech LIVE 2026 in Las Vegas — the organizations seeing real results from agentic AI aren't the ones with the most budget or the flashiest tools. They're the ones who got serious about governance and failure patterns early. The $3.3M savings figure is real, but the more useful part of this piece is the failure taxonomy: what actually goes wrong when agents hit production and how to avoid it.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/20340/agentic-ai-in-production-3-3m-in-savings-and-the-failure-patterns-nobody-warns-you-about&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;from-pilot-to-production-the-agentic-ai-governance-playbook&quot;&gt;From pilot to production: the agentic AI governance playbook&lt;/h3&gt;
&lt;p&gt;Most agentic AI pilots succeed. Most production deployments don't — at least not on the first try. The gap isn't a technology problem; the agents work. The problem is that organizations treat the move to production as a scaling exercise when it's actually a governance exercise. This piece lays out the playbook for getting agents out of the lab and into the real world without breaking things.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/19899/helping-clients-move-from-pilot-to-production-the-agentic-ai-governance-playbook&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-developer-survival-guide-for-the-age-of-agentification&quot;&gt;The developer survival guide for the age of agentification&lt;/h3&gt;
&lt;p&gt;Agents are taking on more of the work that developers used to do. This isn't a theoretical future — it's already happening. Here's a practical look at what skills still matter, what's changing, and how to position yourself for what comes next.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/20112/the-developer-survival-guide-for-the-age-of-agentification&quot;&gt;Read more&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;also-from-coder-legion-this-week-1&quot;&gt;Also from Coder Legion this week&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/20213/securing-agentic-ai-and-the-new-security-reality&quot;&gt;Securing agentic AI and the new security reality&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/20321/your-data-strategy-is-backwards-heres-the-pattern-that-actually-works&quot;&gt;Your data strategy is backwards — here's the pattern that actually works&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/20117/your-service-desk-data-is-smarter-than-you-think-ai-is-finally-proving-it&quot;&gt;Your service desk data is smarter than you think — AI is finally proving it&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/20115/agents-2-0-what-a-year-of-actually-building-them-taught-us&quot;&gt;Agents 2.0: what a year of actually building them taught us&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/19778/cognizants-ai-builder-vision-closing-the-gap-between-hype-and-enterprise-reality&quot;&gt;Cognizant's AI Builder vision: closing the gap between hype and enterprise reality&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt;The through-line this week: the developer environment is now a target, not just the code. Miasma and the npm changes aren't separate stories — they're the same story.&lt;/p&gt;
&lt;p&gt;See you next Friday.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Developer Weekly Briefing is published every Friday on &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com&quot;&gt;Coder Legion&lt;/a&gt;. Written by &lt;a rel=&quot;nofollow&quot; href=&quot;https://coderlegion.com/user/Tom+Smith&quot;&gt;Tom Smith&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/20401/developer-weekly-briefing-june-13-2026</guid>
<pubDate>Fri, 12 Jun 2026 12:07:35 +0000</pubDate>
</item>
<item>
<title>Lazy SRE's guide to secure systems, part 1: the dependencies you didn't read</title>
<link>https://coderlegion.com/19913/lazy-sres-guide-to-secure-systems-part-1-the-dependencies-you-didnt-read</link>
<description>&lt;p&gt;&lt;em&gt;Originally published at &lt;a rel=&quot;nofollow&quot; href=&quot;https://harshit.cloud/blog/lazy-security-part-1-supply-chain&quot;&gt;harshit.cloud&lt;/a&gt; on 2026-04-05.&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;A few months ago a friend's CI pipeline tried to install a package none of us had heard of. The build failed. The error wasn't a missing dep. The error was a local proxy saying &lt;em&gt;this is malware, I'm not letting it touch disk&lt;/em&gt;. The package was a transitive dependency, six levels deep, that had been published to npm 38 minutes earlier. Nobody on the team had asked for it.&lt;/p&gt;
&lt;p&gt;I do platform work at a startup. The job is: keep production up, keep the bill down, keep the kind of person who reads HN comments from getting a free shell on the cluster. The thesis for the series is short. The best security work for a small team is the work &lt;em&gt;Future You at 3am&lt;/em&gt; will actually execute. The lazy answer is also, almost always, the right one: the configuration that makes the wrong thing impossible, rather than only discouraged. This is part 1. Part 2 is GitHub (&lt;code&gt;tj-actions&lt;/code&gt;, OIDC, fork PRs). Part 3 is the unsexy list (IAP, Tailscale, PrivateLink, Okta, default helm creds, the PAT you forgot).&lt;/p&gt;
&lt;h3 id=&quot;the-picture&quot;&gt;the picture&lt;/h3&gt;
&lt;p&gt;You opened your editor, ran &lt;code&gt;npm install&lt;/code&gt;, and onboarded somewhere between 800 and 2,000 packages maintained by people you have never audited. Your team reviewed five direct dependencies. The other 1,995 came in for the ride.&lt;/p&gt;
&lt;p&gt;The job is not to read all 1,995. The job is to make sure that when one of them is the problem, the blast radius is small and the alarm goes off.&lt;/p&gt;
&lt;h3 id=&quot;chainjacking&quot;&gt;chainjacking&lt;/h3&gt;
&lt;p&gt;Chainjacking is the umbrella term for &quot;someone got control of a package you depend on and pushed a bad version.&quot; The attacker doesn't break npm. They get the credentials of the human who publishes the package, ship a patch version, and semver puts it on your machine the next time anyone runs &lt;code&gt;npm install&lt;/code&gt;. &lt;code&gt;event-stream&lt;/code&gt; (2018), &lt;code&gt;ua-parser-js&lt;/code&gt; / &lt;code&gt;coa&lt;/code&gt; / &lt;code&gt;rc&lt;/code&gt; (2021), &lt;code&gt;lottie-player&lt;/code&gt; (2024), and the shai-hulud worm (Sept 2025, with a 2.0 wave in Nov 2025) that self-replicated by stealing tokens from compromised maintainer machines via TruffleHog-style secret scans and then republishing every other package that maintainer owned. The economics still work for the attacker. It is going to keep happening.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://harshit.cloud/images/lazy-security-part-1-supply-chain/chainjacking-timeline.png&quot; alt=&quot;A magazine-infographic-style timeline on a dark navy background. Six stages from left to right: T-7d maintainer account targeted, T-0 malicious version published, T+12m first CI installs it, T+12m02s secrets exfiltrated, T+1h backdoor in artifacts, T+24h credentials for sale. Stages 1-2 highlighted in coral as 'compromise' stages; stages 3-6 in cyan as 'victim' stages.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Fig. 2 — twenty-four hours from maintainer phish to credential resale. Nobody noticed the version bump.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;What matters in that timeline is that the &lt;em&gt;human&lt;/em&gt; steps are slow and the &lt;em&gt;automated&lt;/em&gt; steps are fast. The window between &quot;malicious version published&quot; and &quot;your CI runs &lt;code&gt;npm install&lt;/code&gt;&quot; is whatever your dependabot cron is. If you auto-merge minor and patch bumps, that window is ninety seconds.&lt;/p&gt;
&lt;h3 id=&quot;dependency-confusion&quot;&gt;dependency confusion&lt;/h3&gt;
&lt;p&gt;You have a private package called &lt;code&gt;internal-utils&lt;/code&gt;. Your CI is configured with both your private registry and the public npm registry. Somebody publishes &lt;code&gt;internal-utils&lt;/code&gt; to public npm at a higher version. CI installs the public one. Birsan did this against Apple, Microsoft, Tesla, and PayPal in a weekend in 2021 for bug bounties.&lt;/p&gt;
&lt;p&gt;Fix, in &lt;code&gt;.npmrc&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-conf&quot;&gt;@yourorg:registry=https://npm.yourorg.internal
registry=https://registry.npmjs.org/
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Scope everything internal. Register your scope on public npm as a parked placeholder. It costs nothing.&lt;/p&gt;
&lt;h3 id=&quot;postinstall&quot;&gt;postinstall&lt;/h3&gt;
&lt;p&gt;Most install-time npm supply-chain incidents I have read the postmortem on shipped their malicious code in a &lt;code&gt;postinstall&lt;/code&gt; script — not in runtime code. (Some recent ones, like the chalk/debug compromise of Sept 2025, activate at runtime in the browser or on first import; the switch below doesn't help against those. It does help against the install-time class, which is still the majority.) The install hook runs before your tests, before your linter, as part of the install. Default is enabled. The one-line change with the highest blast-radius reduction:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ini&quot;&gt;# .npmrc
ignore-scripts=true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You'll need to allowlist two to five packages that genuinely need it (typically &lt;code&gt;bcrypt&lt;/code&gt;-shaped things). That number is small. The alternative is letting every package run code on install. Pick.&lt;/p&gt;
&lt;h3 id=&quot;the-install-that-buys-the-most&quot;&gt;the install that buys the most&lt;/h3&gt;
&lt;p&gt;Aikido's &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/AikidoSec/safe-chain&quot;&gt;&lt;code&gt;safe-chain&lt;/code&gt;&lt;/a&gt; is an open-source local proxy that sits in front of &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;npx&lt;/code&gt;, &lt;code&gt;yarn&lt;/code&gt;, &lt;code&gt;pnpm&lt;/code&gt;, &lt;code&gt;pnpx&lt;/code&gt;, &lt;code&gt;bun&lt;/code&gt;, &lt;code&gt;pip&lt;/code&gt;, &lt;code&gt;uv&lt;/code&gt;, &lt;code&gt;poetry&lt;/code&gt;, &lt;code&gt;pipx&lt;/code&gt;. Every package download is intercepted and checked against Aikido Intel, an open malware feed. Malware is blocked before bytes hit disk. Which is before &lt;code&gt;postinstall&lt;/code&gt; runs. Free. No account.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://harshit.cloud/images/lazy-security-part-1-supply-chain/safe-chain-flow.png&quot; alt=&quot;A clean dark-editorial flow diagram. Five columns from left to right: developer terminal running 'npm install lodash-utils', a shell alias intercepting the command, a local proxy that all package downloads route through, the Aikido Intel cloud queried for malware reputation, and an outcome column with a green 'allowed → installed' branch and a red 'blocked → install aborted' branch.&quot;&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Fig. 3 — safe-chain in one picture. A local proxy in front of every package manager, checked against an open threat-intel feed.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;On a dev machine:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh | sh
# restart your shell
npm safe-chain-verify
# expected: OK: Safe-chain works!
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In CI:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;- name: Install safe-chain
  run: |
    curl -fsSL https://github.com/AikidoSec/safe-chain/releases/latest/download/install-safe-chain.sh \
      | sh -s -- --install-dir /usr/local/.safe-chain
    echo &quot;$HOME/.safe-chain/bin&quot; &amp;gt;&amp;gt; &quot;$GITHUB_PATH&quot;
- run: npm ci
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And, the part that quietly does the most work — refuse to install anything younger than 48 hours, because that's the window in which most npm malware is caught and removed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;export SAFE_CHAIN_MINIMUM_PACKAGE_AGE_HOURS=48
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;the-receipts&quot;&gt;the receipts&lt;/h3&gt;
&lt;p&gt;The above stack is approximately one afternoon of work: &lt;code&gt;npm ci&lt;/code&gt; from a committed lockfile, &lt;code&gt;ignore-scripts=true&lt;/code&gt; with a tiny allowlist, scoped private packages with locked registry resolution, safe-chain in front of every install, minimum package age of 48 hours. It will catch most known-bad packages, kill dependency-confusion at the registry level, and reduce postinstall blast radius to zero for the long tail. It will not catch a maintainer-account compromise that ships clean-looking malware that only activates in production weeks later. Nothing in this post will. The next-tier defenses (sigstore signing, npm provenance, reproducible builds) are real, and Part 3 will name them.&lt;/p&gt;
&lt;p&gt;For a startup the delta from this post is moving from &quot;one of the next ten incidents has a non-trivial chance of being yours&quot; to &quot;you would have to be very unlucky.&quot; That's the only delta that matters.&lt;/p&gt;
&lt;p&gt;If you do one thing this week, go register your npm scope.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;diagrams-what-i-tried&quot;&gt;diagrams: what i tried&lt;/h3&gt;
&lt;p&gt;Three diagrams, three different tools, one brief: &quot;explain a supply-chain attack to a tired SRE in one image.&quot; Prompts kept short. Results below.&lt;/p&gt;
&lt;h4 id=&quot;1-the-napkin-contrast-coleam00-excalidraw-diagram-skill-&quot;&gt;1 — the napkin contrast (coleam00 excalidraw-diagram skill)&lt;/h4&gt;
&lt;p&gt;Brief: &lt;em&gt;&quot;two-panel hand-drawn napkin. Left panel 'what i installed': five direct deps off a 'your app' box. Right panel 'what npm install pulled in': same five direct deps, transitive sprawl under each, one of them is a red &lt;code&gt;evil-helper@1.2.3&lt;/code&gt;, callout reads 'shipping crypto miners.'&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Result: &lt;a rel=&quot;nofollow&quot; href=&quot;/images/lazy-security-part-1-supply-chain/dependency-tree-contrast.png&quot;&gt;Fig. 1&lt;/a&gt;. Built with the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/coleam00/excalidraw-diagram-skill&quot;&gt;&lt;code&gt;excalidraw-diagram&lt;/code&gt;&lt;/a&gt; skill, a Claude Code skill that enforces a design methodology (depth assessment → pattern mapping → evidence artifacts → mandatory render-and-validate loop) and ships a Playwright-based renderer. The &lt;code&gt;.excalidraw&lt;/code&gt; source is &lt;a rel=&quot;nofollow&quot; href=&quot;/images/lazy-security-part-1-supply-chain/dependency-tree-contrast.excalidraw&quot;&gt;downloadable&lt;/a&gt;; open it on excalidraw.com to edit.&lt;/p&gt;
&lt;p&gt;Two things the skill produced that I wouldn't have prompted for: a semantic color palette (Start/Trigger orange for &quot;your app&quot;, Error red for the malicious package, Inactive blue-dashed for the &quot;+N more&quot; bags), and a summary-flow strip at the bottom (&lt;code&gt;5 direct → 1,200 transitive → 1 malicious → full keychain&lt;/code&gt;) that compresses the post's thesis into nine words. The methodology turns a drawing into an argument.&lt;/p&gt;
&lt;p&gt;One install gotcha worth knowing: the skill loads Excalidraw via ESM and the default CDN (&lt;code&gt;esm.sh&lt;/code&gt;) was unreachable from my environment. One-line patch in &lt;code&gt;render_template.html&lt;/code&gt; to use &lt;code&gt;cdn.jsdelivr.net&lt;/code&gt; and it worked.&lt;/p&gt;
&lt;p&gt;Verdict: &lt;strong&gt;won for the hero.&lt;/strong&gt; The Excalidraw aesthetic earns a place when a post needs a punchline; the skill's methodology adds the second zoom level that elevates a punchline into something that teaches.&lt;/p&gt;
&lt;h4 id=&quot;2-the-chainjacking-timeline-diagram-design-polished-editorial-&quot;&gt;2 — the chainjacking timeline (diagram-design, polished editorial)&lt;/h4&gt;
&lt;p&gt;Brief: &lt;em&gt;&quot;Horizontal six-stage timeline. Dark navy &lt;code&gt;#11141c&lt;/code&gt; ground, coral &lt;code&gt;#ff6b5a&lt;/code&gt; for the two 'compromise' stages, muted cyan &lt;code&gt;#5bc0d9&lt;/code&gt; for the four 'victim' stages. Each stage: small timestamp label (T-7d, T-0, T+12m, T+12m02s, T+1h, T+24h), a node on the line, a short title, one-line caption. Title 'a chainjacking attack, in six steps' (lowercase). Italic figcaption. Magazine-infographic feel, no neon, no scanlines.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Result: &lt;a rel=&quot;nofollow&quot; href=&quot;/images/lazy-security-part-1-supply-chain/chainjacking-timeline.png&quot;&gt;Fig. 2&lt;/a&gt;. Built by a diagram-design subagent. Came back with a ghosted &quot;COMPROMISE / VICTIM&quot; phase label in the background that I hadn't asked for and now wouldn't part with.&lt;/p&gt;
&lt;p&gt;Verdict: &lt;strong&gt;won for explaining attacker workflow.&lt;/strong&gt; Editorial polish without being a dashboard. The agent's improvisation (the phase label) was the part I would not have prompted my way to.&lt;/p&gt;
&lt;h4 id=&quot;3-the-safe-chain-flow-diagram-design-five-column-system-diagram-&quot;&gt;3 — the safe-chain flow (diagram-design, five-column system diagram)&lt;/h4&gt;
&lt;p&gt;Brief: &lt;em&gt;&quot;Five columns left to right: developer terminal running &lt;code&gt;npm install lodash-utils&lt;/code&gt;, shell alias intercepting, local proxy as the focal column, Aikido Intel cloud with a lookup arrow, outcome column with a green 'installed' branch and a red 'install aborted' branch. Dark editorial background &lt;code&gt;#0d1117&lt;/code&gt;. Cyan normal flow, red blocked, green allowed. Legend strip at the bottom.&quot;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Result: &lt;a rel=&quot;nofollow&quot; href=&quot;/images/lazy-security-part-1-supply-chain/safe-chain-flow.png&quot;&gt;Fig. 3&lt;/a&gt;. Same skill as #2. Came back with &lt;code&gt;harshit.cloud · lazy security&lt;/code&gt; baked into the footer (also unprompted, also welcome) and a clean two-outcome fork that makes the block/allow decision the visual sink.&lt;/p&gt;
&lt;p&gt;Verdict: &lt;strong&gt;won for explaining a system.&lt;/strong&gt; When the diagram has to show &lt;em&gt;what a tool does&lt;/em&gt;, this format beats the napkin every time. The napkin is a punchline. This one is a reference.&lt;/p&gt;
&lt;p&gt;I did not ship an animated &lt;code&gt;.gif&lt;/code&gt; for Part 1. The &lt;code&gt;infographic-gif&lt;/code&gt; skill is the right tool for &lt;em&gt;quantitative motion&lt;/em&gt; — a funnel decaying, a bar chart counting up. Nothing in Part 1 needed motion to make the point. If Part 3 ends up wanting a &quot;blast radius over 24 hours&quot; visualisation, that's where the GIF goes.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/19913/lazy-sres-guide-to-secure-systems-part-1-the-dependencies-you-didnt-read</guid>
<pubDate>Sun, 07 Jun 2026 21:55:28 +0000</pubDate>
</item>
<item>
<title>Outside Click</title>
<link>https://coderlegion.com/19344/outside-click</link>
<description>&lt;h3 id=&quot;the-story-behind-use-outside-click&quot;&gt;The Story Behind &lt;code&gt;use-outside-click&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;It started with frustration. Every dropdown, modal, and popover needed the same thing — detect a click outside an element. The existing options were either 6kb class-based relics or unmaintained HOCs. So one late night, &lt;strong&gt;Kitsune Labs&lt;/strong&gt; shipped a fix.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;useOutsideClick(ref, () =&amp;gt; setOpen(false));
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That's the whole API. Hook-first, TypeScript-native, SSR-safe, ~300 bytes gzipped, zero dependencies. Multiple refs, touch + mouse, capture phase, and auto cleanup — all built in.&lt;/p&gt;
&lt;h3 id=&quot;what-s-next&quot;&gt;What's Next&lt;/h3&gt;
&lt;p&gt;This is just the beginning. We're building a whole family of tiny, focused web-dev utilities under &lt;strong&gt;Kitsune Labs&lt;/strong&gt; — lean tools with clean DX and no bloat.&lt;/p&gt;
&lt;h3 id=&quot;contributing&quot;&gt;Contributing&lt;/h3&gt;
&lt;p&gt;We'd love your help. Open an issue, file a PR, or just share ideas.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/kitsunechaos-labs/use-outside-click.git
cd use-outside-click &amp;amp;&amp;amp; npm install &amp;amp;&amp;amp; npm test
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's build a kind, quality-first community together. ⭐ &lt;strong&gt;Star the repo&lt;/strong&gt; if it saved you time!&lt;/p&gt;
</description>
<category>Launches</category>
<guid isPermaLink="true">https://coderlegion.com/19344/outside-click</guid>
<pubDate>Sun, 31 May 2026 09:47:45 +0000</pubDate>
</item>
<item>
<title>LibKill v0.1.5</title>
<link>https://coderlegion.com/18964/libkill-v0-1-5</link>
<description>&lt;p&gt;The past 60 days have been brutal for supply chain security:&lt;/p&gt;
&lt;p&gt;• 639 malicious versions across 323 packages in ONE hour (AntV wave)&lt;br&gt;
• A worm with valid SLSA Build Level 3 provenance (TanStack)&lt;br&gt;
• North Korean APTs injecting malware into &quot;AI-assisted coding&quot; workflows&lt;br&gt;
• Credential stealers hiding in node-ipc, axios, lightning, and hundreds more&lt;br&gt;
I built LibKill to answer one question: &quot;Is my system clean?&quot;&lt;br&gt;
It scans your npm/pip packages against 2,800+ known compromised artifacts and tells you exactly what to remove. No SaaS, no API keys, no cloud. Just a single binary.&lt;/p&gt;
&lt;p&gt;What's new in v0.1.5:&lt;/p&gt;
&lt;p&gt;✅ Live threat feeds from 6 sources — Socket.dev, OSV.dev, GitHub Advisory DB, Cisco Talos, Unit 42, BleepingComputer&lt;br&gt;
✅ uv support — detects Python packages installed via &lt;code&gt;uv pip&lt;/code&gt; and &lt;code&gt;uv tool&lt;/code&gt;&lt;br&gt;
✅ Venv scanning — walks your project directories for virtual environments&lt;br&gt;
✅ Windows Scripts/ path support&lt;br&gt;
✅ 165 new malicious packages added — TrapDoor, Axios/Lazarus, CanisterSprawl, Contagious Interview, Solana typosquats&lt;br&gt;
✅ 19 malware families tracked — PamDOORa, Quasar Linux RAT, CloudZ RAT, Nitrogen, and more&lt;br&gt;
✅ GitHub token integration for live GHSA updates (optional, works without it)&lt;br&gt;
One command to install:&lt;/p&gt;
&lt;p&gt;&lt;code&gt;`&lt;/code&gt;bash&lt;code&gt;`&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;curl -fsSL &lt;a rel=&quot;nofollow&quot; href=&quot;https://lnkd.in/dTv3ukhe&quot;&gt;https://lnkd.in/dTv3ukhe&lt;/a&gt; | bash&lt;/p&gt;
&lt;p&gt;Open source. MIT license. Cross-platform.&lt;/p&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/firfircelik/libkill&quot;&gt;https://github.com/firfircelik/libkill&lt;/a&gt;&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18964/libkill-v0-1-5</guid>
<pubDate>Tue, 26 May 2026 07:27:27 +0000</pubDate>
</item>
<item>
<title>Setting GitHub as a trusted publisher for npm</title>
<link>https://coderlegion.com/18956/setting-github-as-a-trusted-publisher-for-npm</link>
<description>&lt;p&gt;So, stuff happened and &lt;strong&gt;npm&lt;/strong&gt; has been updated to reduce the volume of stuff happening. In a world of SBOMs, SLSA, and supply chain attacks, it's time to get serious about publishing packages. In this case, that means using the new &lt;em&gt;Trusted Publisher&lt;/em&gt; feature to connect GitHub (or GitLab) to &lt;strong&gt;npm&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;set-the-trusted-publisher-on-npm&quot;&gt;Set the trusted publisher on npm&lt;/h3&gt;
&lt;ol&gt;
&lt;li&gt;Sign into &lt;a rel=&quot;nofollow&quot; href=&quot;https://npmjs.com&quot;&gt;npm&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Select the package you want to set up, for example &lt;code&gt;astro-accelerator-utils&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Click &lt;em&gt;Settings&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;In the &lt;em&gt;Trusted Publishers&lt;/em&gt; section, select your provider, in my case it's &lt;strong&gt;GitHub&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;Enter you repository information:&lt;ul&gt;
&lt;li&gt;Organization or user name, for example &lt;code&gt;Steve-Fenton&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Repository name, for example &lt;code&gt;astro-accelerator-utils&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The result should be that &lt;code&gt;Steve-Fenton/astro-accelerator-utils&lt;/code&gt; matches your repo in GitHub&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Provide the workflow file name&lt;ul&gt;
&lt;li&gt;This should match the workflow that will publish the package, in my case &lt;code&gt;build-astro.yml&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;The file must be in &lt;code&gt;.github/workflows/&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Set up connection&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you use environments, you can optionally limit publishing by environment.&lt;/p&gt;
&lt;h3 id=&quot;check-you-github-action&quot;&gt;Check you GitHub Action&lt;/h3&gt;
&lt;p&gt;In your permissions section, you need to allow &lt;code&gt;id-token&lt;/code&gt; to be written.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;permissions:
    id-token: write
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can then use the &lt;code&gt;npm publish&lt;/code&gt; step in your workflow.&lt;/p&gt;
&lt;p&gt;I conditionally publish based on the version number, so I only publish when the version number changes.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-yaml&quot;&gt;- name: Publish if version has been updated
    env:
        NPM_AUTH_TOKEN: ${{ secrets.NPM_AUTH_TOKEN }}
    run: |
        PACKAGE_NAME=$(node -p &quot;require('./package.json').name&quot;)
        LOCAL_VERSION=$(node -p &quot;require('./package.json').version&quot;)
        REMOTE_VERSION=$(npm view $PACKAGE_NAME version || echo &quot;0.0.0&quot;)

        if [ &quot;$LOCAL_VERSION&quot; != &quot;$REMOTE_VERSION&quot; ] &amp;amp;&amp;amp; [ &quot;$(printf '%s\n%s' &quot;$REMOTE_VERSION&quot; &quot;$LOCAL_VERSION&quot; | sort -V | tail -n1)&quot; = &quot;$LOCAL_VERSION&quot; ]; then
        echo &quot;Local version $LOCAL_VERSION is higher than remote version $REMOTE_VERSION. Publishing...&quot;
        echo &quot;//registry.npmjs.org/:_authToken=$NPM_AUTH_TOKEN&quot; &amp;gt; ~/.npmrc
        npm publish --access public
        else
        echo &quot;Version $LOCAL_VERSION is not newer than $REMOTE_VERSION. Skipping publish.&quot;
        fi
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a more secure way to publish npm packages, but it's also easier because you don't need to keep updating tokens and secrets.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18956/setting-github-as-a-trusted-publisher-for-npm</guid>
<pubDate>Tue, 26 May 2026 06:29:10 +0000</pubDate>
</item>
<item>
<title>3 actionable security upgrades to fix npm’s trust problem</title>
<link>https://coderlegion.com/18336/3-actionable-security-upgrades-to-fix-npms-trust-problem</link>
<description>&lt;p&gt;Liran Tal of Snyk published a really useful Github repo called “&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/lirantal/npm-security-best-practices&quot;&gt;Awesome npm security best practices&lt;/a&gt;&lt;/strong&gt;”. In this repo, Liran discusses structural constraints within the CLI toolchain, highlighting many practical, architectural shifts away from npm’s historically permissive, &quot;trust-by-default&quot; model. In the wake of attacks like Shai-Hulud and the Sapphire Sleet Axios compromise, security cannot rely on developer discipline or retroactive registry-side malware yanking. The toolchain itself must be engineered to fail-secure.&lt;/p&gt;
&lt;p&gt;In this short blog post, we’ll highlight 3 distinct, highly actionable decisions that should become the new norm as far as interacting with software packages on JavaScript’s npm registry:&lt;/p&gt;
&lt;h3 id=&quot;1-stop-running-unvetted-code-automatically&quot;&gt;1. Stop running unvetted code automatically&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/lirantal/npm-security-best-practices#1-disable-post-install-scripts&quot;&gt;Github reference&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;This should seem obvious from a security standpoint, but historically, npm has treated &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.npmjs.com/cli/v8/using-npm/scripts#npm-install&quot;&gt;lifecycle scripts&lt;/a&gt;&lt;/strong&gt; (&lt;code&gt;preinstall&lt;/code&gt;, &lt;code&gt;postinstall&lt;/code&gt;) as first-class, unrestricted citizens. Attackers are able to exploit this by adding sleeper dependencies that were never even imported by the application runtime code. In these scenarios, the sole purpose is to trigger an install-time script that’s able to drop a Remote Access Trojan (RAT).&lt;/p&gt;
&lt;p&gt;The CLI must invert its defaults. Instead of requiring developers to pass &lt;code&gt;--ignore-scripts&lt;/code&gt; or manually configure &lt;code&gt;ignore-scripts=true&lt;/code&gt; in &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.npmjs.com/cli/v8/configuring-npm/npmrc&quot;&gt;.npmrc&lt;/a&gt;&lt;/strong&gt;, the toolchain should block all lifecycle scripts out-of-the-box. The npm registry should adopt a mechanism similar to &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://pnpm.io/settings#strictdepbuilds&quot;&gt;pnpm&lt;/a&gt;&lt;/strong&gt;’s &lt;code&gt;strictDepBuilds&lt;/code&gt; or &lt;code&gt;allowBuilds&lt;/code&gt; map. If a dependency attempts to execute a script that isn't explicitly greenlit in a locked configuration file, the CLI should throw a hard error and block the installation, forcing an explicit audit rather than permitting a silent compromise.&lt;/p&gt;
&lt;h3 id=&quot;2-close-any-backdoor-registry-loopholes&quot;&gt;2. Close any backdoor registry loopholes&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/lirantal/npm-security-best-practices#21-pnpm-block-exotic-subdependencies&quot;&gt;Github reference&lt;/a&gt;&lt;/strong&gt; &lt;/p&gt;
&lt;p&gt;A major structural loophole is the flexibility JavaScript package managers offer in resolving dependencies from unconventional sources, such as raw Git URLs or direct tarballs. These &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://stackoverflow.com/questions/44663318/what-does-is-the-exotic-naming-in-the-version-of-npm-packages&quot;&gt;exotic sources&lt;/a&gt;&lt;/strong&gt; completely sidestep registry-side protections - meaning no malware scanning, no OIDC trusted publisher verification, and no provenance cryptographic auditing. To add more fuel to the fire, an attacker could easily commit a malicious .npmrc inside a git dependency that silently re-enables lifecycle scripts on the victim’s machine.&lt;/p&gt;
&lt;p&gt;The toolchain really must enforce strict protocol alignment here. Native CLI defaults should mirror npm's &lt;code&gt;allow-git=none&lt;/code&gt; or pnpm's &lt;code&gt;blockExoticSubdeps=true&lt;/code&gt;. Transitive dependencies must be structurally banned from pulling code outside of the primary secure registry. The CLI should also enforce that only explicitly audited, direct dependencies declared in the root &lt;code&gt;package.json&lt;/code&gt; can utilise non-registry sources, effectively securing the wider dependency tree.&lt;/p&gt;
&lt;h3 id=&quot;3-introduce-a-cool-off-period-for-new-updates&quot;&gt;3. Introduce a cool-off period for new updates&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/lirantal/npm-security-best-practices#3-install-with-cooldown&quot;&gt;Github reference&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Most supply chain compromises have a highly volatile, but brief, day-or-so golden window where the malware propagates before security researchers or automated scanners flag and remove the package (the Shai-Hulud waves were typically mitigated within 12 hours, and the Axios compromise was also caught within hours). Permissive semver ranges mean automated CI/CD pipelines blindly pull down these poisoned packages minutes after publication.&lt;/p&gt;
&lt;p&gt;If you don’t have an existing artifact management layer, like Cloudsmith or Nexus, we would recommend developers introduce a simple cooldown buffer into the dependency resolution engine itself. While features like &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://docs.npmjs.com/cli/v11/using-npm/config#min-release-age&quot;&gt;min-release-age&lt;/a&gt;&lt;/strong&gt; have started rolling out across package managers (such as in npm versions &lt;code&gt;11.10.0&lt;/code&gt; and higher), the structural shift requires making a relative age gate (for example &lt;code&gt;min-release-age=3d&lt;/code&gt;) an opt-out standard rather than an opt-in luxury. By natively refusing to resolve any direct or transitive package version that has not crossed a stability threshold, the CLI acts as a temporal circuit breaker, completely neutralising the smash-and-grab window of rapid worm propagation.&lt;/p&gt;
&lt;h3 id=&quot;the-software-supply-chain-is-broken-but-we-can-fix-it-&quot;&gt;The software supply chain is broken (but we can fix it)&lt;/h3&gt;
&lt;p&gt;As discussed, there are practical solutions to many of the existing pain points in the npm ecosystem. To recap, npm shouldn't run setup scripts automatically. Turn them off unless they are explicitly trusted. Stop dependencies from pulling unverified code from raw Git links or external URLs that skip standard security scans. Try to avoid automatically downloading brand-new updates the second they publish (unless you absolutely have a strong reason for doing so). Wait a day or two to let the security community catch any hidden malware. If you have followed the above 3 secure-by-default approaches, you can confidently regain trust in open source software, and securely prevent the next wave of supply chain attacks.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/18336/3-actionable-security-upgrades-to-fix-npms-trust-problem</guid>
<pubDate>Tue, 19 May 2026 15:42:10 +0000</pubDate>
</item>
<item>
<title>LibKill: Scan Your Machine for Compromised npm, pip, and Bun Packages</title>
<link>https://coderlegion.com/17708/libkill-scan-your-machine-for-compromised-npm-pip-and-bun-packages</link>
<description>&lt;p&gt;Over the last few weeks, supply chain security has once again become a very real problem for developers.&lt;/p&gt;
&lt;p&gt;We keep seeing the same attack pattern:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;CI/CD token stolen
→ malicious package version published
→ developers install or update dependencies
→ compromised code lands on real machines
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And the worst part is simple:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;You may already have one of those compromised packages on your machine without knowing it.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;That is why I built &lt;strong&gt;LibKill&lt;/strong&gt;.&lt;/p&gt;
&lt;h3 id=&quot;what-is-libkill-&quot;&gt;What is LibKill?&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;LibKill&lt;/strong&gt; is an open-source supply chain security scanner.&lt;/p&gt;
&lt;p&gt;It checks your local development environment against a database of known compromised package artifacts.&lt;/p&gt;
&lt;p&gt;Currently, it scans:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;npm global packages&lt;/li&gt;
&lt;li&gt;pip packages&lt;/li&gt;
&lt;li&gt;Bun cache&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Then it cross-references what it finds against &lt;strong&gt;2,672+ known compromised package artifacts&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;If something suspicious is detected, LibKill can help you remove it interactively.&lt;/p&gt;
&lt;h3 id=&quot;why-i-built-it&quot;&gt;Why I built it&lt;/h3&gt;
&lt;p&gt;Most developers hear about compromised packages after the damage is already done.&lt;/p&gt;
&lt;p&gt;A package gets reported.&lt;br&gt;
A malicious version is removed.&lt;br&gt;
A GitHub advisory appears.&lt;br&gt;
A security company publishes an analysis.&lt;/p&gt;
&lt;p&gt;But then comes the real question:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;“Is any of this already on my machine?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;That question should be easy to answer.&lt;/p&gt;
&lt;p&gt;LibKill is my attempt to make that check fast, local, and simple.&lt;/p&gt;
&lt;h3 id=&quot;features&quot;&gt;Features&lt;/h3&gt;
&lt;p&gt;LibKill currently supports:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Scanning npm global packages&lt;/li&gt;
&lt;li&gt;Scanning installed pip packages&lt;/li&gt;
&lt;li&gt;Scanning Bun cache&lt;/li&gt;
&lt;li&gt;Cross-checking against known compromised artifacts&lt;/li&gt;
&lt;li&gt;Interactive removal when threats are found&lt;/li&gt;
&lt;li&gt;Background daemon mode&lt;/li&gt;
&lt;li&gt;Desktop notifications&lt;/li&gt;
&lt;li&gt;macOS, Linux, and Windows support&lt;/li&gt;
&lt;li&gt;Single binary&lt;/li&gt;
&lt;li&gt;Zero runtime dependencies&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;It is written in Go and distributed as a small standalone binary.&lt;/p&gt;
&lt;h3 id=&quot;installation-1&quot;&gt;Installation&lt;/h3&gt;
&lt;p&gt;You can install it with one command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://raw.githubusercontent.com/firfircelik/libkill/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or build it manually:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;git clone https://github.com/firfircelik/libkill.git
cd libkill
make install
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;example-usage&quot;&gt;Example usage&lt;/h3&gt;
&lt;p&gt;After installation, run:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;libkill scan
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To start background monitoring:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;libkill daemon
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If compromised artifacts are found, LibKill will show the results and offer interactive cleanup options.&lt;/p&gt;
&lt;h3 id=&quot;threat-coverage&quot;&gt;Threat coverage&lt;/h3&gt;
&lt;p&gt;LibKill currently covers known compromised artifacts related to campaigns and threat clusters such as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Mini Shai-Hulud&lt;/li&gt;
&lt;li&gt;Contagious Interview&lt;/li&gt;
&lt;li&gt;StegaBin&lt;/li&gt;
&lt;li&gt;SANDWORM_MODE&lt;/li&gt;
&lt;li&gt;CanisterWorm&lt;/li&gt;
&lt;li&gt;W4SP&lt;/li&gt;
&lt;li&gt;TeamPCP&lt;/li&gt;
&lt;li&gt;and many others&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The threat database is built from public security sources including:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Socket.dev&lt;/li&gt;
&lt;li&gt;GitHub Advisory Database&lt;/li&gt;
&lt;li&gt;OSV.dev&lt;/li&gt;
&lt;li&gt;Public security research&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;why-local-scanning-matters&quot;&gt;Why local scanning matters&lt;/h3&gt;
&lt;p&gt;A lot of supply chain security tools focus on repositories, lockfiles, or CI pipelines.&lt;/p&gt;
&lt;p&gt;That is useful, but it does not fully answer what is already installed locally.&lt;/p&gt;
&lt;p&gt;Developers often install tools globally.&lt;/p&gt;
&lt;p&gt;They test packages.&lt;/p&gt;
&lt;p&gt;They use temporary scripts.&lt;/p&gt;
&lt;p&gt;They work across multiple languages and package managers.&lt;/p&gt;
&lt;p&gt;They forget what was installed weeks ago.&lt;/p&gt;
&lt;p&gt;A compromised package does not need to be in your current production repository to be a problem.&lt;/p&gt;
&lt;p&gt;It may already exist somewhere in your local environment.&lt;/p&gt;
&lt;h3 id=&quot;project-details&quot;&gt;Project details&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Language: Go&lt;/li&gt;
&lt;li&gt;License: MIT&lt;/li&gt;
&lt;li&gt;Binary size: around 14 MB&lt;/li&gt;
&lt;li&gt;Dependencies: zero runtime dependencies&lt;/li&gt;
&lt;li&gt;Platforms: macOS, Linux, Windows&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;GitHub:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-txt&quot;&gt;https://github.com/firfircelik/libkill
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;final-note&quot;&gt;Final note&lt;/h3&gt;
&lt;p&gt;LibKill is still evolving.&lt;/p&gt;
&lt;p&gt;My goal is to make it a practical local security tool for developers who work with open-source packages every day.&lt;/p&gt;
&lt;p&gt;If you work with npm, Python, Bun, or modern JavaScript tooling, you should not have to guess whether a known compromised artifact exists on your machine.&lt;/p&gt;
&lt;p&gt;You should be able to check it quickly.&lt;/p&gt;
&lt;p&gt;That is what LibKill is for.&lt;/p&gt;
&lt;p&gt;Feedback, issues, and contributions are welcome.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;curl -fsSL https://raw.githubusercontent.com/firfircelik/libkill/main/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;
</description>
<category>Discussions</category>
<guid isPermaLink="true">https://coderlegion.com/17708/libkill-scan-your-machine-for-compromised-npm-pip-and-bun-packages</guid>
<pubDate>Wed, 13 May 2026 11:02:12 +0000</pubDate>
</item>
<item>
<title>I Published My First npm CLI Tool</title>
<link>https://coderlegion.com/13906/i-published-my-first-npm-cli-tool</link>
<description>&lt;p&gt;Hey folks! &lt;/p&gt;
&lt;p&gt;Let me tell you about &lt;strong&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/create-scaffold-kit&quot;&gt;create-scaffold-kit&lt;/a&gt;&lt;/strong&gt; — what it does, and my experience shipping it for the first time.&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;why-does-this-exist-&quot;&gt;Why does this exist?&lt;/h4&gt;
&lt;p&gt;Every new project I start begins the same way:&lt;/p&gt;
&lt;p&gt;ESLint. Prettier. Husky. Docker. CI.&lt;/p&gt;
&lt;p&gt;An hour gone before I even write real code.&lt;/p&gt;
&lt;p&gt;It’s tedious and honestly, it’s the same copy-paste every time.&lt;/p&gt;
&lt;p&gt;So the question became, &lt;em&gt;why not just automate this?&lt;/em&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;what-is-create-scaffold-kit-&quot;&gt;What is create-scaffold-kit?&lt;/h4&gt;
&lt;p&gt;It's a simple CLI tool that scaffolds a &lt;strong&gt;React&lt;/strong&gt; or &lt;strong&gt;Next.js&lt;/strong&gt; project with all the good stuff already set up. Just run one command:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx create-scaffold-kit my-app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It asks you a few questions — framework, TypeScript, linting, testing, Docker, CI/CD and generates a full project ready to go.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Things it sets up for you:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;✅ TypeScript (strict mode)&lt;/li&gt;
&lt;li&gt;✅ ESLint with flat config (strict or standard)&lt;/li&gt;
&lt;li&gt;✅ Prettier / OXfmt formatting&lt;/li&gt;
&lt;li&gt;✅ Husky + lint-staged git hooks&lt;/li&gt;
&lt;li&gt;✅ Vitest + React Testing Library&lt;/li&gt;
&lt;li&gt;✅ Tailwind CSS&lt;/li&gt;
&lt;li&gt;✅ Docker with multi-stage builds&lt;/li&gt;
&lt;li&gt;✅ GitHub Actions CI/CD&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No more copy-pasting configs. Just answer the prompts and start coding.&lt;/p&gt;
&lt;p&gt;This is especially useful if you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Start projects frequently&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Care about consistent tooling&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Don’t want to reinvent setup every time&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h4 id=&quot;things-i-didn-t-know-before-building-a-cli&quot;&gt;Things I Didn’t Know Before Building a CLI&lt;/h4&gt;
&lt;p&gt;Here are things that were genuinely surprising while going through the process of building a CLI package for the first time. Maybe it saves you some time!&lt;/p&gt;
&lt;h5 id=&quot;1-the-bin-field-in-package-json-is-the-magic&quot;&gt;1. The &lt;code&gt;bin&lt;/code&gt; field in package.json is the magic&lt;/h5&gt;
&lt;p&gt;Always wondered how &lt;code&gt;npx create-something&lt;/code&gt; just... works. Turns out it's this little thing:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;bin&quot;: {
  &quot;create-scaffold-kit&quot;: &quot;dist/index.js&quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That maps the command name to your entry file. Simple, but easy to miss.&lt;/p&gt;
&lt;h5 id=&quot;2-your-entry-file-needs-a-shebang-line&quot;&gt;2. Your entry file needs a shebang line&lt;/h5&gt;
&lt;p&gt;The first line of your CLI entry file must be:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-js&quot;&gt;#!/usr/bin/env node
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without it, the command just won't run. With &lt;strong&gt;tsup&lt;/strong&gt;, this can be injected cleanly via the &lt;code&gt;banner&lt;/code&gt; option in the config, no manual step needed:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-ts&quot;&gt;banner: {
  js: '#!/usr/bin/env node',
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h5 id=&quot;3-bundling-for-cli-is-different&quot;&gt;3. Bundling for CLI is different&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;tsup&lt;/strong&gt; handles the TypeScript compilation. But a few things stood out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Setting &lt;code&gt;&quot;type&quot;: &quot;module&quot;&lt;/code&gt; in package.json&lt;/li&gt;
&lt;li&gt;Making sure import paths use &lt;code&gt;.js&lt;/code&gt; extensions, even in TypeScript source files&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;files&lt;/code&gt; field to control what actually gets published to npm&lt;/li&gt;
&lt;/ul&gt;
&lt;h5 id=&quot;4-the-files-field-matters-a-lot&quot;&gt;4. The &lt;code&gt;files&lt;/code&gt; field matters a lot&lt;/h5&gt;
&lt;p&gt;You can control which files get included when running &lt;code&gt;npm publish&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;&quot;files&quot;: [&quot;dist&quot;]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without this, you'd accidentally publish test files, source maps, scripts, everything. Not great.&lt;/p&gt;
&lt;h5 id=&quot;5-prepack-and-postpack-scripts-are-a-thing&quot;&gt;5. &lt;code&gt;prepack&lt;/code&gt; and &lt;code&gt;postpack&lt;/code&gt; scripts are a thing&lt;/h5&gt;
&lt;p&gt;There are &lt;code&gt;prepack&lt;/code&gt;/&lt;code&gt;postpack&lt;/code&gt; lifecycle hooks that run before and after packaging. These weren’t on my radar before this project. Turns out they’re really handy for automating pre-publish steps.&lt;/p&gt;
&lt;h5 id=&quot;6-testing-a-cli-is-interesting&quot;&gt;6. Testing a CLI is... interesting&lt;/h5&gt;
&lt;p&gt;&lt;strong&gt;Vitest&lt;/strong&gt; handles the unit tests here. Testing something that writes to disk and executes shell commands is tricky. Mocking &lt;code&gt;fs&lt;/code&gt;, child processes, and prompts is a different kind of challenge compared to typical frontend component tests.&lt;/p&gt;
&lt;h5 id=&quot;7-publishing-is-both-easy-and-scary-at-the-same-time&quot;&gt;7. Publishing is both easy and scary at the same time&lt;/h5&gt;
&lt;p&gt;&lt;code&gt;npm publish&lt;/code&gt;. Just two words. But right before running it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&quot;Was everything tested?&quot;&lt;/li&gt;
&lt;li&gt;&quot;Is the version right?&quot;&lt;/li&gt;
&lt;li&gt;&quot;Is the dist clean?&quot;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;A &lt;code&gt;prepublishOnly&lt;/code&gt; script that runs type checking, linting, tests, and build before every publish is a lifesaver. It catches things before they go out. &lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;final-thoughts&quot;&gt;Final Thoughts&lt;/h4&gt;
&lt;p&gt;This isn’t a groundbreaking tool, but building it gave me a much deeper understanding of the Node.js ecosystem, npm publishing, and how CLI tools actually work under the hood.&lt;/p&gt;
&lt;p&gt;If you're a frontend dev who has never shipped a CLI tool — try it once. Even something small. The learning is worth it.&lt;/p&gt;
&lt;p&gt;And if you give &lt;strong&gt;create-scaffold-kit&lt;/strong&gt; a try, I'd love to hear your thoughts! It’s still early, but it works and is actively evolving..&lt;/p&gt;
&lt;hr&gt;
&lt;h4 id=&quot;try-it-out&quot;&gt;Try it out&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx create-scaffold-kit my-app
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Check out the &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/vkondi/create-scaffold-kit&quot;&gt;GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Thanks for reading! &lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/13906/i-published-my-first-npm-cli-tool</guid>
<pubDate>Fri, 03 Apr 2026 12:28:31 +0000</pubDate>
</item>
<item>
<title>Introducing csv-faker-generator: Generate Realistic Test Data in Seconds</title>
<link>https://coderlegion.com/7922/introducing-csv-faker-generator-generate-realistic-test-data-in-seconds</link>
<description>&lt;p&gt;&lt;em&gt;Photo courtesy of &lt;a rel=&quot;nofollow&quot; href=&quot;https://pexels.com/photo/code-coding-computer-data-574071&quot;&gt;Pexels&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;TL;DR:&lt;/strong&gt; I just published &lt;code&gt;csv-faker-generator&lt;/code&gt;, a TypeScript CLI tool that generates CSV files with realistic fake data. Perfect for testing, prototyping, and development. Install it with &lt;code&gt;npm install csv-faker-generator&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-problem-2&quot;&gt;The Problem&lt;/h3&gt;
&lt;p&gt;How many times have you needed test data for your application? Whether you're building a data pipeline, testing import functionality, or prototyping a feature, you inevitably need realistic-looking CSV files with hundreds or thousands of rows.&lt;br&gt;
You have a few options:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Manually create sample data — tedious and limited&lt;/li&gt;
&lt;li&gt;Use existing faker libraries — great, but requires writing boilerplate code&lt;/li&gt;
&lt;li&gt;Export production data — risky and time-consuming&lt;/li&gt;
&lt;li&gt;Use a generic CSV generator — inflexible and often outdated&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;None of these felt ideal. I found myself repeatedly writing the same wrapper code around &lt;code&gt;@faker-js/faker&lt;/code&gt; and &lt;code&gt;csv-writer&lt;/code&gt; just to generate test datasets. That's when I decided to build &lt;code&gt;csv-faker-generator&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id=&quot;the-solution&quot;&gt;The Solution&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;csv-faker-generator&lt;/code&gt; is a lightweight, open-source npm package that makes generating fake CSV data as simple as running a single command.&lt;/p&gt;
&lt;h4 id=&quot;features-1&quot;&gt;Features&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;19+ data types — UUID, names, emails, addresses, phone numbers, dates, and more&lt;/li&gt;
&lt;li&gt;CLI &amp;amp; Programmatic API — Use it from the command line or integrate it into your Node.js/TypeScript projects&lt;/li&gt;
&lt;li&gt;Full TypeScript support — Complete type definitions included&lt;/li&gt;
&lt;li&gt;Customizable output — Define your own columns and data types&lt;/li&gt;
&lt;li&gt;Fast &amp;amp; efficient — Generate thousands of rows in milliseconds&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;getting-started&quot;&gt;Getting Started&lt;/h3&gt;
&lt;h4 id=&quot;installation-2&quot;&gt;Installation&lt;/h4&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install csv-faker-generator
or use it globally
npx csv-faker-generator
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;cli-usage&quot;&gt;CLI Usage&lt;/h4&gt;
&lt;p&gt;The simplest way to generate a CSV file is through the command line:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;Generate 100 user records
npx csv-faker -c id:uuid,name:name,email:email -r 100 -o users.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This creates a file with 100 rows containing random UUIDs, names, and emails.&lt;/p&gt;
&lt;h4 id=&quot;more-examples&quot;&gt;More Examples&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Generate customer data:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx csv-faker --columns &quot;userId:uuid,firstName:firstName,lastName:lastName,email:email,phone:phone&quot; --rows 50 --output customers.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Generate company data:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx csv-faker -c id:uuid,company:company,city:city,country:country -r 20
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Generate employee records with salary data:&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx csv-faker -c id:uuid,name:name,jobTitle:jobTitle,salary:number -r 200 --output employees.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;programmatic-usage&quot;&gt;Programmatic Usage&lt;/h4&gt;
&lt;p&gt;Use it directly in your Node.js or TypeScript projects:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { generateCSV } from 'csv-faker-generator';

// Generate user test data
await generateCSV({
  columns: [
    { header: 'id', type: 'uuid' },
    { header: 'name', type: 'name' },
    { header: 'email', type: 'email' },
    { header: 'address', type: 'address' },
  ],
  rows: 100,
  output: 'users.csv',
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;real-world-use-cases&quot;&gt;Real-World Use Cases&lt;/h3&gt;
&lt;h5 id=&quot;testing-data-import-features&quot;&gt;Testing Data Import Features&lt;/h5&gt;
&lt;p&gt;When building a feature that imports CSV files, you need realistic test data:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx csv-faker -c orderId:uuid,customerName:name,email:email,amount:number -r 1000 -o orders.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now you have 1000 realistic order records to test your import logic.&lt;/p&gt;
&lt;h3 id=&quot;load-testing-amp-performance-testing&quot;&gt;Load Testing &amp;amp; Performance Testing&lt;/h3&gt;
&lt;p&gt;Generate large datasets quickly to test your application's performance:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx csv-faker -c id:uuid,data:username,value:number -r 100000 -o large-dataset.csv
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;prototyping-amp-demos&quot;&gt;Prototyping &amp;amp; Demos&lt;/h3&gt;
&lt;p&gt;Show stakeholders what your data pipeline looks like with real-looking sample data instead of &lt;code&gt;[SAMPLE DATA]&lt;/code&gt; placeholders.&lt;/p&gt;
&lt;h3 id=&quot;database-seeding&quot;&gt;Database Seeding&lt;/h3&gt;
&lt;p&gt;Generate initial data for your development environment:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-typescript&quot;&gt;import { generateCSV } from 'csv-faker-generator';
import { parseCSV } from './your-parser';

// Generate test data
await generateCSV({
  columns: [
    { header: 'id', type: 'uuid' },
    { header: 'username', type: 'username' },
    { header: 'email', type: 'email' },
  ],
  rows: 500,
  output: 'seed-data.csv',
});

// Then seed your database
const data = await parseCSV('seed-data.csv');
await database.insert(data);
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;why-i-built-this&quot;&gt;Why I Built This&lt;/h3&gt;
&lt;p&gt;As an Engineer working heavily with data pipelines and API testing, I found myself repeatedly needing the same thing: quick, realistic test data. The existing solutions either required a lot of boilerplate code or were overly complex for simple use cases.&lt;br&gt;
I wanted something that was:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Dead simple to use (one command)&lt;/li&gt;
&lt;li&gt;Flexible for both CLI and programmatic use&lt;/li&gt;
&lt;li&gt;Well-typed (since I work with TypeScript)&lt;/li&gt;
&lt;li&gt;Focused on the specific problem of CSV generation&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;open-source-amp-contributions&quot;&gt;Open Source &amp;amp; Contributions&lt;/h3&gt;
&lt;p&gt;This package is completely open source (MIT licensed) and hosted on GitHub. If you find it useful, have feature requests, or want to contribute, I'd love to hear from you!&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Repository&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/manojkken/csv-faker-generator&quot;&gt;https://github.com/manojkken/csv-faker-generator&lt;/a&gt; &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;npm&lt;/strong&gt;: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/csv-faker-generator&quot;&gt;https://www.npmjs.com/package/csv-faker-generator&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;conclusion&quot;&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Whether you're testing a data pipeline, prototyping an application, or just need realistic sample data, &lt;code&gt;csv-faker-generator&lt;/code&gt; is here to save you time. Give it a try and let me know what you think!&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npm install csv-faker-generator
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Questions or feedback?&lt;/strong&gt; Feel free to open an issue on &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/manojkken/csv-faker-generator/issues&quot;&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/a&gt; or reach out to me on &lt;a rel=&quot;nofollow&quot; href=&quot;https://twitter.com/mken_&quot;&gt;&lt;strong&gt;Twitter&lt;/strong&gt;&lt;/a&gt;.&lt;/p&gt;
</description>
<category>Automation Tools</category>
<guid isPermaLink="true">https://coderlegion.com/7922/introducing-csv-faker-generator-generate-realistic-test-data-in-seconds</guid>
<pubDate>Fri, 28 Nov 2025 22:22:04 +0000</pubDate>
</item>
<item>
<title>A module for a hexagonal masonry layout with CSS and JavaScript</title>
<link>https://coderlegion.com/5850/a-module-for-a-hexagonal-masonry-layout-with-css-and-javascript</link>
<description>&lt;p&gt;This is an announcement post for a new npm module, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/hexagonal-masonry-placement&quot;&gt;hexagonal-masonry-placement&lt;/a&gt;, released and maintained by me, &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.ingo-steinke.de/&quot;&gt;Ingo Steinke&lt;/a&gt;, creative web developer based in Berlin, Germany.&lt;/p&gt;
&lt;p&gt;A hex layout looks something like this.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://coderlegion.com/?qa=blob&amp;amp;qa_blobid=15105145995728724004&quot; alt=&quot;hex layout website screenshot&quot;&gt;&lt;/p&gt;
&lt;p&gt;Maybe it looks familiar from board games or online worlds, as the honeycomb tiling seemt to be the most efficient way to place items apart from the usual square layouts that most web forms and elements default to.&lt;/p&gt;
&lt;p&gt;The above screenshot comes from my side project web app at &lt;a rel=&quot;nofollow&quot; href=&quot;https://devux.ingo-steinke.de/&quot;&gt;https://devux.ingo-steinke.de/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;While the CSS community has working drafts and experimental implementations for so-called masonry layouts, that place rectangular items in a shifted pattern like a brick wall, we can use a simple flex layout to fake more complex structures like in the screenshot above.&lt;/p&gt;
&lt;p&gt;CSS flex and grid layouts can place items automatically in a responsive way, trying to fill the available screen space without cutting or hiding anything. That's super useful, and the result looks like a table with rows, only that there is no official built-in way to determine or act on which row an item is placed in. There are no :first-row or nth-row(2) pseudo-selectors in CSS.&lt;/p&gt;
&lt;p&gt;I have wrapped my head around mathematical calculations to determine an items' row and set a custom CSS class name accordingly, until I found a much simpler solution that just compares each item's vertical top position with that of its predecessor and increment the row count when they differ.&lt;/p&gt;
&lt;p&gt;I decided to publish the code as an npm module so I can refine the code and concept in one central place to avoid further confusion and possible inconsistent states of duplicated code across projects, if I ever use it anywhere else.The utility requires HTML markup and CSS styles similar to the example code provided. You can use the provided base styles in index.css or provide your own.&lt;/p&gt;
&lt;p&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/hexagonal-masonry-placement&quot;&gt;https://www.npmjs.com/package/hexagonal-masonry-placement&lt;/a&gt;&lt;/p&gt;
</description>
<category>Java</category>
<guid isPermaLink="true">https://coderlegion.com/5850/a-module-for-a-hexagonal-masonry-layout-with-css-and-javascript</guid>
<pubDate>Wed, 01 Oct 2025 15:52:32 +0000</pubDate>
</item>
<item>
<title>Streamlining Your Next.js Project with Private GitHub/Gitlab Repositories as NPM Packages</title>
<link>https://coderlegion.com/5276/streamlining-your-next-js-project-with-private-github-gitlab-repositories-npm-packages</link>
<description>&lt;h4 id=&quot;using-private-github-gitlab-repos-in-your-next-js-projects&quot;&gt;Using Private GitHub/Gitlab Repos in Your Next.js Projects&lt;/h4&gt;
&lt;p&gt;Imagine this scenario: you're working on a super cool Next.js project, and you have some shared code that you'd like to use across multiple projects. You don't really want to make it public by publishing it to npm. You also don't want to pay for an npm account to make your repos private. Is there a way to keep it private, secure, yet reusable? You bet!&lt;/p&gt;
&lt;p&gt;In the world of Next.js v13.4 and beyond, using private GitHub or GitLab repositories as npm packages is not only possible, it's also quite straightforward. The steps vary for older versions of Next.js before v13.4 (those using Page Router) and won't be covered in this blog. &lt;/p&gt;
&lt;p&gt;Essentially, we’ll be taking code from a private GitHub or GitLab repository and using it directly in our Next.js project. This method is perfect when you have shared components, utilities, or any other code that you need to reuse across several projects, but don't want to expose to the world (or persevere through the overhead of setting up an npm account).&lt;/p&gt;
&lt;p&gt;Today, I'll walk you through the steps to make this happen. Buckle up, because we're about to make your life a whole lot easier!&lt;/p&gt;
&lt;h5 id=&quot;step-1-create-your-private-github-repo&quot;&gt;Step 1: Create Your Private GitHub Repo&lt;/h5&gt;
&lt;p&gt;This is where your shared code will live. If you already have one, fantastic. If not, go ahead and create a new private repository on GitHub or Gitlab.&lt;/p&gt;
&lt;h5 id=&quot;step-2-generate-a-github-access-token&quot;&gt;Step 2: Generate a GitHub Access Token&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;Navigate to your GitHub settings (or Gitlab settings for those using Gitlab).&lt;/li&gt;
&lt;li&gt;Create a new personal access token (make sure it only has read access).&lt;/li&gt;
&lt;li&gt;Copy this token and keep it safe (you’ll need it soon).&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id=&quot;step-3-set-up-your-shared-repo&quot;&gt;Step 3: Set Up Your Shared Repo&lt;/h5&gt;
&lt;p&gt;In your shared repository's &lt;code&gt;package.json&lt;/code&gt;, set the &lt;code&gt;name&lt;/code&gt; field to whatever you'd like to call your package, e.g., &lt;code&gt;&quot;package-name&quot;: &quot;1.0.0&quot;&lt;/code&gt;.&lt;/p&gt;
&lt;h5 id=&quot;step-4-update-your-next-js-project-s-package-json&quot;&gt;Step 4: Update Your Next.js Project’s &lt;code&gt;package.json&lt;/code&gt;&lt;/h5&gt;
&lt;p&gt;In the &lt;code&gt;package.json&lt;/code&gt; of the Next.js project where you want to use your shared code:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;{
  &quot;dependencies&quot;: {
    &quot;package-name&quot;: &quot;git+https://&amp;lt;github_token&amp;gt;:*Emails are not allowed*/&amp;lt;user&amp;gt;/&amp;lt;repo&amp;gt;.git#branch-name&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;&amp;lt;github_token&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;user&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;repo&amp;gt;&lt;/code&gt;, and &lt;code&gt;branch-name&lt;/code&gt; with the appropriate values from your setup.&lt;/p&gt;
&lt;h5 id=&quot;final-steps-configuration-in-next-js&quot;&gt;Final Steps: Configuration in Next.js&lt;/h5&gt;
&lt;ol&gt;
&lt;li&gt;&lt;p&gt;In your &lt;code&gt;next.config.js&lt;/code&gt;, add the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;module.exports = {
    transpilePackages: [&quot;package-name&quot;],
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This tells Next.js to also compile your typescript code from the shared repo and transform it to javascript - that's basically what transpile means; a combination of the word 'transform' and 'compile'. Without this, you'll get module parsing errors for your typescript (.ts) files.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If you're using Tailwind CSS, update your &lt;code&gt;tailwind.config.js&lt;/code&gt; to include your shared package:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-javascript&quot;&gt;module.exports = {
    content: [
        &quot;./node_modules/&amp;lt;package_name&amp;gt;/src/**/*.{js,ts,jsx,tsx,html,mdx}&quot;,
        &quot;./node_modules/&amp;lt;package_name&amp;gt;/app/**/*.{js,ts,jsx,tsx,html,mdx}&quot;,
    ],
    //… other tailwind config
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Replace &lt;code&gt;&amp;lt;package_name&amp;gt;&lt;/code&gt; with the name of your shared repo. Also, if you aren't using the &lt;code&gt;src&lt;/code&gt; folder, feel free to adjust the values according to your setup&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Now run &lt;code&gt;npm install&lt;/code&gt; and voila! your shared package has now been installed as an NPM dependency and you can use it in your project.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h5 id=&quot;before-we-finish-just-yet&quot;&gt;Before We Finish Just Yet&lt;/h5&gt;
&lt;p&gt;Always remember to update the version number in your package.json with a higher value each time you make a change to your shared repo. After words, run &lt;code&gt;npm update &amp;lt;package_name&amp;gt;&lt;/code&gt; where &lt;code&gt;&amp;lt;package_name&amp;gt;&lt;/code&gt; is the name of your shared package/repo. This will ensure that your changes in the shared package's changes are published to your project.&lt;/p&gt;
&lt;h4 id=&quot;conclusion-1&quot;&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Incorporating private GitHub repos as npm packages in your Next.js projects not only keeps your code organized and secure but also significantly boosts your productivity. You no longer need to duplicate code or compromise your privacy by publishing sensitive code to public registries.&lt;/p&gt;
&lt;p&gt;So, what are you waiting for? Dive in and streamline your projects today. And remember, it’s these smart shortcuts that often make a big difference in the efficiency and success of your development efforts.&lt;/p&gt;
</description>
<category>Articles</category>
<guid isPermaLink="true">https://coderlegion.com/5276/streamlining-your-next-js-project-with-private-github-gitlab-repositories-npm-packages</guid>
<pubDate>Wed, 10 Sep 2025 16:53:12 +0000</pubDate>
</item>
<item>
<title>Publish your packages to NPM automatically with GitHub Actions</title>
<link>https://coderlegion.com/4825/publish-your-packages-to-npm-automatically-with-github-actions</link>
<description>&lt;blockquote&gt;&lt;p&gt;Automating publication prevents manual errors, speeds up releases, and forces you to maintain a repeatable and transparent process.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In this article, we're going to create a GitHub Actions workflow that &lt;strong&gt;publishes to NPM&lt;/strong&gt; when you push to &lt;code&gt;main&lt;/code&gt;. The flow installs dependencies, runs tests, compiles, and, if all goes well, &lt;strong&gt;publishes&lt;/strong&gt; the package using a secure token stored as a secret.&lt;/p&gt;
&lt;h4 id=&quot;what-is-the-purpose-of-the-workflow-&quot;&gt;What is the purpose of the workflow?&lt;/h4&gt;
&lt;p&gt;Every time you merge changes to &lt;code&gt;main&lt;/code&gt;, we want to:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Install dependencies&lt;/li&gt;
&lt;li&gt;Run tests&lt;/li&gt;
&lt;li&gt;Build the package&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Publish&lt;/strong&gt; to NPM using secure credentials&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This standardizes the publishing process and reduces the risk of human error.&lt;/p&gt;
&lt;h4 id=&quot;general-workflow-structure&quot;&gt;General workflow structure&lt;/h4&gt;
&lt;pre&gt;&lt;code&gt;name: Publish to NPM
on:
  push:
    branches:
      - main

jobs:
  publish:
    name: publish
    runs-on: ubuntu-latest
    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node
        uses: actions/setup-node@v4
        with:
          node-version: &quot;22&quot;

      - name: Install dependencies
        run: npm ci

      - name: Run tests
        run: npm run test

      - name: Build package
        run: npm run build

      - name: Publish to NPM
        uses: JS-DevTools/npm-publish@v3
        with:
          token: ${{ secrets.NPM_TOKEN }}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;-what-does-each-step-do-&quot;&gt;️ ️ What does each step do?&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;on: push to main&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Triggers the workflow when pushing to the &lt;code&gt;main&lt;/code&gt; branch. If you prefer to publish only with tags, you can change the trigger to &lt;code&gt;on: push: tags: - ‘v*’&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;actions/checkout@v4&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Downloads the repository to the runner.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;actions/setup-node@v4&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Configures Node.js (v22 in the example). You can enable &lt;code&gt;cache: ‘npm’&lt;/code&gt; to speed up installations.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;npm ci / npm install&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Installs dependencies. &lt;code&gt;npm ci&lt;/code&gt; is faster and more reproducible in CI if you use &lt;code&gt;package-lock.json&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;npm run test and npm run build&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Verify that the package passes tests and compiles correctly before publishing.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JS-DevTools/npm-publish@v3&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Publish the package by reading &lt;code&gt;package.json&lt;/code&gt;. Use the token you pass through &lt;code&gt;with.token&lt;/code&gt;.&lt;/p&gt;
&lt;h4 id=&quot;authentication-with-npm-npm-token&quot;&gt;Authentication with NPM: NPM_TOKEN&lt;/h4&gt;
&lt;p&gt;To publish, you need a token in &lt;strong&gt;NPM&lt;/strong&gt; and save it as a secret in GitHub:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Create an &lt;strong&gt;Automation Token&lt;/strong&gt; in your NPM account (recommended for CI; respects 2FA and allows automatic publishing).&lt;/li&gt;
&lt;li&gt;In GitHub, go to &lt;strong&gt;Settings&lt;/strong&gt; → &lt;strong&gt;Secrets and variables&lt;/strong&gt; → &lt;strong&gt;Actions&lt;/strong&gt; → &lt;strong&gt;New repository secret&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Create the secret &lt;code&gt;NPM_TOKEN&lt;/code&gt; with the value of the NPM token.&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;&lt;p&gt;  If your package is scoped (e.g., &lt;code&gt;@your-org/package&lt;/code&gt;), make sure that &lt;code&gt;package.json&lt;/code&gt; contains &lt;code&gt;“name”: “@your-org/package”&lt;/code&gt; and &lt;code&gt;“publishConfig”: { ‘access’: “public” }&lt;/code&gt; if you want it to be public.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Example of &lt;code&gt;publishConfig&lt;/code&gt; in &lt;code&gt;package.json&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;name&quot;: &quot;@tu-org/tu-paquete&quot;,
  &quot;version&quot;: &quot;1.2.3&quot;,
  &quot;publishConfig&quot;: {
    &quot;access&quot;: &quot;public&quot;,
    &quot;tag&quot;: &quot;latest&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;dry-run-and-version-control&quot;&gt;Dry-run and version control&lt;/h4&gt;
&lt;p&gt;&lt;strong&gt;Dry-run:&lt;/strong&gt; test the flow without actually publishing:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- name: Publish to NPM (dry run)
  uses: JS-DevTools/npm-publish@v3
  with:
    token: ${{ secrets.NPM_TOKEN }}
    dry-run: true
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Versioning:&lt;/strong&gt; this flow publishes the version specified in &lt;code&gt;package.json&lt;/code&gt;. Be sure to &lt;strong&gt;update the version&lt;/strong&gt; before merging to &lt;code&gt;main&lt;/code&gt;.&lt;br&gt;
If you use automatic versioning (e.g., Conventional Commits + automatic releases), integrate that stage &lt;strong&gt;before&lt;/strong&gt; the publication step.&lt;/p&gt;
&lt;h4 id=&quot;-final-result&quot;&gt;✅ Final result&lt;/h4&gt;
&lt;p&gt;With this workflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You publish to NPM in a &lt;strong&gt;consistent and repeatable&lt;/strong&gt; manner.&lt;/li&gt;
&lt;li&gt;You avoid broken releases thanks to &lt;strong&gt;preliminary tests + builds&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You keep credentials secure with &lt;strong&gt;GitHub Secrets&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;You reduce manual steps and time between merge and release.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;would-you-rather-set-it-up-with-a-visual-interface-&quot;&gt;Would you rather set it up with a visual interface?&lt;/h4&gt;
&lt;p&gt;If you're interested in defining this flow with a visual interface and getting the YAML instantly, you can use &lt;strong&gt;OctoLab&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Step-by-step visual editor&lt;/li&gt;
&lt;li&gt;Dynamic fields for actions and tokens&lt;/li&gt;
&lt;li&gt;Real-time YAML preview&lt;/li&gt;
&lt;li&gt;Built-in validations&lt;/li&gt;
&lt;li&gt;Copy/download and you're done&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt; Try it out: &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.octolab.app/templates/npm-publish&quot;&gt;https://www.octolab.app/templates/npm-publish&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;conclusion-2&quot;&gt;Conclusion&lt;/h4&gt;
&lt;p&gt;Automating publication to NPM with GitHub Actions reduces friction and errors. With a simple configuration—tests, build, and a publish step with a secure token—you can standardize releases and focus on building value.&lt;/p&gt;
&lt;p&gt;If this was helpful, let me know what you would like to add to the template (tags, prereleases, monorepos, changelogs, etc.). Let's keep going!&lt;/p&gt;
</description>
<category>GitHub Actions</category>
<guid isPermaLink="true">https://coderlegion.com/4825/publish-your-packages-to-npm-automatically-with-github-actions</guid>
<pubDate>Tue, 19 Aug 2025 11:21:15 +0000</pubDate>
</item>
<item>
<title>Create Launcher CLI (npm Package)</title>
<link>https://coderlegion.com/4403/create-launcher-cli-npm-package</link>
<description>&lt;blockquote&gt;&lt;p&gt;“Why am I still copy pasting the same setup files again and again?”&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you’ve ever asked yourself this after starting yet another web project... &lt;/p&gt;
&lt;p&gt;You’re not alone — I’ve been there too.&lt;/p&gt;
&lt;p&gt;So I built something to make project bootstrapping painless.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-meet-create-launcher&quot;&gt;️ Meet &lt;code&gt;create-launcher&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;A zero-config CLI tool that lets you scaffold modern web projects instantly, with everything pre wired and ready to go.&lt;/p&gt;
&lt;h4 id=&quot;features-at-a-glance&quot;&gt;Features at a Glance&lt;/h4&gt;
&lt;ul&gt;
&lt;li&gt;✅ Interactive CLI with prompts  &lt;/li&gt;
&lt;li&gt;⚛️ Project templates:  &lt;ul&gt;
&lt;li&gt;React (Vite)  &lt;/li&gt;
&lt;li&gt;Next.js  &lt;/li&gt;
&lt;li&gt;Eleventy (11ty)  &lt;/li&gt;
&lt;li&gt;Vanilla JavaScript &amp;amp; TypeScript  &lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Tailwind CSS integration (optional)  &lt;/li&gt;
&lt;li&gt;TypeScript support  &lt;/li&gt;
&lt;li&gt;Pick your favorite package manager (npm, yarn, pnpm)  &lt;/li&gt;
&lt;li&gt;ESLint pre-configured  &lt;/li&gt;
&lt;li&gt;⚡ Auto installs everything you need&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;get-started-in-one-line&quot;&gt;Get Started in One Line&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;npx create-launcher
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The CLI walks you through setup, just answer a few prompts, and boom &lt;br&gt;
Your project is scaffolded and dependencies installed. No extra config.&lt;/p&gt;
&lt;h3 id=&quot;see-it-in-action&quot;&gt;See It In Action&lt;/h3&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8l85iar13pp2m6i4qmie.gif&quot; alt=&quot;create-launcher Demo&quot;&gt;&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;⚡ Creating a React + TypeScript + Tailwind project in under 30 seconds&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;why-i-built-this-1&quot;&gt;Why I Built This&lt;/h3&gt;
&lt;p&gt;As a dev who loves shipping fast, I kept hitting the same roadblocks every time I started a new project - boilerplate, repeated setup, switching between Vite and Next.js, etc.&lt;/p&gt;
&lt;p&gt;Now, with &lt;code&gt;create-launcher&lt;/code&gt;, I can:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Start a project in under 15 seconds&lt;/li&gt;
&lt;li&gt;Have a consistent, clean folder structure&lt;/li&gt;
&lt;li&gt;Focus on writing code, not configuration&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;check-it-out&quot;&gt;Check It Out&lt;/h3&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.npmjs.com/package/create-launcher&quot;&gt;NPM Package&lt;/a&gt;&lt;br&gt;
 &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/pranav89624/create-launcher&quot;&gt;GitHub Repo&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;ideas-for-future-versions&quot;&gt;Ideas for Future Versions&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Templates On (Vue.js, Svelte, Angular etc.)&lt;/li&gt;
&lt;li&gt;Git init + first commit&lt;/li&gt;
&lt;li&gt;Prettier integration&lt;/li&gt;
&lt;li&gt;Optional GitHub Actions CI setup&lt;/li&gt;
&lt;li&gt;Custom template support&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;feedback-welcome&quot;&gt;Feedback Welcome&lt;/h3&gt;
&lt;p&gt;I’d love to hear what you think.&lt;br&gt;
Found a bug? Have a feature request? PRs are welcome, and stars always appreciated ⭐&lt;/p&gt;
&lt;p&gt;Let’s make spinning up projects feel exciting again.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-about-the-author&quot;&gt;♂️ About the Author&lt;/h3&gt;
&lt;p&gt;I'm &lt;strong&gt;Pranav Verma&lt;/strong&gt;, a passionate Full Stack Developer and tech enthusiast exploring the intersection of &lt;strong&gt;DevTools, AI, and modern web development&lt;/strong&gt;. I love building tools that simplify developer workflows and enhance productivity.&lt;/p&gt;
&lt;p&gt; &lt;strong&gt;Connect with me&lt;/strong&gt;:  &lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/vermapranav/&quot;&gt;LinkedIn&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/pranav89624&quot;&gt;Twitter / X&lt;/a&gt;  &lt;/li&gt;
&lt;li&gt;&lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/pranav89624&quot;&gt;GitHub&lt;/a&gt;  &lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;p&gt; If you liked this project, don’t forget to ⭐ it on &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/pranav89624/create-launcher&quot;&gt;GitHub&lt;/a&gt; and share it with fellow devs!&lt;/p&gt;
&lt;p&gt;And, if you want to contribute something helpful to the community, just read the &lt;code&gt;CONTRIBUTING.md&lt;/code&gt; in GitHub repo and start helping others and yourselves.&lt;/p&gt;
</description>
<category>CLI Tools &amp; Scripts</category>
<guid isPermaLink="true">https://coderlegion.com/4403/create-launcher-cli-npm-package</guid>
<pubDate>Sun, 03 Aug 2025 08:17:14 +0000</pubDate>
</item>
<item>
<title>How npm install Works? What Really Happens When You Hit Enter</title>
<link>https://coderlegion.com/4355/how-npm-install-works-what-really-happens-when-you-hit-enter</link>
<description>&lt;p&gt;You've just cloned a new repository. The first command you run, almost by muscle memory, is &lt;code&gt;npm install&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But have you ever paused to wonder why a fresh &lt;code&gt;npm install&lt;/code&gt; sometimes takes 30 seconds, and other times 3 minutes? What hidden mechanisms kick in? What separates a smooth install from one that spews errors about peer dependencies or breaks your build?&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt; isn't a simple download command; it's a complex, multi-stage process. In this deep dive, we'll dissect it piece by piece, equipping you with the under the hood intelligence to debug issues, optimize your workflows, and understand the trade offs of the entire Node.js ecosystem.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-cast-of-characters-key-files-and-concepts&quot;&gt;The Cast of Characters: Key Files and Concepts&lt;/h3&gt;
&lt;p&gt;Before we follow the trail, let's meet the key players.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt;: Your project's manifest. It’s the &lt;strong&gt;shopping list&lt;/strong&gt; that names your direct dependencies and their allowed version ranges (e.g., &lt;code&gt;&quot;react&quot;: &quot;^18.2.0&quot;&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;package-lock.json&lt;/code&gt;&lt;/strong&gt;: The &lt;strong&gt;detailed, immutable receipt&lt;/strong&gt; of your installation. It records the &lt;em&gt;exact&lt;/em&gt; version of every single package (including sub-dependencies), ensuring &lt;strong&gt;deterministic installs&lt;/strong&gt;. This is your guarantee that every machine gets the same &lt;code&gt;node_modules&lt;/code&gt; tree.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;&lt;code&gt;node_modules&lt;/code&gt;&lt;/strong&gt;: The directory where the actual package code is stored. It's the &quot;pantry&quot; that gets filled with ingredients.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The npm Registry&lt;/strong&gt;: The public &quot;supermarket&quot; of packages, located at &lt;code&gt;registry.npmjs.org&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The npm Cache&lt;/strong&gt;: A local storage area on your computer (find it with &lt;code&gt;npm config get cache&lt;/code&gt;) where &lt;code&gt;npm&lt;/code&gt; keeps copies of downloaded packages to speed up future installs.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cameo Appearance: Registry Proxies&lt;/strong&gt;: In many corporate environments, you won't hit the public npm registry directly. Instead, you'll go through a proxy like &lt;a rel=&quot;nofollow&quot; href=&quot;https://verdaccio.org/&quot;&gt;&lt;strong&gt;Verdaccio&lt;/strong&gt;&lt;/a&gt; or &lt;a rel=&quot;nofollow&quot; href=&quot;https://jfrog.com/artifactory/&quot;&gt;&lt;strong&gt;JFrog Artifactory&lt;/strong&gt;&lt;/a&gt;. These cache packages on the company's network, providing security, compliance, and faster installs for common dependencies. They act as a middle man, changing the &lt;code&gt;npm Registry&lt;/code&gt; character in our story to a local one.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-process-at-a-glance-a-flowchart&quot;&gt;The Process at a Glance: A Flowchart&lt;/h3&gt;
&lt;p&gt;Here's a high level map of the journey we're about to explore.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://dev-to-uploads.s3.amazonaws.com/uploads/articles/vpwd5yjmrzirlwgbg9dv.png&quot; alt=&quot;Flowchart - npm install&quot;&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;the-step-by-step-process&quot;&gt;The Step by Step Process&lt;/h3&gt;
&lt;h4 id=&quot;1-reading-the-manifests&quot;&gt;1. Reading the Manifests&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;npm&lt;/code&gt; begins by reading the &lt;code&gt;dependencies&lt;/code&gt;, &lt;code&gt;devDependencies&lt;/code&gt;, and &lt;code&gt;optionalDependencies &lt;/code&gt;from your &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-json&quot;&gt;// package.json (excerpt)
{
  &quot;name&quot;: &quot;my-project&quot;,
  &quot;version&quot;: &quot;1.0.0&quot;,
  &quot;dependencies&quot;: {
    &quot;express&quot;: &quot;^4.18.2&quot;,
    &quot;dotenv&quot;: &quot;^16.3.1&quot;
  },
  &quot;devDependencies&quot;: {
    &quot;nodemon&quot;: &quot;^2.0.22&quot;
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4 id=&quot;2-resolving-the-dependency-tree&quot;&gt;2. Resolving the Dependency Tree&lt;/h4&gt;
&lt;p&gt;This is where the real logic kicks in. &lt;code&gt;npm &lt;/code&gt;builds a complete tree of all packages required.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;If a &lt;code&gt;package-lock.json&lt;/code&gt; exists, it's treated as the absolute source of truth, ensuring a deterministic build.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;If not, &lt;code&gt;npm &lt;/code&gt;resolves versions based on &lt;code&gt;package.json&lt;/code&gt; ranges and generates a new &lt;code&gt;package-lock.json&lt;/code&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;3-checking-the-local-cache&quot;&gt;3. Checking the Local Cache&lt;/h4&gt;
&lt;p&gt;Before making any network requests, &lt;code&gt;npm &lt;/code&gt;checks its local cache for every package in the tree. If a specific version exists, it's used instantly.&lt;/p&gt;
&lt;h4 id=&quot;4-fetching-from-the-registry&quot;&gt;4. Fetching from the Registry&lt;/h4&gt;
&lt;p&gt;Any packages not found in the cache are downloaded from the configured registry (public npm or your company's proxy).&lt;/p&gt;
&lt;h4 id=&quot;5-unpacking-and-laying-out-node-modules&quot;&gt;5. Unpacking and Laying out &lt;code&gt;node_modules&lt;/code&gt;&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;npm &lt;/code&gt;unpacks the downloaded tarballs into your project's &lt;code&gt;node_modules&lt;/code&gt; folder, creating a &quot;flat&quot; dependency tree to minimize duplication.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;A Quick Sidebar on Peer Dependencies&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;You've likely seen warnings like &lt;code&gt;UNMET PEER DEPENDENCY&lt;/code&gt;. A peer dependency is when a package expects you (the developer) to install another specific package in your project. For example, &lt;code&gt;react-router-dom&lt;/code&gt; requires &lt;code&gt;react&lt;/code&gt; as a peer.&lt;br&gt;
Before npm 7, you had to install these manually. Now, npm 7+ auto-installs them, but it will still warn you if it detects a version conflict, which is a signal you need to resolve in your &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h4 id=&quot;6-running-lifecycle-scripts&quot;&gt;6. Running Lifecycle Scripts&lt;/h4&gt;
&lt;p&gt;This is a powerful, but risky, step. Packages can define scripts (&lt;code&gt;preinstall&lt;/code&gt;, &lt;code&gt;install&lt;/code&gt;, &lt;code&gt;postinstall&lt;/code&gt;) that run arbitrary code on your machine after installation. This is used for tasks like compiling native addons.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Watch Out: The Security Risk of Lifecycle Scripts&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;Because &lt;code&gt;postinstall &lt;/code&gt;scripts can run any code, they are a potential attack vector (e.g., a malicious package stealing environment variables).&lt;br&gt;
&lt;strong&gt;The Defense&lt;/strong&gt;: Be cautious about the dependencies you add. Use &lt;code&gt;npm audit&lt;/code&gt; regularly to check for known vulnerabilities in your project's dependency tree.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h3 id=&quot;beyond-npm-a-look-at-the-alternatives&quot;&gt;Beyond &lt;code&gt;npm&lt;/code&gt;: A Look at the Alternatives&lt;/h3&gt;
&lt;p&gt;Understanding &lt;code&gt;npm install&lt;/code&gt; is great, but a true expert knows the landscape. Here’s how &lt;code&gt;npm&lt;/code&gt;'s philosophy compares to its main rivals.&lt;/p&gt;
&lt;h4 id=&quot;pnpm-the-disk-space-saver&quot;&gt;&lt;code&gt;pnpm&lt;/code&gt;: The Disk Space Saver&lt;/h4&gt;
&lt;p&gt;&lt;code&gt;pnpm &lt;/code&gt;takes a radically different approach. Instead of copying packages into every project's &lt;code&gt;node_modules&lt;/code&gt;, it stores all packages in a single, global content-addressable store on your machine. It then uses symlinks (or hardlinks) to link them into your &lt;code&gt;node_modules&lt;/code&gt; folder.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pro: Massive disk space savings and significantly faster installs.&lt;/li&gt;
&lt;li&gt;Con: Can sometimes have edge-case issues with tools that don't correctly resolve symlinks.&lt;/li&gt;
&lt;/ul&gt;
&lt;h4 id=&quot;yarn-v2-the-plug-n-play-innovator&quot;&gt;&lt;code&gt;Yarn (v2+)&lt;/code&gt;: The Plug'n'Play Innovator&lt;/h4&gt;
&lt;p&gt;Yarn v2+ (often called &quot;Berry&quot;) gets rid of the &lt;code&gt;node_modules&lt;/code&gt; folder entirely. Instead, it generates a single &lt;code&gt;.pnp.cjs&lt;/code&gt; file. This file tells Node.js exactly where to find the code for every required package (which are stored in a compressed &lt;code&gt;.zip&lt;/code&gt; format in your &lt;code&gt;.yarn/cache&lt;/code&gt; directory).&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Pro: Extremely fast and reliable installs, zero phantom dependencies, and a better check-in-to-git experience.&lt;/li&gt;
&lt;li&gt;Con: Requires editor and tool integration to work, which can be a steeper learning curve for teams.&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;conclusion-your-turn-to-investigate&quot;&gt;Conclusion: Your Turn to Investigate&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;npm install&lt;/code&gt; is a cornerstone of modern web development, balancing speed, determinism, and a vast ecosystem. Now you know its secrets, from the lockfile's critical role to the security implications of its final steps.&lt;/p&gt;
&lt;p&gt;Here’s your challenge: Next time you run &lt;code&gt;npm install&lt;/code&gt; on a project, add the &lt;code&gt;--timing&lt;/code&gt; flag (&lt;code&gt;npm install --timing&lt;/code&gt;). This will generate a performance report (&lt;code&gt;npm-debug.log&lt;/code&gt;). Open it up, find the total time, and see which step was the bottleneck.&lt;/p&gt;
&lt;p&gt;Share your findings in the comments!&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;let-s-connect&quot;&gt;Let's Connect&lt;/h3&gt;
&lt;p&gt;Thanks for diving deep into the world of &lt;code&gt;npm install&lt;/code&gt; with me!&lt;/p&gt;
&lt;p&gt;If you found this helpful:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Share it with a teammate who's always puzzled by lockfiles&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;⭐ Leave a reaction or comment, I'd love to hear your thoughts!&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Got a tip or trick around &lt;code&gt;npm&lt;/code&gt;, &lt;code&gt;pnpm&lt;/code&gt;, or dependency management? Drop it below.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Follow me here on Dev.to or connect with me on &lt;/p&gt;
&lt;p&gt;• &lt;a rel=&quot;nofollow&quot; href=&quot;https://www.linkedin.com/in/vermapranav&quot;&gt;LinkedIn&lt;/a&gt; • &lt;a rel=&quot;nofollow&quot; href=&quot;https://x.com/pranav89624&quot;&gt;X (Twitter)&lt;/a&gt; • &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/pranav89624&quot;&gt;GitHub&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;for more dev deep dives.&lt;/p&gt;
&lt;p&gt;Till next time,  &lt;/p&gt;
&lt;p&gt;&lt;strong&gt;– Pranav Verma&lt;/strong&gt; &lt;/p&gt;
</description>
<category>Command Line Tools</category>
<guid isPermaLink="true">https://coderlegion.com/4355/how-npm-install-works-what-really-happens-when-you-hit-enter</guid>
<pubDate>Wed, 30 Jul 2025 19:52:33 +0000</pubDate>
</item>
<item>
<title>Meet Hippoo: The Lightning-Fast CLI Tool Every Dev Needs to Check NPM Package Sizes</title>
<link>https://coderlegion.com/3032/meet-hippoo-the-lightning-fast-cli-tool-every-dev-needs-to-check-npm-package-sizes</link>
<description>&lt;h3 id=&quot;introducing-hippoo-a-blazing-fast-cli-tool-that-helps-you-analyze-and-compare-npm-package-sizes-before-you-install-them-built-by-james-akpan&quot;&gt;Introducing Hippoo — a blazing-fast CLI tool that helps you analyze and compare npm package sizes before you install them. Built by &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/codiejay/&quot;&gt;James Akpan&lt;/a&gt;&lt;/h3&gt;
&lt;blockquote&gt;&lt;p&gt;“Don’t just install it. Inspect it.”&lt;br&gt;
— &lt;em&gt;Hippoo’s unspoken philosophy&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;In the age of bloated bundles and performance-first web apps, &lt;strong&gt;Hippoo&lt;/strong&gt; steps in like a digital bodyguard. — Hippoo is a smart, sleek CLI tool that helps you &lt;strong&gt;check and compare npm package sizes&lt;/strong&gt; &lt;em&gt;before&lt;/em&gt; they enter your codebase.&lt;/p&gt;
&lt;p&gt;No more surprises. No more blind installs.&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;what-is-hippoo-&quot;&gt;What is Hippoo?&lt;/h3&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;Hippoo&lt;/strong&gt; (yes, with two o's) is a lightning-fast command-line tool to analyze and compare npm packages — giving you clear insights about:&lt;/p&gt;
&lt;/blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Minified &amp;amp; Gzipped Size&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Dependency Count&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;️ &lt;strong&gt;Download Time&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Tree-shaking Support&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Hippoo Score (0–10)&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Smart Recommendations&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Hippoo answers the real dev questions:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Is this package too heavy?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Should I choose React or Angular?&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Will this slow down my app?&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;installation-3&quot;&gt;Installation&lt;/h3&gt;
&lt;p&gt;You can install Hippoo globally using either &lt;strong&gt;npm&lt;/strong&gt; or &lt;strong&gt;yarn&lt;/strong&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# Using npm
npm install -g hippoo

# Using yarn
yarn global add hippoo
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;basic-usage-1&quot;&gt;Basic Usage&lt;/h3&gt;
&lt;p&gt; Check a single package:&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hippoo react@18.0.0
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;-compare-multiple-packages-&quot;&gt;⚖️ Compare multiple packages:&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hippoo compare react angular
&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;-sample-output-real-example-&quot;&gt;✨ Sample Output (Real Example)&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hippoo compare react angular
&lt;/code&gt;&lt;/pre&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;&lt;th&gt;Metric        &lt;/th&gt;&lt;th&gt;React   &lt;/th&gt;&lt;th&gt;Angular&lt;/th&gt;&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;&lt;td&gt;Size          &lt;/td&gt;&lt;td&gt;7.4 kB  &lt;/td&gt;&lt;td&gt;181.0 kB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Gzipped       &lt;/td&gt;&lt;td&gt;2.8 kB  &lt;/td&gt;&lt;td&gt;62.3 kB&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Download Time &lt;/td&gt;&lt;td&gt;1ms     &lt;/td&gt;&lt;td&gt;2ms&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Dependencies  &lt;/td&gt;&lt;td&gt;0       &lt;/td&gt;&lt;td&gt;0&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Tree-Shake    &lt;/td&gt;&lt;td&gt;❌       &lt;/td&gt;&lt;td&gt;❌&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Hippoo Score  &lt;/td&gt;&lt;td&gt;8/10  &lt;/td&gt;&lt;td&gt;6/10&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt; Best for Performance-Critical Apps&lt;/strong&gt;: React&lt;br&gt;
&lt;strong&gt; Best for Feature-Rich Projects&lt;/strong&gt;: Angular&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;-command-reference&quot;&gt;️ Command Reference&lt;/h3&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;hippoo [package-name] [options]
hippoo compare [pkg1] [pkg2] [...]
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;Options&lt;/strong&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-v, --version&lt;/code&gt; → Show version number&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-d, --detailed&lt;/code&gt; → Show detailed breakdown&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-j, --json&lt;/code&gt; → Output results in JSON&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;-h, --help&lt;/code&gt; → Show help info&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;why-use-hippoo-&quot;&gt;Why Use Hippoo?&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt; Size Analysis&lt;/strong&gt; — Know what you're adding.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;⚖️ Package Comparison&lt;/strong&gt; — Pick the better tool.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt; Performance Insights&lt;/strong&gt; — Stay light and fast.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt; Smart Recommendations&lt;/strong&gt; — Use the right tool for the task/project.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h3 id=&quot;resources&quot;&gt;Resources&lt;/h3&gt;
&lt;p&gt; &lt;a rel=&quot;nofollow&quot; href=&quot;https://hippoo-sigma.vercel.app/&quot;&gt;Official Website&lt;/a&gt;&lt;br&gt;
 &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/codiejay/hippoo&quot;&gt;GitHub Repository&lt;/a&gt;&lt;br&gt;
 &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/codiejay/hippoo/blob/master/LICENSE&quot;&gt;MIT License&lt;/a&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h3 id=&quot;final-thoughts-1&quot;&gt;Final Thoughts&lt;/h3&gt;
&lt;p&gt;Hippoo isn’t just a tool.&lt;br&gt;
It’s a mindset — inspect before install.&lt;br&gt;
Analyze before adding. Optimize before bloating.&lt;br&gt;
Big ups to &lt;a rel=&quot;nofollow&quot; href=&quot;https://github.com/codiejay&quot;&gt;@codiejay (James Akpan)&lt;/a&gt; for building something every dev can use. Let’s support, test, and spread the word. &lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Next time you go to install a package, pause and ask: &lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;“Did I Hippoo it first?” &lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt; Feel free to share thoughts, issues, or praise in the comments. And if you're vibing with Hippoo, don’t forget to ⭐ the repo!&lt;/p&gt;
</description>
<category>Web Dev</category>
<guid isPermaLink="true">https://coderlegion.com/3032/meet-hippoo-the-lightning-fast-cli-tool-every-dev-needs-to-check-npm-package-sizes</guid>
<pubDate>Wed, 14 May 2025 23:27:57 +0000</pubDate>
</item>
</channel>
</rss>