<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Anton Golub</title>
    <description>The latest articles on DEV Community by Anton Golub (@antongolub).</description>
    <link>https://dev.to/antongolub</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F397476%2F7f754171-a3f3-4aa3-9740-15b2341cf7ae.jpeg</url>
      <title>DEV Community: Anton Golub</title>
      <link>https://dev.to/antongolub</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/antongolub"/>
    <language>en</language>
    <item>
      <title>yarn.lock: may the `--force` be with you</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Tue, 29 Sep 2026 06:56:40 +0000</pubDate>
      <link>https://dev.to/antongolub/yarnlock-may-the-force-be-with-you-50pm</link>
      <guid>https://dev.to/antongolub/yarnlock-may-the-force-be-with-you-50pm</guid>
      <description>&lt;p&gt;&lt;em&gt;Part 2 brought Yarn level with &lt;code&gt;npm audit fix&lt;/code&gt;; this part goes past it. &lt;code&gt;--force&lt;/code&gt; is the one move npm already had — the rest is new: gate a fix on the Node you target, the licenses you allow, the module format it must keep, and scope it to production, or to the one workspace you're touching.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/antongolub/yarn-audit-still-does-not-fix-3n3"&gt;&lt;code&gt;yarn audit&lt;/code&gt; [still does not] &lt;code&gt;fix&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/antongolub/-yarnlock-you-cant-sed-a-graph-5ffp"&gt;&lt;code&gt;yarn.lock&lt;/code&gt;: you can't &lt;code&gt;sed&lt;/code&gt; a graph&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yarn.lock&lt;/code&gt;: may the &lt;code&gt;--force&lt;/code&gt; be with you&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;"Lowest version that clears the advisory" (Part 2) is necessary, not sufficient: that fix — or the closure it drags in — can need a &lt;strong&gt;newer Node than you target&lt;/strong&gt;, a &lt;strong&gt;license you don't allow&lt;/strong&gt;, or fall &lt;strong&gt;outside a range you pinned&lt;/strong&gt;. &lt;code&gt;--immutable&lt;/code&gt; won't catch it — it checks the lockfile &lt;em&gt;reproduces&lt;/em&gt;, not that the result &lt;em&gt;runs&lt;/em&gt; or &lt;em&gt;complies&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Landing a fix is a &lt;strong&gt;dial&lt;/strong&gt;. &lt;code&gt;--force&lt;/code&gt; bends ranges and rewrites &lt;code&gt;package.json&lt;/code&gt; to force a patch in; the opt-in &lt;strong&gt;constraints&lt;/strong&gt; — &lt;code&gt;--engines&lt;/code&gt;, &lt;code&gt;--license&lt;/code&gt;, &lt;code&gt;--package-type&lt;/code&gt; — pull the other way, applying a fix only if it won't introduce a &lt;em&gt;new&lt;/em&gt; break, and &lt;code&gt;--safe&lt;/code&gt; flips the automatable ones on together. The contract is &lt;strong&gt;guarantee-or-flag&lt;/strong&gt;: a compatible fix or an attributed skip, never a silent incompatible one.&lt;/li&gt;
&lt;li&gt;Two more knobs narrow &lt;em&gt;which&lt;/em&gt; advisories, not just how a fix resolves: &lt;strong&gt;&lt;code&gt;--production&lt;/code&gt;&lt;/strong&gt; skips dev-only vulnerabilities (they never ship), &lt;strong&gt;&lt;code&gt;--workspace&lt;/code&gt;&lt;/strong&gt; scopes a monorepo fix to the packages you name.&lt;/li&gt;
&lt;li&gt;Every skip is &lt;strong&gt;attributed&lt;/strong&gt; — the axis, the package, the versions it tried — so you can widen, exclude, or accept. Constraints, &lt;code&gt;--safe&lt;/code&gt;, scoping, and precise suppression (&lt;code&gt;--ignore&lt;/code&gt; a CVE, &lt;code&gt;--exclude&lt;/code&gt; a package) are all things &lt;code&gt;npm audit fix&lt;/code&gt; doesn't do: this is where yaf leads it, not mirrors it.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The fix that installed clean and broke anyway
&lt;/h2&gt;

&lt;p&gt;Part 2 ended on a strong claim: the patched lockfile passes &lt;code&gt;yarn install --immutable&lt;/code&gt;, byte-for-byte what Yarn would compute. That invariant is real, and it's the whole design — but it's worth being clear about what it &lt;em&gt;doesn't&lt;/em&gt; promise.&lt;/p&gt;

&lt;p&gt;Immutable mode is a &lt;strong&gt;reproducibility&lt;/strong&gt; oracle. It verifies that the lockfile agrees with itself and with your manifests — same versions, same checksums, no drift. It does not verify that the versions it locked will &lt;em&gt;run&lt;/em&gt;. Berry doesn't check a dependency's &lt;code&gt;engines&lt;/code&gt; at install at all (npm merely warns, &lt;code&gt;EBADENGINE&lt;/code&gt;); licenses aren't its concern either; and a range you declared is checked for &lt;em&gt;consistency&lt;/em&gt;, not for whether the fix was a good idea.&lt;/p&gt;

&lt;p&gt;A security bump moves a package to its lowest patched version — and that version's dependencies, re-resolved the Part-2 way to the &lt;strong&gt;highest satisfying&lt;/strong&gt; version, pull in a transitive whose recent major quietly &lt;strong&gt;dropped old Node support&lt;/strong&gt;. &lt;code&gt;lru-cache@11&lt;/code&gt; wants Node 20; &lt;code&gt;glob@11&lt;/code&gt;, &lt;code&gt;rimraf@6&lt;/code&gt; — same. Your project targets Node 18. &lt;code&gt;yarn install&lt;/code&gt; is delighted: the graph resolves, the checksums match, &lt;code&gt;--immutable&lt;/code&gt; exits &lt;code&gt;0&lt;/code&gt;. Then CI runs the suite on Node 18 and something three levels down throws &lt;code&gt;SyntaxError&lt;/code&gt; on a syntax it doesn't recognize, or the process exits on an &lt;code&gt;engines&lt;/code&gt; assertion. The fix was "correct" by every check we'd built — and it broke the build.&lt;/p&gt;

&lt;p&gt;We hit exactly this on a real project: clean remediation, green install, and it died on Node 18 in CI. The lockfile was &lt;em&gt;right&lt;/em&gt;; the fix was &lt;em&gt;wrong for that project&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;So &lt;strong&gt;a fix has a footprint beyond the advisory&lt;/strong&gt; — and the real question is how much of &lt;em&gt;your&lt;/em&gt; project it's allowed to reshape to land. That's a dial. At one end, &lt;code&gt;--force&lt;/code&gt;: bend a consumer's range, rewrite a manifest, take the major bump — &lt;em&gt;make&lt;/em&gt; it fit. At the other, the constraints and &lt;code&gt;--safe&lt;/code&gt;: apply a fix only if it leaves your Node, your licenses, and your declared ranges alone. The rest of this part is those two ends, and the default that sits between them.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;--force&lt;/code&gt;: break to fix
&lt;/h2&gt;

&lt;p&gt;The forceful end of the dial is the oldest flag in the tool, and the bluntest. By default a fix has to &lt;em&gt;fit&lt;/em&gt;: it must stay within every surviving consumer's declared range &lt;strong&gt;and&lt;/strong&gt; within the range you declared for a direct dependency in &lt;code&gt;package.json&lt;/code&gt;. When it can't — the only patched version is a semver-major that overshoots a parent's &lt;code&gt;^1&lt;/code&gt;, or a dependency you pinned exactly — the default &lt;strong&gt;declines and reports&lt;/strong&gt;, because quietly applying it would hand you a lockfile your own manifests forbid:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Skipped (package.json pins these to a range the fix can't satisfy; re-run with --force …):
  lodash (pinned → "4.17.11" in packages/foo/package.json)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;--force&lt;/code&gt; overrides that. It takes the cross-major bump past a consumer that wanted the old line, and — the newer half of this — &lt;strong&gt;rewrites the direct-dependency range in your &lt;code&gt;package.json&lt;/code&gt;&lt;/strong&gt; so manifest and lockfile agree again (&lt;code&gt;"lodash": "4.17.11"&lt;/code&gt; → &lt;code&gt;"4.18.0"&lt;/code&gt;, &lt;code&gt;"~4.17.0"&lt;/code&gt; → &lt;code&gt;"~4.18.0"&lt;/code&gt;, preserving your operator). In a monorepo it finds the pin in the workspace that declared it and rewrites &lt;em&gt;that&lt;/em&gt; file, not the root. Both moves — cross the range, rewrite the pin — are exactly where the community plugin (Part 1) stops: it fixes &lt;em&gt;within&lt;/em&gt; a package's range and, when the only patch lies outside it, reports &lt;em&gt;no compatible patched version&lt;/em&gt; and gives up on it. &lt;code&gt;--force&lt;/code&gt; is the out-of-range flag its roadmap wanted.&lt;/p&gt;

&lt;p&gt;It works the same across formats: Berry models workspace→dependency edges in the graph, so its compatibility gate (Part 2) can already &lt;em&gt;see&lt;/em&gt; a workspace consumer; Yarn Classic can't, so reading the manifests is the uniform path — and it adds the &lt;code&gt;--force&lt;/code&gt; &lt;em&gt;rewrite&lt;/em&gt; the edge-only gate never had. Same move as &lt;code&gt;npm audit fix --force&lt;/code&gt;, done out loud.&lt;/p&gt;

&lt;p&gt;It earns its Star Wars reputation, though. The &lt;a href="https://instatunnel.my/blog/why-npm-audit-fix---force-is-a-terrible-idea" rel="noopener noreferrer"&gt;case against &lt;code&gt;npm audit fix --force&lt;/code&gt;&lt;/a&gt; is concrete: it installs versions &lt;strong&gt;outside your declared ranges&lt;/strong&gt; — semver-major, 1.2.0 straight to 5.0.0 if that's where the patch lives — so a function you call gets renamed or removed and the app won't start, or starts and quietly returns wrong answers. One forced top-level bump &lt;strong&gt;cascades&lt;/strong&gt; into dozens of moved transitives, each its own chance to break. It can even oscillate — downgrade, upgrade, downgrade — into a lockfile that differs from one run to the next. And a green test suite is thin cover: the real break tends to hide in an integration seam or an edge case that surfaces months later, sometimes as a &lt;em&gt;fresh&lt;/em&gt; vulnerability in a version nobody has battle-tested yet.&lt;/p&gt;

&lt;p&gt;yaf's &lt;code&gt;--force&lt;/code&gt; is that instinct, narrowed. It bumps to the &lt;strong&gt;lowest&lt;/strong&gt; version that clears the advisory, never the latest, so the jump is the smallest one that works; it moves the vulnerable package and re-resolves &lt;em&gt;its&lt;/em&gt; closure, not the whole tree; and it stays bounded by the Part 2 reproducibility check and whatever constraints you've set. So &lt;code&gt;--force&lt;/code&gt; here means &lt;em&gt;bend the ranges that block this fix&lt;/em&gt;, not &lt;em&gt;take the resolver off its leash&lt;/em&gt;. Sometimes the real fix is the major bump and you'd rather the tool take it and say so than hand you a red pipeline — but it's opt-in, loud, and paired with an opposite.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;--safe&lt;/code&gt;: skip to stabilize
&lt;/h2&gt;

&lt;p&gt;That was the forceful end. The other runs on the opposite instinct: when landing a fix would break something new, &lt;strong&gt;skip it&lt;/strong&gt; — leave the vulnerability flagged rather than trade it for a fresh breakage. It's the newer half, and still finding its edges. Before its mechanics, the policy, because it too departs from what npm does, on purpose.&lt;/p&gt;

&lt;p&gt;Run &lt;code&gt;npm audit fix&lt;/code&gt; and a bump that needs a newer engine still gets applied; you get an &lt;code&gt;EBADENGINE&lt;/code&gt; &lt;strong&gt;warning&lt;/strong&gt; and a lockfile that installs. That's the package-manager default: resolve the highest thing that satisfies semver, warn, move on. It's a reasonable default for an &lt;em&gt;always-on&lt;/em&gt; installer.&lt;/p&gt;

&lt;p&gt;It's the wrong default for an &lt;em&gt;opt-in remediation flag&lt;/em&gt;. If you go out of your way to tell the tool "this fix has to run on Node 18," you don't want a warning buried in install output three CI steps later — you want one of two things: a fix that &lt;strong&gt;actually meets the bar&lt;/strong&gt;, or a &lt;strong&gt;clear refusal&lt;/strong&gt; that names what it couldn't satisfy.&lt;/p&gt;

&lt;p&gt;So the constraints are off unless you ask; ask, and &lt;em&gt;highest-satisfying-that-also-passes&lt;/em&gt; replaces &lt;em&gt;highest-satisfying-and-warn&lt;/em&gt;. When nothing passes, the default is to &lt;strong&gt;skip that one fix and report it&lt;/strong&gt; — leave the vulnerability, tell you exactly why — with &lt;code&gt;--on-conflict=stop&lt;/code&gt; to turn any such skip into a hard error for a CI gate that must not proceed on a half-fix.&lt;/p&gt;

&lt;h2&gt;
  
  
  Engines: will the fix run on the Node you target?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn-audit-fix &lt;span class="nt"&gt;--engines&lt;/span&gt;.node&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'&amp;gt;=18'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reads as: &lt;em&gt;apply a fix only if its completed closure runs on Node ≥ 18.&lt;/em&gt; Every newly-pulled transitive is now selected as the highest in-range version &lt;strong&gt;whose own &lt;code&gt;engines.node&lt;/code&gt; admits your floor&lt;/strong&gt;. The &lt;code&gt;lru-cache@11&lt;/code&gt; from the opening is rejected for needing Node 20; if an in-range &lt;code&gt;lru-cache@10&lt;/code&gt; line runs on 18, that's what lands. If nothing in range clears the bar, the fix is skipped and attributed (below).&lt;/p&gt;

&lt;p&gt;Three ways to name the target, because "what Node do you support" has three real answers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A range&lt;/strong&gt; — &lt;code&gt;--engines.node='&amp;gt;=18'&lt;/code&gt;. Explicit, unambiguous, what CI should use.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bare&lt;/strong&gt; — &lt;code&gt;--engines.node&lt;/code&gt;, meaning &lt;em&gt;the Node running the CLI&lt;/em&gt;. Cheapest, and the report flags that it may differ from your project's target (the classic footgun: you're on 22, prod is on 18).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;floor&lt;/code&gt;&lt;/strong&gt; — &lt;code&gt;--engines.node=floor&lt;/code&gt;, meaning &lt;em&gt;the highest engine floor your project already requires&lt;/em&gt;. We read the root &lt;code&gt;package.json&lt;/code&gt;, every workspace manifest, and every hoisted package in &lt;code&gt;node_modules&lt;/code&gt;, take the maximum &lt;code&gt;engines&lt;/code&gt; lower bound, and target that. The invariant it encodes: &lt;strong&gt;a fix must not raise the floor your tree already stands on.&lt;/strong&gt; If the tree already needs 18, a fix is fine as long as it doesn't quietly push you to 20.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The check is lenient in the npm sense — a package that declares &lt;em&gt;no&lt;/em&gt; &lt;code&gt;engines&lt;/code&gt; makes no claim and is accepted (rejecting the silent majority would be useless). And it's not just for &lt;code&gt;node&lt;/code&gt;: &lt;code&gt;--engines.npm='&amp;gt;=9'&lt;/code&gt; gates the same way on any engine your closure declares.&lt;/p&gt;

&lt;p&gt;One subtlety that matters for correctness: the gate covers the &lt;strong&gt;fix version itself&lt;/strong&gt;, not only its closure. Completion re-resolves the transitives, but the package you're bumping &lt;em&gt;to&lt;/em&gt; is placed directly — so a fix that lands on a version whose &lt;em&gt;own&lt;/em&gt; &lt;code&gt;engines&lt;/code&gt; exceed your target would slip past a closure-only check. A pre-apply pass runs the same constraints against the fix version before it's committed; if the fix itself doesn't fit, it's skipped with &lt;em&gt;"the fix version itself is not permitted."&lt;/em&gt; Closure and seed, both gated.&lt;/p&gt;

&lt;h2&gt;
  
  
  Licenses: does the fix pass your policy?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn-audit-fix &lt;span class="nt"&gt;--license&lt;/span&gt;.allow&lt;span class="o"&gt;=&lt;/span&gt;MIT,ISC,Apache-2.0,BSD-3-Clause
&lt;span class="c"&gt;# or the other direction:&lt;/span&gt;
yarn-audit-fix &lt;span class="nt"&gt;--license&lt;/span&gt;.deny&lt;span class="o"&gt;=&lt;/span&gt;GPL-3.0
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same machine, different axis: apply a fix only if every new package it pulls in carries an allowed SPDX license (or none of a denied one). A security bump that drags a copyleft transitive into a proprietary codebase is exactly the kind of thing you'd rather catch at remediation time than at legal review.&lt;/p&gt;

&lt;p&gt;There's a data wrinkle worth surfacing, because it drove a real change in the pipeline. The registry's &lt;strong&gt;abbreviated&lt;/strong&gt; package document — the compact "corgi" metadata the completion walk fetches for speed — carries &lt;code&gt;engines&lt;/code&gt; but &lt;strong&gt;not &lt;code&gt;license&lt;/code&gt;&lt;/strong&gt;. So the engine gate is free (it reads metadata we already have), while the license gate has to fetch the &lt;strong&gt;full&lt;/strong&gt; manifest for a candidate to see its license field. The tool exposes that full-manifest fetch through the same scope-aware, host-bound registry client the rest of the pipeline uses, and the license gate reaches for it only when a candidate actually needs licensing — cheap gates first, so a version an in-memory engine check already rejected never triggers a license round-trip.&lt;/p&gt;

&lt;p&gt;v1 compares single SPDX ids. An SPDX &lt;strong&gt;expression&lt;/strong&gt; — &lt;code&gt;(MIT OR Apache-2.0)&lt;/code&gt; — isn't something a token match can decide: under &lt;code&gt;OR&lt;/code&gt;, one allowed license should clear the whole thing, but a naive scan trips on the disallowed half. So rather than guess, an unresolvable expression is treated as &lt;em&gt;unevaluable&lt;/em&gt; and, under the "guarantee-or-flag" contract, &lt;strong&gt;flagged rather than silently accepted&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Package format: does the fix stay require-able?
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn-audit-fix &lt;span class="nt"&gt;--package-type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A third axis, built on a limit worth stating plainly: &lt;em&gt;we can't verify a change fully, but we can check its consistency.&lt;/em&gt; A good chunk of the ecosystem has gone &lt;strong&gt;ESM-only&lt;/strong&gt; — a package flips to &lt;code&gt;"type": "module"&lt;/code&gt;, drops its CommonJS entry, and now &lt;code&gt;require('that-package')&lt;/code&gt; throws &lt;code&gt;ERR_REQUIRE_ESM&lt;/code&gt;. A security bump that lands on such a version, or drags one in transitively, breaks every CommonJS &lt;code&gt;require&lt;/code&gt; of it at runtime — cleanly, past every install check.&lt;/p&gt;

&lt;p&gt;We can't run your code to know whether that break is real for you. But we &lt;em&gt;can&lt;/em&gt; check the thing that makes it possible: does the fix's closure stay require-able? &lt;code&gt;--package-type=cjs&lt;/code&gt; rejects a new package that's ESM-only — &lt;code&gt;"type": "module"&lt;/code&gt; with no CJS &lt;code&gt;main&lt;/code&gt; and no &lt;code&gt;require&lt;/code&gt;/&lt;code&gt;default&lt;/code&gt; export condition. A package that makes no &lt;code&gt;type&lt;/code&gt; claim is CommonJS by default and passes; a dual-mode one (an ESM build &lt;em&gt;plus&lt;/em&gt; a &lt;code&gt;require&lt;/code&gt; export) passes; only the genuinely ESM-only versions are held back.&lt;/p&gt;

&lt;p&gt;Unlike engines and license, this isn't a library built-in — and that's deliberate. True &lt;code&gt;require(ESM)&lt;/code&gt; compatibility is &lt;em&gt;per-edge&lt;/em&gt; and Node-version-gated (newer Node keeps relaxing it), so a node-local yes/no is an approximation, not a proof. &lt;code&gt;lockgraph&lt;/code&gt; ships engines and license as built-in conditions and takes custom ones through the same seam; yaf plugs its module-format check in there — checking &lt;strong&gt;entry-point consistency&lt;/strong&gt;, which it can, and staying upfront that it isn't checking &lt;strong&gt;behaviour&lt;/strong&gt;, which it can't.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scope: which advisories, not just which fix
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;--force&lt;/code&gt; and &lt;code&gt;--safe&lt;/code&gt; tune &lt;em&gt;how&lt;/em&gt; a fix lands. A separate control tunes &lt;em&gt;how much&lt;/em&gt; gets fixed at all — because not every advisory is worth acting on in every context, and the smallest change that clears the risk is usually the right one.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;--production&lt;/code&gt;&lt;/strong&gt; fixes only advisories reachable from your production dependencies. A vulnerability that lives entirely under &lt;code&gt;devDependencies&lt;/code&gt; — in a test runner, a bundler, some linter's transitive — never ships, so patching it churns the lockfile for a risk your users never run. &lt;code&gt;--production&lt;/code&gt; leaves those flagged and fixes the rest. The split can't be read from the lockfile — &lt;code&gt;yarn.lock&lt;/code&gt; records &lt;a href="https://github.com/lockgraph/lockgraph/tree/master/docs/spec/formats" rel="noopener noreferrer"&gt;no dev/prod distinction&lt;/a&gt;; every edge is just &lt;em&gt;depends on&lt;/em&gt;. So it's computed from each &lt;code&gt;package.json&lt;/code&gt;, walked over the graph: start at the production dependencies, follow the edges, and a package is in scope only if production actually reaches it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;--workspace=&amp;lt;glob&amp;gt;&lt;/code&gt;&lt;/strong&gt; narrows a monorepo fix to the packages you name — &lt;code&gt;--workspace=packages/api&lt;/code&gt;, &lt;code&gt;--workspace='@scope/*'&lt;/code&gt;. Remediating one service's advisories in a fifty-package repo without touching the other forty-nine keeps the diff reviewable and the blast radius small. A workspace pulled in as a dependency of one you selected contributes only its &lt;em&gt;production&lt;/em&gt; closure, so a neighbour's dev tree never leaks into your fix.&lt;/p&gt;

&lt;p&gt;Two more narrow by &lt;em&gt;identity&lt;/em&gt; rather than reach. &lt;strong&gt;&lt;code&gt;--ignore&lt;/code&gt;&lt;/strong&gt; drops a specific advisory before the fix starts — a GHSA or npm-advisory-id glob — so a CVE you've assessed and accepted (a dev-only false positive, a disputed report) never triggers a bump. &lt;strong&gt;&lt;code&gt;--exclude&lt;/code&gt;&lt;/strong&gt; holds a package still by name and optional version range: &lt;code&gt;--exclude='lodash@&amp;gt;=2 &amp;lt;3'&lt;/code&gt; leaves that line alone wherever it sits in the tree, however deep. Neither has an equivalent in &lt;code&gt;npm audit fix&lt;/code&gt; — to fix everything &lt;em&gt;except&lt;/em&gt; one CVE, or to freeze one nested package, you'd reach for &lt;code&gt;overrides&lt;/code&gt; or a third-party audit wrapper.&lt;/p&gt;

&lt;p&gt;They all compose into one precise instruction — &lt;code&gt;--production --workspace=packages/api --safe&lt;/code&gt;, with &lt;code&gt;--ignore&lt;/code&gt; / &lt;code&gt;--exclude&lt;/code&gt; trimming the exceptions you've already vetted — so the fix set is exactly the one you meant, not everything the audit happened to return.&lt;/p&gt;

&lt;h2&gt;
  
  
  Attribution: a skip you can act on
&lt;/h2&gt;

&lt;p&gt;A gate that silently drops fixes is worse than no gate — you'd never know a vulnerability was left behind, or why. So every skip carries its reason, composed from the constraint you set, the axis that rejected, the package that blocked, and the versions it tried:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Constraints: node &amp;gt;=18
Skipped (constraints — no fix keeps the closure within the policy [node &amp;gt;=18];
         relax it, --exclude the package, or accept the newer dep):
  some-tool@1.4.0 → 2.0.0 — needs lru-cache@^11.0.0
    - lru-cache@11.5.2: engines.node '20 || &amp;gt;=22' does not support node 18.0.0 (target '&amp;gt;=18')
    - lru-cache@11.0.0: engines.node '20 || &amp;gt;=22' does not support node 18.0.0 (target '&amp;gt;=18')
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;From that you know precisely what to do: widen the target if 20 is actually fine, &lt;code&gt;--exclude some-tool&lt;/code&gt; to accept the residual risk deliberately, or pin &lt;code&gt;lru-cache&lt;/code&gt; yourself. The vulnerability is still there — but it's &lt;em&gt;visible and explained&lt;/em&gt;, which is the whole difference between "skipped" and "swept under the rug." The license axis reads the same, with &lt;code&gt;license MIT not permitted&lt;/code&gt; in the reason slot; a rejected fix version reads &lt;em&gt;"the fix version itself is not permitted."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;For a pipeline, the same account comes as data: &lt;code&gt;--dry-run --json&lt;/code&gt; prints what would be upgraded and what would be skipped — each skip with its reason (&lt;code&gt;consumer-range&lt;/code&gt;, &lt;code&gt;override-pin&lt;/code&gt;, &lt;code&gt;manifest-pin&lt;/code&gt;, &lt;code&gt;constraint&lt;/code&gt;, &lt;code&gt;out-of-scope&lt;/code&gt;) — as one JSON object, before anything is written. A CI step can gate on what a fix would leave behind, not just on what it would change.&lt;/p&gt;

&lt;h2&gt;
  
  
  Under the hood
&lt;/h2&gt;

&lt;p&gt;Three properties hold it in line: off by default, no effect on the reproducibility guarantee, and no claim beyond what it checks.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Off means off.&lt;/strong&gt; With no constraint set, the apply path is exactly Part 2's: rebind everything, complete the closure once, byte-identical output — the golden and real-world fixtures prove it. The constraint machinery is a branch you only enter by opting in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tentative apply, immutable graphs.&lt;/strong&gt; When constraints &lt;em&gt;are&lt;/em&gt; active, each fix is applied and its closure completed &lt;strong&gt;on a trial graph&lt;/strong&gt;, and committed only if it clears every gate; a fix that hits a dead end is discarded and the graph is left exactly as it was, then the next fix is tried. That's clean precisely because the graph library is immutable — &lt;code&gt;modify&lt;/code&gt; and &lt;code&gt;complete&lt;/code&gt; return new graphs rather than mutating in place, so "try this fix, and if it doesn't pass, pretend it never happened" is just &lt;em&gt;don't keep the result&lt;/em&gt;. No rollback, no partial state. The constraints themselves ride the library's own resolution seam — the same step that already honored &lt;code&gt;resolutions&lt;/code&gt; (Part 2) also runs your acceptance gates — so "don't pick an engine-incompatible version" is a property of resolution, next to "don't violate a known pin," not a filter bolted on afterward.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It's a bet, not a proof.&lt;/strong&gt; The safe end rests on one wager: that checking each fix against its &lt;em&gt;own&lt;/em&gt; declared metadata — the version it lands on, and the ones it drags in — is enough, without a solver backtracking the whole tree. That check is node-local, which buys a single guarantee: it can only be &lt;em&gt;too cautious&lt;/em&gt;, never too permissive. It won't hand you an engine-incompatible or ESM-only pin. At worst it over-reports, refusing a fix a backtracking resolver might have landed by picking a lower parent upstream. That case is narrow, and the skip report names it, so you close it by hand. So far it holds; the corners get named and narrowed one at a time.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this leaves the tool
&lt;/h2&gt;

&lt;p&gt;Part 2's claim was &lt;em&gt;the fix reproduces&lt;/em&gt;. Part 3's is a step further: &lt;em&gt;the fix reproduces **and fits&lt;/em&gt;** — runs on the Node you target, passes the license policy you set, agrees with the ranges you declared, across every workspace in a monorepo. All opt-in, all guarantee-or-flag, all attributed when they can't.&lt;/p&gt;

&lt;p&gt;The lowest patched version clears the advisory. Whether it's the &lt;em&gt;right&lt;/em&gt; version, only your project can answer — so the tool leaves that call to you, and flags the ones where the answer is "not this one."&lt;/p&gt;

&lt;p&gt;There's a single stance under all of it. &lt;code&gt;--force&lt;/code&gt; says &lt;em&gt;apply everything, break what you must&lt;/em&gt;. The constraints are its mirror image — there's even a &lt;code&gt;--safe&lt;/code&gt; flag that flips the automatable ones on together: &lt;em&gt;apply what fits, leave what doesn't for a deliberate hand.&lt;/em&gt; Update what you safely can now, and schedule the engine bump, the license swap, or the ESM migration as its own change rather than smuggling it inside a security patch. The tool's job is to make the split legible: what it fixed, what it couldn't, and why.&lt;/p&gt;

&lt;p&gt;If Yarn ships a native &lt;code&gt;audit fix&lt;/code&gt; one day, we'll retire ours happily — constraints and all. Until then, it fixes your lockfile, and names the fixes it can't land without breaking something else.&lt;/p&gt;




&lt;h3&gt;
  
  
  Sources / links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The tool: &lt;a href="https://github.com/lockgraph/yarn-audit-fix" rel="noopener noreferrer"&gt;&lt;code&gt;yarn-audit-fix&lt;/code&gt;&lt;/a&gt; (&lt;a href="https://www.npmjs.com/package/yarn-audit-fix" rel="noopener noreferrer"&gt;npm&lt;/a&gt;) — &lt;code&gt;--engines.&amp;lt;engine&amp;gt;&lt;/code&gt;, &lt;code&gt;--license.allow/.deny&lt;/code&gt;, &lt;code&gt;--package-type&lt;/code&gt;, &lt;code&gt;--safe&lt;/code&gt;, &lt;code&gt;--on-conflict&lt;/code&gt;, &lt;code&gt;--production&lt;/code&gt;, &lt;code&gt;--workspace&lt;/code&gt;, &lt;code&gt;--json&lt;/code&gt;, and the &lt;code&gt;--force&lt;/code&gt; manifest rewrite.&lt;/li&gt;
&lt;li&gt;The library: &lt;a href="https://github.com/lockgraph/lockgraph" rel="noopener noreferrer"&gt;&lt;code&gt;lockgraph&lt;/code&gt;&lt;/a&gt; — the &lt;code&gt;complete&lt;/code&gt; step's constraint seam (&lt;code&gt;engines&lt;/code&gt; / &lt;code&gt;license&lt;/code&gt; conditions, plus custom ones) and the immutable graph operations the tentative apply relies on; the per-format specs (including the missing dev/prod marker) live under &lt;a href="https://github.com/lockgraph/lockgraph/tree/master/docs/spec/formats" rel="noopener noreferrer"&gt;docs/spec/formats&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Background: &lt;a href="https://instatunnel.my/blog/why-npm-audit-fix---force-is-a-terrible-idea" rel="noopener noreferrer"&gt;Why &lt;code&gt;npm audit fix --force&lt;/code&gt; is risky&lt;/a&gt;, &lt;a href="https://spdx.org/licenses/" rel="noopener noreferrer"&gt;SPDX license list&lt;/a&gt;, &lt;a href="https://docs.npmjs.com/cli/v10/using-npm/config#engine-strict" rel="noopener noreferrer"&gt;&lt;code&gt;EBADENGINE&lt;/code&gt; in npm&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>security</category>
      <category>npm</category>
      <category>supplychain</category>
    </item>
    <item>
      <title>yarn.lock: you can't `sed` a graph</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Tue, 29 Sep 2026 06:55:05 +0000</pubDate>
      <link>https://dev.to/antongolub/-yarnlock-you-cant-sed-a-graph-5ffp</link>
      <guid>https://dev.to/antongolub/-yarnlock-you-cant-sed-a-graph-5ffp</guid>
      <description>&lt;p&gt;&lt;em&gt;After the first workarounds, a lockfile-graph library appeared — and it changed what a fix even is: not a text edit, but graph surgery. How &lt;code&gt;lockgraph&lt;/code&gt; rebuilt &lt;code&gt;yarn-audit-fix&lt;/code&gt; from the inside, down to the unglamorous work of emitting a file that still installs.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/antongolub/yarn-audit-still-does-not-fix-3n3"&gt;&lt;code&gt;yarn audit&lt;/code&gt; [still does not] &lt;code&gt;fix&lt;/code&gt;&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;yarn.lock&lt;/code&gt;: you can't &lt;code&gt;sed&lt;/code&gt; a graph&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/antongolub/yarnlock-may-the-force-be-with-you-50pm"&gt;&lt;code&gt;yarn.lock&lt;/code&gt;: may the &lt;code&gt;--force&lt;/code&gt; be with you&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A lockfile fix is not string replacement. Bump a package and its &lt;strong&gt;transitive closure&lt;/strong&gt; can change, its &lt;strong&gt;checksums&lt;/strong&gt; change, and the file still has to &lt;strong&gt;reproduce&lt;/strong&gt; under &lt;code&gt;--immutable&lt;/code&gt;. Miss any of those and you've shipped a lockfile that doesn't install.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;lockgraph&lt;/code&gt; parses every Yarn lockfile — Classic (v1) through Berry (v4–v10, Yarn 2 onward) — into &lt;strong&gt;one dependency graph&lt;/strong&gt;; the fix is a transformation on that graph, serialized back to the &lt;em&gt;original&lt;/em&gt; format. &lt;strong&gt;Registry-only, spawn-free&lt;/strong&gt; — no &lt;code&gt;npm&lt;/code&gt;/&lt;code&gt;yarn&lt;/code&gt; child process.&lt;/li&gt;
&lt;li&gt;The hard part is the &lt;strong&gt;closure&lt;/strong&gt;: re-resolve the upgraded node's dependencies the way &lt;code&gt;yarn install&lt;/code&gt; would — honoring a project's declared &lt;strong&gt;&lt;code&gt;resolutions&lt;/code&gt;&lt;/strong&gt; pins rather than silently resolving around them — and recompute checksums, so the result passes &lt;code&gt;yarn install --immutable&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Field proof: a real monorepo build broke because a &lt;code&gt;simple-git&lt;/code&gt; bump needed two sub-packages it never received. The graph model adds them.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Recap: the wall
&lt;/h2&gt;

&lt;p&gt;Part 1 ended on the difficulty that actually matters:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Bump one entry and its &lt;em&gt;transitive&lt;/em&gt; dependencies should change too — but Yarn doesn't reload a patched entry's dependencies for you. A correct fix has to re-resolve subtrees against the registry and keep the lockfile internally consistent and deterministic. With PnP and Yarn's strict linker, a naive in-place edit yields an install that doesn't reproduce.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A lockfile isn't a list of versions; it's a &lt;strong&gt;resolved graph&lt;/strong&gt; — every package, every edge, pinned and checksummed so an install is byte-reproducible. "Fix the vulnerability" means &lt;em&gt;edit the graph and keep it resolved&lt;/em&gt;. A &lt;code&gt;sed&lt;/code&gt; one-liner gives you a file that lints fine and installs wrong: you can't &lt;code&gt;sed&lt;/code&gt; the graph underneath it.&lt;/p&gt;

&lt;p&gt;So we stopped editing text and started editing the graph.&lt;/p&gt;

&lt;h2&gt;
  
  
  One graph for every Yarn lockfile
&lt;/h2&gt;

&lt;p&gt;Underneath the tool is a separate, single-purpose library — &lt;a href="https://github.com/lockgraph/lockgraph" rel="noopener noreferrer"&gt;&lt;code&gt;lockgraph&lt;/code&gt;&lt;/a&gt; — whose only job is to turn lockfiles into a graph and back:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;detect(text)            → which format is this?
parse(text, format)     → a dependency Graph (nodes = packages, edges = ranges)
…transform the graph…
stringify(graph, format) → text in the SAME format, quirks preserved
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The formats it round-trips for us: Yarn Classic &lt;code&gt;yarn.lock&lt;/code&gt; (v1) and Yarn Berry (the v4 through v10 schemas, Yarn 2 onward). One model, many serializers. That's the leverage: the fix is written &lt;strong&gt;once&lt;/strong&gt;, against the graph, and every Yarn schema gets it. The serializer's job is to be boring — emit the exact dialect Yarn expects, down to key ordering and quoting, so a clean tool run produces a diff that's &lt;em&gt;only&lt;/em&gt; the fix.&lt;/p&gt;

&lt;p&gt;The same library also round-trips npm, pnpm, bun, and Deno locks through one model, but that breadth is &lt;a href="https://github.com/lockgraph/lockgraph/blob/master/docs/arch/CONVERT.md" rel="noopener noreferrer"&gt;its own story&lt;/a&gt;. Here, every lock is a &lt;code&gt;yarn.lock&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A node is a resolved package (&lt;code&gt;lodash@4.17.20&lt;/code&gt;, with its integrity, its dependency ranges). An edge is "this descriptor (&lt;code&gt;lodash@^4&lt;/code&gt;) resolves to that node." Once the file is a graph, a security fix is just graph surgery: move a node, rewire its edges, complete what's now missing, drop what's now orphaned.&lt;/p&gt;

&lt;h2&gt;
  
  
  The pipeline
&lt;/h2&gt;

&lt;p&gt;Five steps, all over HTTP to a registry, no subprocess.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Advisories, straight from the registry
&lt;/h3&gt;

&lt;p&gt;The library hands us the package set that actually needs auditing — &lt;code&gt;graph.registryPackages()&lt;/code&gt; — which is &lt;em&gt;locator-aware&lt;/em&gt;: it skips workspace members, &lt;code&gt;link:&lt;/code&gt; / &lt;code&gt;portal:&lt;/code&gt; / &lt;code&gt;patch:&lt;/code&gt; / &lt;code&gt;file:&lt;/code&gt; / &lt;code&gt;git:&lt;/code&gt; entries, and anything whose version isn't real semver. Those can't have a registry advisory and shouldn't be POSTed.&lt;/p&gt;

&lt;p&gt;That set goes to the registry's bulk audit endpoint (&lt;code&gt;/-/npm/v1/security/advisories/bulk&lt;/code&gt;), chunked, over plain HTTPS — the same endpoint &lt;code&gt;yarn npm audit&lt;/code&gt; proxies, talked to directly. Auth is resolved from your project config (&lt;code&gt;.npmrc&lt;/code&gt;, &lt;code&gt;.yarnrc.yml&lt;/code&gt;, env), &lt;strong&gt;host-bound and https-only&lt;/strong&gt;: a token declared for one registry is never attached to a request to another host, and never sent over http. Scoped packages route to their own registry with their own credential.&lt;/p&gt;

&lt;p&gt;What comes back is normalized into a report: severity and CVSS, the GHSA/CVE refs, and — because advisory ranges are the awkward data from Part 1 — a &lt;em&gt;patched&lt;/em&gt; range derived from the vulnerable one. &lt;code&gt;--audit-level&lt;/code&gt; and &lt;code&gt;--ignore&lt;/code&gt; (npm id or GHSA) filter here. When a package carries several advisories they merge: the vulnerable ranges OR together, the patched ranges AND together.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Choosing the fix version
&lt;/h3&gt;

&lt;p&gt;For each vulnerable node we ask the registry for the &lt;strong&gt;lowest published version that clears the merged advisory set&lt;/strong&gt;. Lowest, not latest — the smallest move that makes you safe is the least likely to break you.&lt;/p&gt;

&lt;p&gt;"Lowest that clears &lt;em&gt;the merged set&lt;/em&gt;" has a subtlety worth showing. Take &lt;code&gt;lodash&lt;/code&gt; at &lt;code&gt;4.17.20&lt;/code&gt; with five advisories. If four are fixed by &lt;code&gt;4.17.21&lt;/code&gt; but one is only fixed in &lt;code&gt;4.18.0&lt;/code&gt;, the AND of their patched ranges is &lt;code&gt;&amp;gt;=4.18.0&lt;/code&gt;, and the lowest version satisfying &lt;em&gt;that&lt;/em&gt; is &lt;code&gt;4.18.0&lt;/code&gt;. So the tool lands on &lt;code&gt;4.18.0&lt;/code&gt; — not because it prefers newer, but because that's the floor where every advisory is cleared at once.&lt;/p&gt;

&lt;p&gt;Then the &lt;strong&gt;compatibility gate&lt;/strong&gt;, which is the default safety rail: a fix is applied only if the new version still satisfies &lt;em&gt;every surviving consumer's declared range&lt;/em&gt;. If clearing the advisory requires a cross-major jump that breaks a parent's &lt;code&gt;^x&lt;/code&gt; range, the default declines and reports it rather than quietly breaking your build — and &lt;code&gt;--force&lt;/code&gt; is there for when you've decided to take the major bump.&lt;/p&gt;

&lt;p&gt;There's a second rail alongside consumer ranges: a project's own &lt;strong&gt;&lt;code&gt;resolutions&lt;/code&gt;&lt;/strong&gt;. Pin a transitive version deliberately, and a fix that ignored that pin would emit a lockfile contradicting its own &lt;code&gt;package.json&lt;/code&gt;. So &lt;code&gt;resolutions&lt;/code&gt; bound the remediation too, and yaf takes the same line &lt;code&gt;npm audit fix --force&lt;/code&gt; takes with a user's override. A pin holding a version the fix can't satisfy is authoritative: yaf &lt;strong&gt;leaves it and reports it&lt;/strong&gt;, never silently rewriting your pin. A pin whose range still admits the fix is honored — the fix resolves &lt;em&gt;within&lt;/em&gt; it. Either way the emitted lock agrees with what &lt;code&gt;yarn install&lt;/code&gt; would produce. This is where modeling pays off: the graph library treats &lt;code&gt;resolutions&lt;/code&gt; as first-class constraints rather than a special case bolted onto the patcher, so "don't violate a known pin" is a property of the resolution step, not an afterthought.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The transformation
&lt;/h3&gt;

&lt;p&gt;Now the graph surgery, in three moves.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rebind&lt;/strong&gt; (&lt;code&gt;modify&lt;/code&gt; with a &lt;code&gt;replaceVersion&lt;/code&gt; operation): add the patched node and redirect the edges that pointed at the old one onto it. The operation reports back what it disturbed — which nodes it just added, which it left orphaned — and that report seeds the next step.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Complete the closure&lt;/strong&gt; (&lt;code&gt;complete&lt;/code&gt;): this is the step the naive tools skip and the one Part 1 called the hard part. The new version's &lt;em&gt;own&lt;/em&gt; dependencies may differ from the old one's. So we walk the newly-added nodes, fetch each one's metadata, and for every dependency it declares we either reuse the resolution the lock already records for that descriptor or resolve a new one and wire it in. And those new nodes have new dependencies of their own — so it's re-resolution, then re-resolution of whatever &lt;em&gt;that&lt;/em&gt; dragged in, and again, until a pass turns up nothing new.&lt;/p&gt;

&lt;p&gt;Crucially, new descriptors resolve to the &lt;strong&gt;highest published version that satisfies the range&lt;/strong&gt; — because that's exactly what a fresh &lt;code&gt;yarn install&lt;/code&gt; would pick. Match that policy and the lockfile you emit equals the lockfile Yarn would compute; diverge from it and &lt;code&gt;--immutable&lt;/code&gt; rejects you. (More on that below.)&lt;/p&gt;

&lt;p&gt;Here's the field case, concretely. A monorepo build broke with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Error: Cannot find module '@simple-git/args-pathspec'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;simple-git&lt;/code&gt; past &lt;code&gt;3.34&lt;/code&gt; had split its argument handling into two new packages. A version-only bump moved &lt;code&gt;simple-git&lt;/code&gt; and left the lockfile believing it had no new dependencies — so the install never fetched them. Before:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;simple-git@npm:^3.10.0"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.24.0&lt;/span&gt;
  &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@kwsites/file-exists"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.1.1&lt;/span&gt;
    &lt;span class="s"&gt;# …no @simple-git/* — they didn't exist yet at &lt;/span&gt;&lt;span class="m"&gt;3.24&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After the graph fix:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;simple-git@npm:^3.10.0"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;3.36.0&lt;/span&gt;
  &lt;span class="na"&gt;dependencies&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@kwsites/file-exists"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.1.1&lt;/span&gt;
    &lt;span class="s"&gt;"@simple-git/args-pathspec"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.0.3&lt;/span&gt;   &lt;span class="c1"&gt;# ← re-resolved + wired&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@simple-git/argv-parser"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;^1.1.0&lt;/span&gt;     &lt;span class="c1"&gt;# ←&lt;/span&gt;

&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@simple-git/args-pathspec@npm:^1.0.3"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;   &lt;span class="c1"&gt;# ← added to the graph&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.0.4&lt;/span&gt;
&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@simple-git/argv-parser@npm:^1.1.0"&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;     &lt;span class="c1"&gt;# ←&lt;/span&gt;
  &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.1.1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Prune&lt;/strong&gt; (&lt;code&gt;pruneOrphans&lt;/code&gt;, an option of the same completion pass): the bump can also &lt;em&gt;strand&lt;/em&gt; a subtree — the old version's unique dependencies, now referenced by nothing. Those get garbage-collected. With one guard that took a round of debugging to get right: a real lockfile already contains "danglers" — entries with no incoming edge that Yarn nonetheless keeps. If the prune is too eager and GCs those, &lt;code&gt;yarn install --immutable&lt;/code&gt; notices the file changed and fails (&lt;code&gt;YN0028&lt;/code&gt;). So the prune starts only from what the fix itself stranded — nodes that had an incoming edge before the bump and have none after — and cascades from there. A dangler that was already there never enters the sweep.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Making it reproduce: checksums
&lt;/h3&gt;

&lt;p&gt;A Berry entry carries an integrity &lt;code&gt;checksum&lt;/code&gt;; a freshly-added node doesn't have one yet, and a lock with the line missing isn't finished — the next install writes it in and leaves your tree dirty. So the last step (&lt;code&gt;refurbish&lt;/code&gt;) fetches each new package's tarball and recomputes the hash.&lt;/p&gt;

&lt;p&gt;That's harder than it sounds. Berry's checksum isn't the tarball's hash: it's computed over the package's &lt;em&gt;cache archive&lt;/em&gt;, a zip whose bytes depend on how Yarn compressed it — the lock's &lt;code&gt;cacheKey&lt;/code&gt;. To reproduce it you rebuild that archive the way Yarn would: with a pure-JS deflate where the layout is known to be reproducible, and with Yarn's own zip layer (&lt;code&gt;@yarnpkg/libzip&lt;/code&gt;) where it isn't. Before trusting either, the tool proves the recipe on a package your lock &lt;em&gt;already&lt;/em&gt; has a checksum for — rebuild it, compare — and fills the new ones only on a match.&lt;/p&gt;

&lt;p&gt;When that proof fails — no fetchable tarball, or a compression profile it can't match byte-for-byte — &lt;a href="https://github.com/lockgraph/lockgraph/blob/master/docs/arch/CONVERT.md" rel="noopener noreferrer"&gt;it doesn't write a &lt;em&gt;wrong&lt;/em&gt; checksum&lt;/a&gt;: it omits those lines and says so, naming the reason:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Could not compute checksums for N package(s) (tarball-unavailable: N) —
run `yarn install` to finish the lockfile.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A plain &lt;code&gt;yarn install&lt;/code&gt; recovers every one of them in a single pass, changing nothing else. It's the one place the user's own &lt;code&gt;yarn&lt;/code&gt; can re-enter the loop — only for checksums the tool couldn't prove — and it's the same two-step the maintainers' own muscle memory already expects: apply the fix, then install.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. The invariant
&lt;/h3&gt;

&lt;p&gt;Put the pieces together and you get the property the whole design is for:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The patched lockfile resolves exactly as Yarn would and, with every checksum in, passes &lt;code&gt;yarn install --immutable&lt;/code&gt; with no rewrite.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That holds because of three choices that have to agree: completion resolves to the &lt;strong&gt;highest satisfying&lt;/strong&gt; version (= what &lt;code&gt;yarn install&lt;/code&gt; picks), the prune &lt;strong&gt;leaves&lt;/strong&gt; pre-existing danglers alone (= what Yarn keeps), and checksums are &lt;strong&gt;recomputed and proven&lt;/strong&gt;, never guessed (= what Yarn verifies). Break any one and immutable mode catches it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does it actually reproduce? A real-world test
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;simple-git&lt;/code&gt; break above came from a real qiwi/mware build (PR #275). So that's where we test the claim, on the real repo, at the commit &lt;em&gt;before&lt;/em&gt; the fix:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Check out the pre-fix commit in a throwaway worktree. &lt;code&gt;simple-git&lt;/code&gt; is &lt;code&gt;3.24.0&lt;/code&gt;; the &lt;code&gt;@simple-git/*&lt;/code&gt; packages are absent.&lt;/li&gt;
&lt;li&gt;Run &lt;code&gt;yarn-audit-fix&lt;/code&gt;. It bumps &lt;code&gt;simple-git&lt;/code&gt; to &lt;code&gt;3.36.0&lt;/code&gt; (plus fifteen other fixes), completes the closure — the two sub-packages appear and are wired in — and recomputes the checksum of every package it added.&lt;/li&gt;
&lt;li&gt;Verify with the user's own Yarn: straight to &lt;code&gt;yarn install --immutable&lt;/code&gt;, on a cold cache.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result that matters: &lt;code&gt;--immutable&lt;/code&gt; exits &lt;code&gt;0&lt;/code&gt; and leaves the file byte-for-byte as it was, no &lt;code&gt;YN0028&lt;/code&gt;. And every checksum the tool computed is &lt;strong&gt;the one Yarn writes itself&lt;/strong&gt; — let Yarn fill them in on the same resolutions and it lands on the identical file, byte for byte. The graph the tool produced is the graph a clean install would have produced, and so is the file.&lt;/p&gt;

&lt;h2&gt;
  
  
  A note on portability
&lt;/h2&gt;

&lt;p&gt;Registry-only means there's no second package manager to install and nothing platform-specific to shell out to — it's just HTTP and graph manipulation. The graph library assumes as little as it can: &lt;code&gt;fetch&lt;/code&gt; is polyfilled (via &lt;code&gt;node-fetch-native&lt;/code&gt;) rather than taken for granted, and it runs on Node &lt;strong&gt;14.18&lt;/strong&gt;. The one piece borrowed from Yarn — its zip layer, the price of byte-exact checksums — is what sets the tool's floor at Node &lt;strong&gt;18.12&lt;/strong&gt;. yarn-audit-fix declares no &lt;code&gt;engines&lt;/code&gt; of its own, so installing it never raises an &lt;code&gt;EBADENGINE&lt;/code&gt; on its own behalf.&lt;/p&gt;

&lt;h2&gt;
  
  
  The side effect: real lockfiles are the test suite
&lt;/h2&gt;

&lt;p&gt;Because the parser is its own library, separate from the fixer, it can run across a large corpus of real-world lockfiles — and real lockfiles are stranger than any fixture you'd think to write. Every odd one in the wild becomes a test you didn't author.&lt;/p&gt;

&lt;p&gt;That's how a quiet auth bug surfaced. A &lt;code&gt;.yarnrc.yml&lt;/code&gt; declaring a scoped registry with a token was dropping it silently: the registry URL was stored under one normalized form and looked up under another, so the two never matched, and requests to that scope went out unauthenticated with nothing to flag it. No fixture would have guessed the exact URL shape that triggered it; a real config had it. Fixed once in the shared parser, it's gone everywhere that parser runs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where this goes
&lt;/h2&gt;

&lt;p&gt;None of this is an argument that Yarn &lt;em&gt;should&lt;/em&gt; have shipped &lt;code&gt;audit fix&lt;/code&gt; years ago — Part 1 is sympathetic to why it didn't. It's a demonstration that the hard part is tractable outside the package manager: treat the lockfile as a graph, re-resolve instead of replace, and check yourself against &lt;code&gt;--immutable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There's a catch that reproducibility alone doesn't cover, though: a lockfile can install perfectly and still be the &lt;em&gt;wrong&lt;/em&gt; fix — one that needs a newer Node than you target, or a license you don't allow, or a version outside a range you pinned on purpose. Making the fix not just reproducible but &lt;em&gt;right for your project&lt;/em&gt; is its own problem.&lt;/p&gt;




&lt;h3&gt;
  
  
  Sources / links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;The library: &lt;a href="https://github.com/lockgraph/lockgraph" rel="noopener noreferrer"&gt;&lt;code&gt;lockgraph&lt;/code&gt;&lt;/a&gt; — &lt;code&gt;parse&lt;/code&gt; / &lt;code&gt;stringify&lt;/code&gt;, and the &lt;code&gt;modify&lt;/code&gt; / &lt;code&gt;complete&lt;/code&gt; / &lt;code&gt;refurbish&lt;/code&gt; operations the pipeline drives; the cross-format conversion + loss reference is &lt;a href="https://github.com/lockgraph/lockgraph/blob/master/docs/arch/CONVERT.md" rel="noopener noreferrer"&gt;CONVERT.md&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;The tool: &lt;a href="https://github.com/lockgraph/yarn-audit-fix" rel="noopener noreferrer"&gt;&lt;code&gt;yarn-audit-fix&lt;/code&gt;&lt;/a&gt; (&lt;a href="https://www.npmjs.com/package/yarn-audit-fix" rel="noopener noreferrer"&gt;npm&lt;/a&gt;).&lt;/li&gt;
&lt;li&gt;Earlier notes: &lt;a href="https://dev.to/antongolub/yarn-audit-fix-workaround-i2a"&gt;&lt;em&gt;Yarn audit fix: workaround&lt;/em&gt;&lt;/a&gt;, &lt;a href="https://dev.to/antongolub/the-missing-yarn-audit-fix-for-yarn-2-berry-1p8"&gt;&lt;em&gt;The missing &lt;code&gt;yarn audit fix&lt;/code&gt; for Yarn 2+ Berry&lt;/em&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>npm</category>
      <category>supplychain</category>
      <category>security</category>
    </item>
    <item>
      <title>yarn audit [still does not] fix</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Tue, 29 Sep 2026 06:54:07 +0000</pubDate>
      <link>https://dev.to/antongolub/yarn-audit-still-does-not-fix-3n3</link>
      <guid>https://dev.to/antongolub/yarn-audit-still-does-not-fix-3n3</guid>
      <description>&lt;p&gt;&lt;em&gt;Why a native &lt;code&gt;yarn audit fix&lt;/code&gt; is a hard problem, the tools and services that help in the meantime, and how we try to lend the Yarn ecosystem a hand — with remediation rebuilt on a lockfile graph.&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;yarn audit&lt;/code&gt; [still does not] &lt;code&gt;fix&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/antongolub/-yarnlock-you-cant-sed-a-graph-5ffp"&gt;&lt;code&gt;yarn.lock&lt;/code&gt;: you can't &lt;code&gt;sed&lt;/code&gt; a graph&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/antongolub/yarnlock-may-the-force-be-with-you-50pm"&gt;&lt;code&gt;yarn.lock&lt;/code&gt;: may the &lt;code&gt;--force&lt;/code&gt; be with you&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;yarn audit&lt;/code&gt; (and &lt;code&gt;yarn npm audit&lt;/code&gt; in Berry) &lt;strong&gt;reports&lt;/strong&gt; vulnerabilities; turning that into an automatic fix has never been part of Yarn. npm has &lt;code&gt;npm audit fix&lt;/code&gt;, pnpm has &lt;code&gt;pnpm audit --fix&lt;/code&gt; — Yarn, Classic through 4, doesn't.&lt;/li&gt;
&lt;li&gt;Not for lack of asking (the request has been open since 2019) — it's a hard problem on a finite maintenance budget, and "fixing" a lockfile is &lt;strong&gt;re-resolution&lt;/strong&gt;, not search-and-replace.&lt;/li&gt;
&lt;li&gt;SaaS bots (Dependabot, Renovate, Snyk, Socket) solve a different shape — asynchronous, hosted, PR-gated — and leave a gap for &lt;strong&gt;local, one-command&lt;/strong&gt; remediation.&lt;/li&gt;
&lt;li&gt;We've tried to fill that gap, in the open: &lt;a href="https://github.com/lockgraph/yarn-audit-fix" rel="noopener noreferrer"&gt;&lt;code&gt;yarn-audit-fix&lt;/code&gt;&lt;/a&gt; does remediation as a deterministic &lt;strong&gt;lockfile-graph&lt;/strong&gt; transformation — registry-only, no &lt;code&gt;npm&lt;/code&gt;/&lt;code&gt;yarn&lt;/code&gt; subprocess — emitting a lockfile that still passes &lt;code&gt;yarn install --immutable&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  &lt;code&gt;audit&lt;/code&gt; ≠ &lt;code&gt;audit fix&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Years ago I ran into something every Yarn user eventually does: &lt;code&gt;yarn audit&lt;/code&gt; happily prints a wall of CVEs, and then you're on your own for the fix. I wrote up the first workaround back then — &lt;a href="https://dev.to/antongolub/yarn-audit-fix-workaround-i2a"&gt;&lt;em&gt;Yarn audit fix: workaround&lt;/em&gt;&lt;/a&gt; — which did something slightly horrifying but effective: convert &lt;code&gt;yarn.lock&lt;/code&gt; → &lt;code&gt;package-lock.json&lt;/code&gt; (via Synp), run &lt;code&gt;npm audit fix --package-lock-only&lt;/code&gt;, convert back. A year later, when Yarn 2 (Berry) changed everything, I followed up with &lt;a href="https://dev.to/antongolub/the-missing-yarn-audit-fix-for-yarn-2-berry-1p8"&gt;&lt;em&gt;The missing &lt;code&gt;yarn audit fix&lt;/code&gt; for Yarn 2+ Berry&lt;/em&gt;&lt;/a&gt;, which dropped the conversion dance and patched the lockfile directly from &lt;code&gt;yarn npm audit --json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Those posts are old. The problem they describe is not.&lt;/p&gt;

&lt;p&gt;The naming confuses people, so let's be precise:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;npm audit fix&lt;/code&gt;&lt;/strong&gt; — bumps vulnerable packages to patched versions &lt;em&gt;within your declared ranges&lt;/em&gt;, in place, locally, in one command. With &lt;code&gt;--force&lt;/code&gt; it will also do semver-major jumps (more on why that's risky below).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;pnpm audit --fix&lt;/code&gt;&lt;/strong&gt; — pnpm took the &lt;em&gt;overrides&lt;/em&gt; route: it writes pinned non-vulnerable versions into overrides (and, since v11, &lt;code&gt;--fix=update&lt;/code&gt; can update the lockfile instead). See the &lt;a href="https://pnpm.io/cli/audit" rel="noopener noreferrer"&gt;pnpm docs&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;yarn audit fix&lt;/code&gt;&lt;/strong&gt; — isn't a command. There's no built-in verb that takes the audit report and applies it: Classic's &lt;code&gt;yarn audit&lt;/code&gt; and Berry's &lt;code&gt;yarn npm audit&lt;/code&gt; filter and format the report (&lt;code&gt;--level&lt;/code&gt;, &lt;code&gt;--severity&lt;/code&gt;, &lt;code&gt;--exclude&lt;/code&gt;, &lt;code&gt;--ignore&lt;/code&gt;, &lt;code&gt;--json&lt;/code&gt;) but never touch the lockfile.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For a single app you can shrug and run npm in a temp dir. For a Yarn-only &lt;strong&gt;monorepo&lt;/strong&gt; — workspaces, constraints, PnP, a &lt;code&gt;yarn.lock&lt;/code&gt; that's the single source of truth — that escape hatch closes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Years of asking — for something hard
&lt;/h2&gt;

&lt;p&gt;This isn't an obscure request; the paper trail is long:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;2019&lt;/strong&gt; — &lt;a href="https://github.com/yarnpkg/yarn/issues/7075" rel="noopener noreferrer"&gt;&lt;code&gt;yarn#7075&lt;/code&gt; &lt;em&gt;[feat] yarn audit fix&lt;/em&gt;&lt;/a&gt;, on Classic.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2021&lt;/strong&gt; — &lt;a href="https://github.com/yarnpkg/berry/issues/3582" rel="noopener noreferrer"&gt;&lt;code&gt;berry#3582&lt;/code&gt; &lt;em&gt;[Feature] &lt;code&gt;yarn npm audit --fix&lt;/code&gt;&lt;/em&gt;&lt;/a&gt;, whose author volunteered to implement it.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;2025&lt;/strong&gt; — &lt;a href="https://github.com/yarnpkg/berry/issues/6959" rel="noopener noreferrer"&gt;&lt;code&gt;berry#6959&lt;/code&gt;&lt;/a&gt;, reframing the gap as a leaky abstraction; closed as a duplicate of the open request.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A community member even built a plugin — &lt;a href="https://github.com/sargunv/yarn-plugin-npm-audit-fix" rel="noopener noreferrer"&gt;&lt;code&gt;sargunv/yarn-plugin-npm-audit-fix&lt;/code&gt;&lt;/a&gt; — and &lt;a href="https://github.com/yarnpkg/berry/issues/3582" rel="noopener noreferrer"&gt;offered to upstream it&lt;/a&gt;. It hasn't landed in core, and the plugin's repository has since been archived. Keeping it out of core is fair: a remediation engine &lt;em&gt;inside&lt;/em&gt; a package manager competes with the installer, PnP, constraints, and the plugin API for a small team's time — a higher bar than a standalone tool.&lt;/p&gt;

&lt;p&gt;The request has outlived &lt;a href="https://endoflife.date/yarn" rel="noopener noreferrer"&gt;Classic, 2, 3 and 4&lt;/a&gt; and every lockfile schema between them — exactly the gap a community stopgap fills.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where the difficulty comes from
&lt;/h2&gt;

&lt;p&gt;It's tempting to read years of an open issue as a quiet no. It isn't: the feature is plainly &lt;em&gt;wanted&lt;/em&gt; — it just hasn't been built yet, because building it well is hard. Architecture, finite maintainer bandwidth, awkward data; any one would slow a feature down, and together they explain the timeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Audit is a thin proxy, by design.&lt;/strong&gt; &lt;code&gt;yarn npm audit --json&lt;/code&gt; is largely a pass-through to the registry's audit endpoint. Yarn returns the server's verdict; it doesn't own a remediation model. That's the "leaky abstraction" from #6959: the report reflects the registry's worldview, which isn't the same as Yarn's resolution graph. Reasonably, Yarn's maintainers have scoped remediation to &lt;code&gt;yarn up --recursive&lt;/code&gt; and &lt;code&gt;resolutions&lt;/code&gt; rather than the &lt;code&gt;audit&lt;/code&gt; command — leaving auto-fix as plugin territory.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. The advisory data is awkward to build on.&lt;/strong&gt; Advisory ranges aren't always valid semver (&lt;code&gt;&amp;gt;1.0.0 &amp;lt;2.0.0&lt;/code&gt; and friends need coercion before you can compare). And the signal itself is still settling: &lt;a href="https://github.com/yarnpkg/berry/issues/6438" rel="noopener noreferrer"&gt;audit IDs aren't stable across runs&lt;/a&gt;, &lt;a href="https://github.com/yarnpkg/berry/issues/5104" rel="noopener noreferrer"&gt;ignores used to misfire for a package included multiple times&lt;/a&gt;, and the endpoint has &lt;a href="https://github.com/yarnpkg/berry/issues/4117" rel="noopener noreferrer"&gt;returned 400s on ordinary lockfiles&lt;/a&gt;. Reliable auto-fix needs a stable input, and much of that input lives upstream of Yarn.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. "Fixing" a lockfile is re-resolution, not string replacement.&lt;/strong&gt; Bump one entry and its &lt;em&gt;transitive&lt;/em&gt; dependencies should change too — but, as I found writing the Berry tool, &lt;strong&gt;Yarn doesn't reload a patched entry's dependencies for you&lt;/strong&gt;. A correct fix has to re-resolve subtrees against the registry and keep the lockfile internally consistent and deterministic. With PnP and Yarn's strict linker, a naive in-place edit yields an install that doesn't reproduce. The community plugin takes the pragmatic route: ask Yarn's own resolver for the highest version &lt;em&gt;within a package's declared range&lt;/em&gt;, and if it clears the advisory, pin that version and re-install. That works right up to the ceiling where most deep transitives live — when the only patched version is a cross-major bump &lt;em&gt;out&lt;/em&gt; of range, the highest in-range candidate still doesn't clear the advisory, so it reports &lt;em&gt;no compatible patched version&lt;/em&gt; and gives up on that package. Its own roadmap names the rest of the climb: &lt;em&gt;walk up the tree to fix via parents, update manifests for direct deps, add a flag for out-of-range bumps.&lt;/em&gt; None of it is trivial.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Most vulnerabilities are transitive and deep.&lt;/strong&gt; The vulnerable package is rarely one you installed. When it's three levels down, the parent controls its version, and the only real fix is often a &lt;strong&gt;cross-major&lt;/strong&gt; bump that breaks the consumer's declared range. Doing that safely — and knowing when not to — is the actual hard part.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. npm's &lt;code&gt;audit fix&lt;/code&gt; shows the trap.&lt;/strong&gt; Even copying npm wouldn't be a clean win. In range, &lt;code&gt;npm audit fix&lt;/code&gt; often reports &lt;em&gt;"fixed 0 of N"&lt;/em&gt; because the patched version is out of range; reach for &lt;a href="https://instatunnel.my/blog/why-npm-audit-fix---force-is-a-terrible-idea" rel="noopener noreferrer"&gt;&lt;code&gt;--force&lt;/code&gt; and it can silently do semver-major upgrades and break builds&lt;/a&gt;; and a declared &lt;a href="https://github.com/npm/cli/issues/7987" rel="noopener noreferrer"&gt;&lt;code&gt;overrides&lt;/code&gt; entry could block it from replacing even eligible dependencies&lt;/a&gt; until npm 11.2. A built-in &lt;code&gt;yarn audit fix&lt;/code&gt; would inherit all of this — plus PnP.&lt;/p&gt;

&lt;h2&gt;
  
  
  The remediation landscape (and the gap for a local fix)
&lt;/h2&gt;

&lt;p&gt;So how &lt;em&gt;do&lt;/em&gt; teams remediate on Yarn? Two tiers, and the gap they leave.&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in / local (manual)
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;yarn up &amp;lt;pkg&amp;gt; --recursive&lt;/code&gt; per advisory, or pin transitive versions via &lt;code&gt;resolutions&lt;/code&gt;, then re-install. This is the maintainer-endorsed path and it works — but it's manual, per-package, and easy to get subtly wrong at scale.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bots (asynchronous, hosted)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.github.com/en/code-security/dependabot" rel="noopener noreferrer"&gt;Dependabot&lt;/a&gt;&lt;/strong&gt; — GitHub-native, zero-config, opens PRs from the GitHub Advisory DB. No reachability analysis; remediation is "here's a PR, you review and merge."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://docs.renovatebot.com" rel="noopener noreferrer"&gt;Renovate&lt;/a&gt;&lt;/strong&gt; — the most configurable updater, multi-platform. It's an &lt;em&gt;update&lt;/em&gt; tool, not a scanner — you layer SCA on top.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://snyk.io" rel="noopener noreferrer"&gt;Snyk&lt;/a&gt;&lt;/strong&gt; — its own advisory DB (often ahead of NVD), automated fix PRs, monitoring, IDE plugins. Commercial, cloud.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://socket.dev" rel="noopener noreferrer"&gt;Socket.dev&lt;/a&gt;&lt;/strong&gt; — a different axis: behavioral / supply-chain analysis (malicious install scripts, network access, typosquats) that CVE scanners miss. Detection-first.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are excellent, and most serious teams should run one. But they share a shape: &lt;strong&gt;asynchronous, hosted, repo-bound, review-gated.&lt;/strong&gt; They assume a cloud-connected VCS, a bot identity, and a PR cadence.&lt;/p&gt;

&lt;h3&gt;
  
  
  The gap they leave
&lt;/h3&gt;

&lt;p&gt;What none of them gives a Yarn user is the thing an npm user gets from &lt;code&gt;npm audit fix&lt;/code&gt;: a &lt;strong&gt;local, synchronous, one-command, scriptable "fix my &lt;code&gt;yarn.lock&lt;/code&gt; &lt;em&gt;now&lt;/em&gt;."&lt;/strong&gt; That shape matters exactly where many enterprises live:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;air-gapped / regulated&lt;/strong&gt; environments where SaaS bots and a chatty GitHub App are non-starters;&lt;/li&gt;
&lt;li&gt;a &lt;strong&gt;CI gate&lt;/strong&gt; that must remediate in the same job, not three PRs later;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;pre-commit / local dev&lt;/strong&gt; loops where you want the fix before you push;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Yarn monorepos&lt;/strong&gt;, where bot support for workspaces + PnP + &lt;code&gt;resolutions&lt;/code&gt; is uneven.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That gap is why community tools exist at all — &lt;a href="https://www.npmjs.com/package/yarn-audit-fix" rel="noopener noreferrer"&gt;&lt;code&gt;yarn-audit-fix&lt;/code&gt;&lt;/a&gt;, the &lt;a href="https://github.com/sargunv/yarn-plugin-npm-audit-fix" rel="noopener noreferrer"&gt;sargunv plugin&lt;/a&gt;, and &lt;a href="https://dev.to/andypotts/automatically-upgrade-security-vulnerabilities-with-this-yarn-audit-fix-alternative-3oo0"&gt;other&lt;/a&gt; takes on it. The need is real and the core feature is hard, so the ecosystem fills in around it. That's open source working as intended.&lt;/p&gt;

&lt;h2&gt;
  
  
  How we try to help
&lt;/h2&gt;

&lt;p&gt;We've kept at it, in the open. &lt;code&gt;yarn-audit-fix&lt;/code&gt; tracks every Yarn lockfile schema that shipped — Classic (v1), Berry v4–v6 (Yarn 2/3), and &lt;strong&gt;Yarn 4+&lt;/strong&gt; (Berry v8/v9/v10) — detecting the version at runtime and parsing each into one shared dependency graph.&lt;/p&gt;

&lt;p&gt;Two things have shifted since those early posts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where the data comes from.&lt;/strong&gt; The first versions shelled out: convert the lockfile, run someone else's &lt;code&gt;audit fix&lt;/code&gt;, parse the CLI output back. The current line talks to the registry directly — it reads advisories from the registry's bulk audit endpoint, resolves fix versions (and the dependencies they pull in) from package metadata, and fetches tarballs to recompute integrity hashes. &lt;strong&gt;No &lt;code&gt;npm&lt;/code&gt; or &lt;code&gt;yarn&lt;/code&gt; subprocess anywhere&lt;/strong&gt; — no second package manager to install, no CLI output format to keep chasing, just HTTP to a registry you already trust, using your existing scoped &lt;code&gt;.npmrc&lt;/code&gt; / &lt;code&gt;.yarnrc.yml&lt;/code&gt; auth (host-bound, https-only).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How the fix is computed.&lt;/strong&gt; Remediation is a deterministic transformation over that graph (&lt;a href="https://github.com/lockgraph/lockgraph" rel="noopener noreferrer"&gt;&lt;code&gt;lockgraph&lt;/code&gt;&lt;/a&gt;): move the vulnerable node to the lowest &lt;em&gt;published&lt;/em&gt; version that clears the advisory, rebind the edges that pointed at it — and then &lt;strong&gt;re-resolve that new version's own dependency closure from the registry.&lt;/strong&gt; That last step is the one flagged as the hard part above: a patched package's transitive deps can differ from the version you had — &lt;code&gt;simple-git&lt;/code&gt; past 3.34, for instance, splits out &lt;code&gt;@simple-git/args-pathspec&lt;/code&gt; and &lt;code&gt;@simple-git/argv-parser&lt;/code&gt; — and Yarn won't reload them for you. Pull them in and the result is a lockfile that actually &lt;em&gt;reproduces&lt;/em&gt;: the resolutions are the ones a clean &lt;code&gt;yarn install&lt;/code&gt; would compute, and the lock passes &lt;code&gt;yarn install --immutable&lt;/code&gt;. A compatibility gate keeps the default safe — a fix applies only when it still satisfies every surviving consumer's declared range, with the riskier cross-major bumps behind an explicit &lt;code&gt;--force&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Building on a shared model has a nice side effect: when the real-world corpus turns up a parser gap, we file it and it gets fixed — the ecosystem gets a little better for everyone, not just for us.&lt;/p&gt;

&lt;p&gt;This is meant as a contribution, not a verdict. If Yarn ships a native &lt;code&gt;audit fix&lt;/code&gt; one day, we'll happily retire ours and help however we can. Until then, consider this a community on-ramp.&lt;/p&gt;

&lt;p&gt;The graph model itself — the edge-redirect patch, the closure re-resolution, the compatibility gate, and how it stays faithful to every Yarn lockfile format from Classic to Berry v10 — is the next part.&lt;/p&gt;




&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Yarn issues: &lt;a href="https://github.com/yarnpkg/yarn/issues/7075" rel="noopener noreferrer"&gt;&lt;code&gt;yarn#7075&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/yarnpkg/berry/issues/3582" rel="noopener noreferrer"&gt;&lt;code&gt;berry#3582&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/yarnpkg/berry/issues/6959" rel="noopener noreferrer"&gt;&lt;code&gt;berry#6959&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/yarnpkg/berry/issues/4117" rel="noopener noreferrer"&gt;&lt;code&gt;berry#4117&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/yarnpkg/berry/issues/6438" rel="noopener noreferrer"&gt;&lt;code&gt;berry#6438&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://github.com/yarnpkg/berry/issues/5104" rel="noopener noreferrer"&gt;&lt;code&gt;berry#5104&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Community plugin: &lt;a href="https://github.com/sargunv/yarn-plugin-npm-audit-fix" rel="noopener noreferrer"&gt;&lt;code&gt;sargunv/yarn-plugin-npm-audit-fix&lt;/code&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;npm / pnpm: &lt;a href="https://github.com/npm/cli/issues/7987" rel="noopener noreferrer"&gt;&lt;code&gt;npm/cli#7987&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://pnpm.io/cli/audit" rel="noopener noreferrer"&gt;pnpm audit docs&lt;/a&gt;, &lt;a href="https://github.com/pnpm/pnpm/issues/8943" rel="noopener noreferrer"&gt;&lt;code&gt;pnpm#8943&lt;/code&gt;&lt;/a&gt;, &lt;a href="https://instatunnel.my/blog/why-npm-audit-fix---force-is-a-terrible-idea" rel="noopener noreferrer"&gt;Why &lt;code&gt;npm audit fix --force&lt;/code&gt; is risky&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Tooling comparisons: &lt;a href="https://rafter.so/blog/sca-tools-comparison" rel="noopener noreferrer"&gt;SCA tools compared&lt;/a&gt;, &lt;a href="https://reintech.io/blog/dependabot-vs-renovate-vs-snyk-dependency-management-tools-compared" rel="noopener noreferrer"&gt;Dependabot vs Renovate vs Snyk&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Yarn versions / EOL: &lt;a href="https://endoflife.date/yarn" rel="noopener noreferrer"&gt;endoflife.date/yarn&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Earlier notes: &lt;a href="https://dev.to/antongolub/yarn-audit-fix-workaround-i2a"&gt;&lt;em&gt;Yarn audit fix: workaround&lt;/em&gt;&lt;/a&gt;, &lt;a href="https://dev.to/antongolub/the-missing-yarn-audit-fix-for-yarn-2-berry-1p8"&gt;&lt;em&gt;The missing &lt;code&gt;yarn audit fix&lt;/code&gt; for Yarn 2+ Berry&lt;/em&gt;&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>security</category>
      <category>tools</category>
      <category>supplychain</category>
    </item>
    <item>
      <title>CVE-2024-29415: problem solution</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Tue, 16 Sep 2025 14:39:58 +0000</pubDate>
      <link>https://dev.to/antongolub/cve-2024-29415-problem-solution-3o3f</link>
      <guid>https://dev.to/antongolub/cve-2024-29415-problem-solution-3o3f</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;an illustrative note on the relationship logic between Enterprise and OSS based on a specific issue&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If you've landed on this page, it's likely that your codebase relies on a widely deployed package that is currently struggling to maintain and is therefore &lt;a href="https://github.com/advisories/GHSA-2p57-rm9w-gvfp" rel="noopener noreferrer"&gt;vulnerable&lt;/a&gt; to certain scenarios. You have several strategies to follow:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/indutny/node-ip/issues/158" rel="noopener noreferrer"&gt;Ask the author for a fix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/indutny/node-ip/pull/157" rel="noopener noreferrer"&gt;Suggest a fix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Apply patches locally&lt;/li&gt;
&lt;li&gt;Override internal registry versions&lt;/li&gt;
&lt;li&gt;Consider migrating to other solutions&lt;/li&gt;
&lt;li&gt;Just live with it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's always a question of rational choice and cost of effort. In our case, the dependency tree profile required too much cascading fixes including external components, so we decided to take matters into our own hands — create a fully compatible alternative and apply it to our systems.&lt;/p&gt;

&lt;p&gt;We found that the address classification error (&lt;code&gt;private/public&lt;/code&gt;) was due to a lack of strictness in the parser implementation, and the format error was interpreted as a sign of being in (or out of) the range. An additional complication was that the logic was divided into several auxiliary functions, but was combined differently in the compositions of parsing addresses of variouus notations.&lt;/p&gt;

&lt;h2&gt;
  
  
  Algorithm
&lt;/h2&gt;

&lt;p&gt;&lt;a href="[](https://github.com/webpod/ip/pull/3)"&gt;In the first attempt&lt;/a&gt;, we simply made the pattern conditions strict, but as expected, we encountered the problem of reduced performance. It became immediately clear that stricter conditions would require a different algorithmic approach.&lt;br&gt;
Here we moved step by step: from &lt;code&gt;split()&lt;/code&gt; to a string traverser, until we were satisfied with the ops/s metric.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/webpod/ip/pull/17" rel="noopener noreferrer"&gt;https://github.com/webpod/ip/pull/17&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/webpod/ip/pull/18" rel="noopener noreferrer"&gt;https://github.com/webpod/ip/pull/18&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The second significant change was related to the internal structure of stored addresses: &lt;code&gt;Buffers&lt;/code&gt; have been replaced with &lt;code&gt;bigints&lt;/code&gt;, which allowed us to greatly simplify and speed up the logic of comparisons and format translation. So it became easy to implement proper and full support for IPv6 for &lt;code&gt;mask()&lt;/code&gt; and &lt;code&gt;subnet()&lt;/code&gt; methods.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/webpod/ip/pull/10" rel="noopener noreferrer"&gt;https://github.com/webpod/ip/pull/10&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/indutny/node-ip/issues/110" rel="noopener noreferrer"&gt;https://github.com/indutny/node-ip/issues/110&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;0.0.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;big&lt;/span&gt; &lt;span class="c1"&gt;// 0n&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The third major aspect of algorithmic improvements is related to the refactoring of special IP address ranges declarations. Instead of a set of &lt;em&gt;magic&lt;/em&gt; conditions in helpers, a single source of truth is allocated now, and productive predicate functions are preinitialized.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://en.wikipedia.org/wiki/Reserved_IP_addresses" rel="noopener noreferrer"&gt;https://en.wikipedia.org/wiki/Reserved_IP_addresses&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;SPECIALS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Special&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;loopback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;127.0.0.0/8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;    &lt;span class="c1"&gt;// IPv4 loopback&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;::1/128&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;        &lt;span class="c1"&gt;// IPv6 loopback&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;SPECIAL_MATCHERS&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;Special&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cidrs&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SPECIALS&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cidr&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;cidrs&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;x&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nf"&gt;ipv6fySubnet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cidr&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;subnet&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cidrSubnet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;x&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;SPECIAL_MATCHERS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Address&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;family&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;family&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;subnet&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;contains&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;addr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cat&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;Special&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Compatibility
&lt;/h2&gt;

&lt;p&gt;The key point was to keep backward compatibility. To ensure this, we completely &lt;a href="https://github.com/webpod/ip/blob/main/src/test/js/legacy.test.js" rel="noopener noreferrer"&gt;borrowed the test suite&lt;/a&gt; from the upstream repo. For clarity, we also &lt;a href="https://github.com/webpod/ip/blob/main/COHERENCE.md" rel="noopener noreferrer"&gt;additionally investigated&lt;/a&gt; how similar other IP tools are in their operating logic. The conclusion is obvious: without additional modifications, it is impossible to achieve equivalent operating results in all scenarios. It was also important for us to make sure that the library functionality would not differ in different versions of runtimes and would be compatible with the browser environment. &lt;a href="https://github.com/webpod/ip/blob/main/src/main/ts/polyfill.ts" rel="noopener noreferrer"&gt;Polyfills&lt;/a&gt; and an extensive palette of smoke tests are right for this. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/webpod/ip/pull/4" rel="noopener noreferrer"&gt;https://github.com/webpod/ip/pull/4&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/webpod/ip/blob/main/.github/workflows/ci.yaml" rel="noopener noreferrer"&gt;https://github.com/webpod/ip/blob/main/.github/workflows/ci.yaml&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Drop-in
&lt;/h2&gt;

&lt;p&gt;As we noted at the beginning, the process of cascading dependency updates is expensive, and some tools of the JS ecosystem provide workarounds for this. And the package should strive for seamlessness: so we've added an &lt;a href="https://github.com/webpod/ip/pull/22" rel="noopener noreferrer"&gt;additional pkg entry point&lt;/a&gt; to support &lt;em&gt;legacy IDE-hinted&lt;/em&gt; &lt;code&gt;require('ip/lib/ip.js)&lt;/code&gt; statements.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;- const ip &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ip'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
+ const ip &lt;span class="o"&gt;=&lt;/span&gt; require&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'@webpod/ip'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Temporary workaround to avoid refactoring is using &lt;code&gt;overrides&lt;/code&gt; / &lt;code&gt;resolutions&lt;/code&gt; in your &lt;code&gt;package.json&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"overrides"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"ip"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"@webpod/ip"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;p&gt;There is no discussion here. The only correct way is to push changes to upstream, reduce the heterogeneity of dependencies. I hope that maintainers, when they have enough time, will support &lt;a href="https://github.com/indutny/node-ip/issues/128" rel="noopener noreferrer"&gt;the community's efforts in this direction&lt;/a&gt;. Until that happens, there is &lt;a href="https://github.com/webpod/ip" rel="noopener noreferrer"&gt;@webpod/ip&lt;/a&gt;&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
      <category>opensource</category>
      <category>npm</category>
    </item>
    <item>
      <title>zx@lite: minimalistic shell scripting with TS/JS superpowers</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Thu, 19 Jun 2025 14:33:00 +0000</pubDate>
      <link>https://dev.to/antongolub/zxlite-minimalistic-shell-scripting-with-tsjs-superpowers-1j50</link>
      <guid>https://dev.to/antongolub/zxlite-minimalistic-shell-scripting-with-tsjs-superpowers-1j50</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Ditch clunky bash syntax. Script smarter — with fewer dependencies and full TypeScript support.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  ⚡ What is &lt;code&gt;zx@lite&lt;/code&gt;?
&lt;/h2&gt;

&lt;p&gt;The original &lt;a href="https://github.com/google/zx" rel="noopener noreferrer"&gt;&lt;code&gt;zx&lt;/code&gt;&lt;/a&gt; makes shell automations delightful in modern JS runtimes. The &lt;code&gt;@lite&lt;/code&gt; version, introduced in &lt;a href="https://github.com/google/zx/releases/tag/8.5.0" rel="noopener noreferrer"&gt;v8.5.0&lt;/a&gt;, gives you optimal performance, devX without the extra API bloat:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ Core zx functionality (&lt;code&gt;$&lt;/code&gt;, &lt;code&gt;cd&lt;/code&gt;, &lt;code&gt;ps&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;✅ First-class TypeScript support&lt;/li&gt;
&lt;li&gt;⚡ ~7× smaller than full &lt;code&gt;zx&lt;/code&gt; (~124 KB vs ~875 kB)&lt;/li&gt;
&lt;li&gt;🧱 Static build with zero dependencies.&lt;/li&gt;
&lt;li&gt;🔌 Wide compat range: Node.js 12+, Deno 1.30+, Bun 1.0+, GraalVM 17+ on Win, Mac or Linux&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ideal for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fast script prototyping&lt;/li&gt;
&lt;li&gt;DevOps and CI automations&lt;/li&gt;
&lt;li&gt;Docker images&lt;/li&gt;
&lt;li&gt;Small CLI tooling&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧾 Setup
&lt;/h2&gt;



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

&lt;/div&gt;



&lt;p&gt;TypeScript is an option but highly recommended for safety and better IDE support.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-D&lt;/span&gt; typescript @types/node
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Attach &lt;code&gt;tsconfig.json&lt;/code&gt; like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"compilerOptions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"target"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"module"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"esnext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"moduleResolution"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodenext"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"strict"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  🧪 Examples
&lt;/h2&gt;

&lt;p&gt;Let’s say you want to clone several repos and list their directories. In bash, this would be a mess of &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;wait&lt;/code&gt;, and error handling. In &lt;code&gt;zx@lite&lt;/code&gt;, it’s much cleaner:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cp"&gt;#!/usr/bin/env zx
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="c1"&gt;// Run 3 commands in parallel&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git clone https://github.com/google/zx zx-clone`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git clone https://github.com/webpod/webpod webpod-clone`&lt;/span&gt;
&lt;span class="p"&gt;])&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`ls -1`&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Cloned directories:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;output&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Native parallelism&lt;/strong&gt; with &lt;code&gt;Promise.all()&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Clean output handling&lt;/strong&gt; via &lt;a href="https://google.github.io/zx/process-promise#json-text-lines-buffer-blob" rel="noopener noreferrer"&gt;&lt;code&gt;stdout&lt;/code&gt;, &lt;code&gt;lines()&lt;/code&gt;, &lt;code&gt;json()&lt;/code&gt; helpers&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Controlled error flow&lt;/strong&gt;: configurable &lt;a href="https://google.github.io/zx/api#nothrow" rel="noopener noreferrer"&gt;exception throwing behavior&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combine &lt;code&gt;zx@lite&lt;/code&gt; with runtime features. Need transport? Just bring in native &lt;code&gt;fetch&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="cp"&gt;#!/usr/bin/env zx
&lt;/span&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;zx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;google/zx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://api.github.com/repos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/releases/latest`&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;tag_name&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;zip_file&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;-&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;-&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.zip`&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`curl -o &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;zip_file&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -L &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;latest&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;zipball_url&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`mkdir -p releases/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`unzip &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;zip_file&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -d releases/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;✅ Done&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  ⚠️ What’s missing in &lt;code&gt;zx@lite&lt;/code&gt;?
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Feature&lt;/th&gt;
&lt;th&gt;&lt;code&gt;zx&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;&lt;code&gt;zx@lite&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;$&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;cd&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ps&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;glob&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;YAML&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fs-extra&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;minimist&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Check the full &lt;a href="https://google.github.io/zx/versions" rel="noopener noreferrer"&gt;API comparison&lt;/a&gt; for details.&lt;/p&gt;

&lt;h2&gt;
  
  
  📦 Upgrade path
&lt;/h2&gt;

&lt;p&gt;Switching between lite and full is trivial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Go full speed:&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;zx

&lt;span class="c"&gt;# Back to lean mode:&lt;/span&gt;
npm &lt;span class="nb"&gt;install &lt;/span&gt;zx@lite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  📚 References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://google.github.io/zx/versions" rel="noopener noreferrer"&gt;Documentation&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/google/zx" rel="noopener noreferrer"&gt;Sources&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/google/zx/network/dependents" rel="noopener noreferrer"&gt;Real world use cases&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/webpod/zurk" rel="noopener noreferrer"&gt;Zurk prototype&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  🧠 Final thoughts
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;zx@lite&lt;/code&gt; is everything you need — and nothing you don’t — for modern scripting with real programming ergonomics. Stop writing fragile bash. Start scripting like a developer.&lt;/p&gt;

&lt;p&gt;💬 Got ideas, snippets, or &lt;code&gt;zx@lite&lt;/code&gt; tips? Drop them below or feel free to share in the project repo: &lt;a href="https://github.com/google/zx/discussions" rel="noopener noreferrer"&gt;discussions&lt;/a&gt;, &lt;a href="https://github.com/google/zx/issues" rel="noopener noreferrer"&gt;issues&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>shell</category>
      <category>devops</category>
      <category>cicd</category>
    </item>
    <item>
      <title>How and why do we bundle zx?</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Mon, 06 May 2024 08:12:21 +0000</pubDate>
      <link>https://dev.to/antongolub/how-and-why-do-we-bundle-zx-1ca6</link>
      <guid>https://dev.to/antongolub/how-and-why-do-we-bundle-zx-1ca6</guid>
      <description>&lt;p&gt;When &lt;a href="https://github.com/google/zx" rel="noopener noreferrer"&gt;zx&lt;/a&gt; first appeared, it was a &lt;a href="https://github.com/google/zx/commit/ded16dd382b393a54a7aa7900f41ac306e4f27b8" rel="noopener noreferrer"&gt;tiny esm script&lt;/a&gt; that just proposed a new idea for how &lt;code&gt;child_process.spawn&lt;/code&gt; API could be enhanced with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Template_literals" rel="noopener noreferrer"&gt;string template literals&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`call smth --with any &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As we now know, this was exactly what JS folks had been looking for for many years :-). The main goal was to improve the developer experience, so we focused on finding the best combination of utils to solve everyday problems, expand the area of application.&lt;/p&gt;
&lt;h2&gt;
  
  
  Short history
&lt;/h2&gt;

&lt;p&gt;The project went through 8 major releases, but in the context of the assembly issues, there are only 3 significant ones.&lt;/p&gt;
&lt;h3&gt;
  
  
  Packed as CommonJS
&lt;/h3&gt;

&lt;p&gt;Despite the fact that the future belongs to ESM, many users have to remain in the notation of their legacy codebase (without any irony). So we have added compatibility with the &lt;code&gt;require&lt;/code&gt; API for them.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/google/zx/commit/303feea3cd0d95e8d733ee72b96b3316ea5827aa" rel="noopener noreferrer"&gt;https://github.com/google/zx/commit/303feea3cd0d95e8d733ee72b96b3316ea5827aa&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Dropped CJS support
&lt;/h3&gt;

&lt;p&gt;On the other hand, there were libraries on which zx depend: and only the latest &lt;em&gt;modern&lt;/em&gt; major versions received patches, including vulnerabilities fixes. In fact we inherited CVE issues transitively, which was completely unacceptable. And we switched back to ESM.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/google/zx/commit/5578d67ebae2049d430bf6489784f6a2aa47b0b1" rel="noopener noreferrer"&gt;https://github.com/google/zx/commit/5578d67ebae2049d430bf6489784f6a2aa47b0b1&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Migrated to TypeScript
&lt;/h3&gt;

&lt;p&gt;As the code base grew, it became harder for us to ensure consistency of internal contracts. Adding typing was the solution, but it also made us dependent on transpilation and we could no longer control the package artifact in detail.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/google/zx/commit/8c2aea89fe7c65767d29c52bfb48f6a46af01864" rel="noopener noreferrer"&gt;https://github.com/google/zx/commit/8c2aea89fe7c65767d29c52bfb48f6a46af01864&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Problems
&lt;/h2&gt;
&lt;h3&gt;
  
  
  Size
&lt;/h3&gt;

&lt;p&gt;Filling the zx with new functionality, we continued to expand the dependency tree, which required their own dependencies, etc. Installation time increased significantly. And at some point it became impossible to ignore: &lt;a href="https://github.com/google/zx/issues/712" rel="noopener noreferrer"&gt;zx#712&lt;/a&gt;, &lt;a href="https://github.com/google/zx/issues/669" rel="noopener noreferrer"&gt;zx#669&lt;/a&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;1.15.2&lt;/th&gt;
&lt;th&gt;2.1.0&lt;/th&gt;
&lt;th&gt;3.1.0&lt;/th&gt;
&lt;th&gt;4.3.0&lt;/th&gt;
&lt;th&gt;5.3.0&lt;/th&gt;
&lt;th&gt;6.2.5&lt;/th&gt;
&lt;th&gt;7.2.3&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1.38 MB&lt;/td&gt;
&lt;td&gt;3.01 MB&lt;/td&gt;
&lt;td&gt;3.01 MB&lt;/td&gt;
&lt;td&gt;3.89 MB&lt;/td&gt;
&lt;td&gt;10.5 MB&lt;/td&gt;
&lt;td&gt;10.7 MB&lt;/td&gt;
&lt;td&gt;16.3 MB&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;h3&gt;
  
  
  Compatibility
&lt;/h3&gt;

&lt;p&gt;When we define &lt;a href="https://github.com/google/zx/commit/fadc6a31517f22afc332fd33ac47a40f2bda5f78" rel="noopener noreferrer"&gt;node engine&lt;/a&gt; requirement we limit not users, but ourselves in what platform API we can use. But this in no way prevents our dependencies from &lt;a href="https://github.com/sindresorhus/globby/blob/main/package.json#L20" rel="noopener noreferrer"&gt;pursuing their own policies&lt;/a&gt; which affects zx.&lt;/p&gt;
&lt;h3&gt;
  
  
  Stability &amp;amp; Security
&lt;/h3&gt;

&lt;p&gt;Reproducible setups are important. Especially for utilities of our type. We can try to pin down versions, but if there is a caret (^, ~) or an asterisk (*) at the secondary level, this introduces node_modules variability. Lockfiles can mitigate the problem, but not for npx/yarn dlx execution case.&lt;/p&gt;
&lt;h2&gt;
  
  
  Bundle
&lt;/h2&gt;

&lt;p&gt;At first we wanted to just &lt;a href="https://github.com/google/zx/issues/550" rel="noopener noreferrer"&gt;get rid of all&lt;/a&gt; the helper utilities. Keep only the kernel, but this would mean a loss of backward compatibility. We needed some efficient code processing instead with recomposition and tree-shaking. We needed a bundler. But which one? Our testing approach &lt;a href="https://github.com/google/zx/blob/main/test/core.test.js#L21" rel="noopener noreferrer"&gt;relies on targets&lt;/a&gt;, not sources. We rebuilt the project frequently, speed was critical requirement. In essence, we chose a solution from a couple of among all available alternatives: &lt;a href="https://esbuild.github.io/" rel="noopener noreferrer"&gt;esbuild&lt;/a&gt; and &lt;a href="https://parceljs.org/" rel="noopener noreferrer"&gt;parcel&lt;/a&gt;. Esbuild won. &lt;strong&gt;Specifically in our case&lt;/strong&gt;, it proved to be more productive and customizable.&lt;/p&gt;
&lt;h3&gt;
  
  
  js
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Customizable&lt;/em&gt; means that we found a way to adapt the tool for our tasks. And a lot had to be improved, including writing our own plugins:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Esbuild provides &lt;a href="https://esbuild.github.io/api/#splitting" rel="noopener noreferrer"&gt;code-splitting&lt;/a&gt; with dynamic imports to reuse common code chunks. But not is static form. The &lt;a href="https://github.com/evanw/esbuild/issues/207" rel="noopener noreferrer"&gt;manual chunks API&lt;/a&gt; is missing.&lt;br&gt;&lt;a href="https://github.com/antongolub/misc/tree/master/packages/esbuild/plugin-entry-chunks" rel="noopener noreferrer"&gt;esbuild-plugin-entry-chunks&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;By default, esbuild injects helpers into each cjs module. It's fine, when you have just a few, but definitely not when there are many.&lt;br&gt;&lt;a href="https://github.com/antongolub/misc/tree/master/packages/esbuild/plugin-extract-helpers" rel="noopener noreferrer"&gt;esbuild-plugin-extract-helpers&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;We also wanted to bring back cjs support, but at the same time avoid duplicating code in two versions of the bundles.&lt;br&gt;&lt;a href="https://github.com/antongolub/misc/tree/master/packages/esbuild/plugin-hybrid-export" rel="noopener noreferrer"&gt;esbuild-plugin-hybrid-export&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Esbuild and its plugins mostly focus on sources processing, but sometimes additional modification is also necessary for dependencies or bundles:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;Polyfill injects: &lt;a href="https://github.com/evanw/esbuild/issues/2840" rel="noopener noreferrer"&gt;esbuild#2840&lt;/a&gt;, &lt;a href="https://github.com/evanw/esbuild/issues/3517" rel="noopener noreferrer"&gt;esbuild#3517&lt;/a&gt;, &lt;a href="https://github.com/evanw/esbuild/issues/3099" rel="noopener noreferrer"&gt;esbuild#3099&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Custom patches: &lt;a href="https://github.com/evanw/esbuild/issues/3360" rel="noopener noreferrer"&gt;esbuild#3360&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Dynamic banners: &lt;a href="https://github.com/evanw/esbuild/issues/3291" rel="noopener noreferrer"&gt;esbuild#3291&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Importable helpers: &lt;a href="https://github.com/evanw/esbuild/issues/1230" rel="noopener noreferrer"&gt;esbuild#1230&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://github.com/antongolub/misc/tree/master/packages/esbuild/plugin-transform-hook" rel="noopener noreferrer"&gt;esbuild-plugin-transform-hook&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  d.ts
&lt;/h3&gt;

&lt;p&gt;While we were fighting against the modules, we forgot one small detail - their built-in typings. Esbuild can't do this at all yet. Unbelievable, but the tsc, native TS compiler, also does not provide a &lt;a href="https://github.com/microsoft/TypeScript/issues/4433" rel="noopener noreferrer"&gt;typings concat feature&lt;/a&gt;. Got around this problem: we've introduced a &lt;a href="https://github.com/antongolub/misc/tree/master/packages/dep/tsc-dts-fix" rel="noopener noreferrer"&gt;utility to combine typings&lt;/a&gt; of zx own code, and applied some &lt;em&gt;monkey&lt;/em&gt; patches for external libdefs squashed via &lt;a href="https://github.com/timocov/dts-bundle-generator" rel="noopener noreferrer"&gt;dts-bundle-generator&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Results
&lt;/h2&gt;

&lt;p&gt;There is no silver bullet, all tools and approaches have limitations. Summary:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Install size reduced: &lt;strong&gt;16Mb → 1Mb&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Reproducible npx invocations&lt;/li&gt;
&lt;li&gt;Hybrid package: ESM and CJS&lt;/li&gt;
&lt;li&gt;Wide range of supported Node.js versions 12...22&lt;/li&gt;
&lt;li&gt;Easy to audit: complete code in one place&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Perhaps our efforts are not commensurate with the profit. But we gained invaluable experience. Now we understand the entire zx code in detail down to each char.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/google/zx/tree/main/scripts" rel="noopener noreferrer"&gt;zx build setup&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/google" rel="noopener noreferrer"&gt;
        google
      &lt;/a&gt; / &lt;a href="https://github.com/google/zx" rel="noopener noreferrer"&gt;
        zx
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A tool for writing better scripts
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b960f28465c1404ba7b4c15fdd72ae254efb5182eb25dfda09a5509bd43e5709/68747470733a2f2f676f6f676c652e6769746875622e696f2f7a782f696d672f6c6f676f2e737667"&gt;&lt;img src="https://camo.githubusercontent.com/b960f28465c1404ba7b4c15fdd72ae254efb5182eb25dfda09a5509bd43e5709/68747470733a2f2f676f6f676c652e6769746875622e696f2f7a782f696d672f6c6f676f2e737667" alt="Zx logo" height="32"&gt;&lt;/a&gt; zx&lt;/h1&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-js notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;#!/usr/bin/env zx

&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-en"&gt;$&lt;/span&gt;&lt;span class="pl-s"&gt;`cat package.json | grep name`&lt;/span&gt;

&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;branch&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-en"&gt;$&lt;/span&gt;&lt;span class="pl-s"&gt;`git branch --show-current`&lt;/span&gt;
&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-en"&gt;$&lt;/span&gt;&lt;span class="pl-s"&gt;`dep deploy --branch=&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-s1"&gt;branch&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;`&lt;/span&gt;

&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-v"&gt;Promise&lt;/span&gt;&lt;span class="pl-kos"&gt;.&lt;/span&gt;&lt;span class="pl-en"&gt;all&lt;/span&gt;&lt;span class="pl-kos"&gt;(&lt;/span&gt;&lt;span class="pl-kos"&gt;[&lt;/span&gt;
  &lt;span class="pl-en"&gt;$&lt;/span&gt;&lt;span class="pl-s"&gt;`sleep 1; echo 1`&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-en"&gt;$&lt;/span&gt;&lt;span class="pl-s"&gt;`sleep 2; echo 2`&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
  &lt;span class="pl-en"&gt;$&lt;/span&gt;&lt;span class="pl-s"&gt;`sleep 3; echo 3`&lt;/span&gt;&lt;span class="pl-kos"&gt;,&lt;/span&gt;
&lt;span class="pl-kos"&gt;]&lt;/span&gt;&lt;span class="pl-kos"&gt;)&lt;/span&gt;

&lt;span class="pl-k"&gt;const&lt;/span&gt; &lt;span class="pl-s1"&gt;name&lt;/span&gt; &lt;span class="pl-c1"&gt;=&lt;/span&gt; &lt;span class="pl-s"&gt;'foo bar'&lt;/span&gt;
&lt;span class="pl-k"&gt;await&lt;/span&gt; &lt;span class="pl-en"&gt;$&lt;/span&gt;&lt;span class="pl-s"&gt;`mkdir /tmp/&lt;span class="pl-s1"&gt;&lt;span class="pl-kos"&gt;${&lt;/span&gt;&lt;span class="pl-s1"&gt;name&lt;/span&gt;&lt;span class="pl-kos"&gt;}&lt;/span&gt;&lt;/span&gt;`&lt;/span&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Bash is great, but when it comes to writing more complex scripts,
many people prefer a more convenient programming language.
JavaScript is a perfect choice, but the Node.js standard library
requires additional hassle before using. The &lt;code&gt;zx&lt;/code&gt; package provides
useful wrappers around &lt;code&gt;child_process&lt;/code&gt;, escapes arguments and
gives sensible defaults.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Install&lt;/h2&gt;

&lt;/div&gt;

&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;npm install zx&lt;/pre&gt;

&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Documentation&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;Read documentation on &lt;a href="https://google.github.io/zx/" rel="nofollow noopener noreferrer"&gt;google.github.io/zx&lt;/a&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;License&lt;/h2&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/google/zxLICENSE" rel="noopener noreferrer"&gt;Apache-2.0&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Disclaimer: &lt;em&gt;This is not an officially supported Google product.&lt;/em&gt;&lt;/p&gt;

&lt;/div&gt;
&lt;br&gt;
&lt;br&gt;
  &lt;/div&gt;
&lt;br&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/google/zx" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;br&gt;
&lt;/div&gt;
&lt;br&gt;


</description>
      <category>zx</category>
      <category>node</category>
      <category>javascript</category>
      <category>tooling</category>
    </item>
    <item>
      <title>config: apply extends</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Mon, 05 Feb 2024 08:00:02 +0000</pubDate>
      <link>https://dev.to/antongolub/config-extends-directive-13p6</link>
      <guid>https://dev.to/antongolub/config-extends-directive-13p6</guid>
      <description>&lt;p&gt;As we know, the forms and approaches to describing configuration files are extremely varied. I discussed this in &lt;a href="https://dev.to/antongolub/topoconfig-enhancing-config-declarations-with-graphs-2d4"&gt;the previous note&lt;/a&gt;. But there are several techniques and practices that may one day become standards.&lt;/p&gt;

&lt;p&gt;One of this – the &lt;code&gt;extends&lt;/code&gt; directive. Many tools provide this feature, but it works a little differently in each place. For example, &lt;code&gt;tsconfig&lt;/code&gt; applies deep merge to &lt;code&gt;compilerOptions&lt;/code&gt;, while &lt;code&gt;prettier&lt;/code&gt; concatenates &lt;code&gt;overrides&lt;/code&gt; array sections, &lt;a href="https://github.com/conventional-changelog/commitlint/blob/79d77c9ef8de5046106027fc7d80dfd77faeb3e5/%40commitlint/resolve-extends/src/index.ts#L29"&gt;commitlint&lt;/a&gt; joins &lt;code&gt;plugin&lt;/code&gt;,  etc. So the authors have to implement these nuances on site every time, and it's tiring and annoying a bit. I believe we will come up with a more efficient way to handle this routine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proposal
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;populate&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;populateSync&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@topoconfig/extends&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tsconfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;populate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tsconfig.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;compilerOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;merge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prettierCfg&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;populateSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.prettierrc.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;overrides&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;merge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  20 minutes adventure
&lt;/h2&gt;

&lt;p&gt;At first glance, the &lt;code&gt;extends&lt;/code&gt; may seem like a simple combination of &lt;code&gt;Object.assign&lt;/code&gt; and &lt;code&gt;fs.readFile&lt;/code&gt;. Well, technically it is, but not quite so. Let's see how the real scenario unfolds. To simplify the details, we will use pseudocode.&lt;/p&gt;

&lt;p&gt;The starting point:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./base.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;pick &lt;code&gt;extends&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;load the referenced &lt;code&gt;./base.json&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;extra&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resource&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;merge &lt;code&gt;input&lt;/code&gt; and &lt;code&gt;extra&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;extra&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How about &lt;code&gt;./base.js&lt;/code&gt;? Reasonable request.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provide js files support
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./base.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;require&lt;/code&gt; hurries to the resque:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;isJs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFileSync&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;./base.mjs&lt;/code&gt;?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ooops. Here the API switches to async mode to handle mjs/esm inputs.

Of course, we can patch &lt;code&gt;require&lt;/code&gt; with &lt;code&gt;esm&lt;/code&gt; package, or load module via a &lt;code&gt;process.spawnSync&lt;/code&gt;ed nodejs. We can, but we won't

&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;load&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;isJs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;import&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;))?.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;promises&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What if the value is passed without an extension, like &lt;code&gt;base&lt;/code&gt;?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add support for shared npm configs&lt;/li&gt;
&lt;li&gt;provide configurable &lt;code&gt;resolve&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;extname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if &lt;code&gt;extends&lt;/code&gt; is a nested object? It definitely is.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;apply everything recursively:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;handle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, circular references are something to pay attention to.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;add index to avoid infinite loops
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;memo&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Map&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="nx"&gt;memoizedLoad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nx"&gt;memo&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Relative resource path should use the closest parent dirname as a base to work properly.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;resolve &lt;code&gt;cwd&lt;/code&gt; for each &lt;code&gt;extends&lt;/code&gt; reference
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;base&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nx"&gt;_opts&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_config&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;string&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dirname&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;basename&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;base&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Multiple &lt;code&gt;extends&lt;/code&gt; maybe?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;let &lt;code&gt;extends&lt;/code&gt; hold several references
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./extra.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./mixin.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;extras&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;input&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;extends&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to read toml, yaml, ini, env? Obviously, custom formats are required too.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provide &lt;code&gt;parse&lt;/code&gt; option
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.yaml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.yml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;parseYaml&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;endsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; 
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;contents&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`Unsupported format: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How to resolve value conflicts: merge / override?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;provide &lt;code&gt;rules&lt;/code&gt; to handle prop-specific join strategies
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;rules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;merge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;override&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getRule&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;propPath&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;isObject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;merge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nf"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="nx"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;index&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What about arrays?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;implement array merge as concat
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;rule&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;merge&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="nx"&gt;arr&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;flat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It would be nice to avoid mutations.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;introduce &lt;code&gt;clone&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;safeLoad&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;compose&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;memoize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;clone&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Summarizing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;search &lt;code&gt;extends&lt;/code&gt; statements&lt;/li&gt;
&lt;li&gt;load &amp;amp; parse referenced resources

&lt;ul&gt;
&lt;li&gt;support async and sync flows&lt;/li&gt;
&lt;li&gt;use cloning to avoid mutations&lt;/li&gt;
&lt;li&gt;.js, .cjs, .mjs, .json&lt;/li&gt;
&lt;li&gt;provide support for custom formats&lt;/li&gt;
&lt;li&gt;resolve cwd for the nested resource by its closest parent resource&lt;/li&gt;
&lt;li&gt;support custom resolvers&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;walk recursively

&lt;ul&gt;
&lt;li&gt;handle circular references&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;li&gt;provide sync and async API&lt;/li&gt;
&lt;li&gt;assemble the result

&lt;ul&gt;
&lt;li&gt;handle arrays / plain objects&lt;/li&gt;
&lt;li&gt;apply prop-specific merge&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Additionally:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;build as cjs and esm&lt;/li&gt;
&lt;li&gt;provide types&lt;/li&gt;
&lt;li&gt;handle windows specific issues&lt;/li&gt;
&lt;li&gt;add dozens of tests&lt;/li&gt;
&lt;li&gt;handle different runtimes specifics (node, deno, bun)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There is an obvious benefit to implementing this algorithm on your own – it clarifies and makes controllable every aspect of config processing in place.&lt;br&gt;
However, we do not always have enough time to do this exercise. Therefore, maybe someone will find this shortcut useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/antongolub/misc/tree/master/packages/topoconfig/extends"&gt;https://github.com/antongolub/misc/tree/master/packages/topoconfig/extends&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.npmjs.com/package/@topoconfig/extends"&gt;https://www.npmjs.com/package/@topoconfig/extends&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>config</category>
      <category>showdev</category>
    </item>
    <item>
      <title>config: enhancing declarations with graphs</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Tue, 03 Oct 2023 11:14:44 +0000</pubDate>
      <link>https://dev.to/antongolub/topoconfig-enhancing-config-declarations-with-graphs-2d4</link>
      <guid>https://dev.to/antongolub/topoconfig-enhancing-config-declarations-with-graphs-2d4</guid>
      <description>&lt;p&gt;Typically application configs are not a bottleneck or development issue. But everything can suddenly change when the context of expands with infrastructure domain, external sources interactions, environments.&lt;/p&gt;

&lt;h2&gt;
  
  
  Config mess
&lt;/h2&gt;

&lt;p&gt;Many years ago &lt;a href="https://en.wikipedia.org/wiki/Configuration_file"&gt;configs&lt;/a&gt; were pretty simple. They looked more or less like &lt;a href="https://en.wikipedia.org/wiki/.properties"&gt;.properties-files&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/INI_file"&gt;INI-files&lt;/a&gt;, simple kv-maps with sections or composite keys to bring some kind of context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight properties"&gt;&lt;code&gt;&lt;span class="c"&gt;# https://docs.oracle.com/cd/E23095_01/Platform.93/ATGProgGuide/html/s0204propertiesfileformat01.html
&lt;/span&gt;
&lt;span class="c"&gt;# You are reading a comment in ".properties" file.
! The exclamation mark can also be used for comments.
# Lines with "properties" contain a key and a value separated by a delimiting character.
# There are 3 delimiting characters: '=' (equal), ':' (colon) and whitespace (space, \t and \f).
&lt;/span&gt;&lt;span class="py"&gt;website&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;https://en.wikipedia.org/&lt;/span&gt;
&lt;span class="py"&gt;language&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;English&lt;/span&gt;
&lt;span class="err"&gt;topic&lt;/span&gt; &lt;span class="err"&gt;.properties&lt;/span&gt; &lt;span class="err"&gt;files&lt;/span&gt;
&lt;span class="c"&gt;# A word on a line will just create a key with no value.
&lt;/span&gt;&lt;span class="err"&gt;empty&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;; last modified 1 April 2001 by John Doe
&lt;/span&gt;&lt;span class="nn"&gt;[owner]&lt;/span&gt;
&lt;span class="py"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;John Doe&lt;/span&gt;
&lt;span class="py"&gt;organization&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;Acme Widgets Inc.&lt;/span&gt;

&lt;span class="nn"&gt;[database]&lt;/span&gt;
&lt;span class="c"&gt;; use IP address in case network name resolution is not working
&lt;/span&gt;&lt;span class="py"&gt;server&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;192.0.2.62     &lt;/span&gt;
&lt;span class="py"&gt;port&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;143&lt;/span&gt;
&lt;span class="py"&gt;file&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"payroll.dat"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;At the same time, another part of the configuration was supplied from &lt;a href="https://en.wikipedia.org/wiki/Environment_variable"&gt;the environment variables&lt;/a&gt; or &lt;a href="https://en.wikipedia.org/wiki/Command-line_interface"&gt;CLI parameters&lt;/a&gt; reflecting the idea of dynamic settings.  &lt;/p&gt;

&lt;p&gt;&lt;em&gt;Now we use &lt;a href="https://stackoverflow.com/questions/68267862/what-is-an-env-or-dotenv-file-exactly"&gt;dotenv-files&lt;/a&gt;, ironic :&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ini"&gt;&lt;code&gt;&lt;span class="c"&gt;# https://hexdocs.pm/dotenvy/0.2.0/dotenv-file-format.html
&lt;/span&gt;&lt;span class="py"&gt;S3_BUCKET&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;YOURS3BUCKET&lt;/span&gt;
&lt;span class="py"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;YOURSECRETKEYGOESHERE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Even then, the resolution logic began to penetrate into the app layer.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Just an illustration. This problem existed before JS was invented&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;config&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;logLevel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;DEBUG&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;trace&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;log.level&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dbConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Customer.dbConfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dbConfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...)&lt;/span&gt;

&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;optionalFeature.detail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detail&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;optionalFeature.detail&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;//...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;When centralized configuration management came, the settings has been moved partially to the remote storage. Local pre-config (entrypoints, db credentials) was used to get the rest. Configuration assembly itself has become multi-stage.&lt;/p&gt;

&lt;p&gt;Later, specialized systems such as &lt;a href="https://developer.hashicorp.com/vault/docs"&gt;vault&lt;/a&gt; made new additions: now env holds an access token and defines an entrypoint by running mode to make a POST request to reveal credentials profile to mix this data to the entire config.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Here's how &lt;a href="https://github.com/qiwi/uniconfig/blob/master/examples/vault.md"&gt;uniconfig&lt;/a&gt; obtains secrets from the vault storage:&lt;/em&gt;&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"secret"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$vault:data"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"vault"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GET"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$url:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"opts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"headers"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"X-Vault-Token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$token:auth.client_token"&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$pkg:name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"space"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"openapi"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$env:ENVIRONMENT_PROFILE_NAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"vaultHost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$env:VAULT_HOST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"vaultPort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$env:VAULT_PORT"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{=it.env==='production' ? 'https': 'http'}}://{{=it.vaultHost}}:{{=it.vaultPort}}/v1/secret/applications/{{=it.space}}/{{=it.name}}"&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"env"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"pkg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pkg"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datatree&amp;gt;dot"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"token"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"method"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"POST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$url:"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"opts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"role"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$pkg:name"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"jwt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$jwt:"&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"pkg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pkg"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"jwt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"tokenPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$env:TOKEN_FILE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"defaultTokenPath"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"/var/run/secrets/kubernetes.io/serviceaccount/token"&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{=it.tokenPath || it.defaultTokenPath}}"&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"env"&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datatree&amp;gt;dot&amp;gt;file"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$env:ENVIRONMENT_PROFILE_NAME"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"vaultHost"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$env:VAULT_HOST"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"vaultPort"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$env:VAULT_PORT"&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="nl"&gt;"template"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"{{=it.env==='production' ? 'https': 'http'}}://{{=it.vaultHost}}:{{=it.vaultPort}}/v1/auth/kubernetes/login"&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="nl"&gt;"env"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"env"&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="nl"&gt;"pkg"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
                        &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"pkg"&lt;/span&gt;&lt;span class="w"&gt;
                      &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
                  &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datatree&amp;gt;dot"&lt;/span&gt;&lt;span class="w"&gt;
                &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
              &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datatree&amp;gt;http&amp;gt;json"&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"pipeline"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"datatree&amp;gt;http&amp;gt;json"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Meanwhile, formats have been evolving (&lt;a href="https://json5.org/"&gt;JSON5&lt;/a&gt;, &lt;a href="https://yaml.org/"&gt;YAML&lt;/a&gt;), config entry points are constantly changing. These fluctuations, fortunately, were covered by tools like the &lt;a href="https://github.com/cosmiconfig/cosmiconfig"&gt;cosmiconfig&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;package.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.json`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.yaml`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.yml`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.js`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.ts`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.mjs`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.cjs`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.config/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.config/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.json`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.config/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.yaml`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.config/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.yml`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.config/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.js`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.config/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.ts`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`.config/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;rc.cjs`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.config.js`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.config.ts`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.config.mjs`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;moduleName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.config.cjs`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Configs are still trying to be declarative, but they can't. Templates appeared first.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;annotations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;cni.projectcalico.org/ipv4pools&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;["${APP_NAME}"]'&lt;/span&gt;
        &lt;span class="na"&gt;vault.hashicorp.com/agent-init-first&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
        &lt;span class="na"&gt;vault.hashicorp.com/agent-inject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;true"&lt;/span&gt;
        &lt;span class="na"&gt;vault.hashicorp.com/secrets-injection-method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;env"&lt;/span&gt;
        &lt;span class="na"&gt;vault.hashicorp.com/secrets-type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;static"&lt;/span&gt;
        &lt;span class="s"&gt;vault.hashicorp.com/agent-inject-secret-${APP_NAME}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="s"&gt;secret-v2/applications/${DEPLOYMENT_NAMESPACE}/${APP_NAME}&lt;/span&gt;
        &lt;span class="s"&gt;vault.hashicorp.com/agent-inject-template-${APP_NAME}&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;{{ with secret "secret-v2/applications/${DEPLOYMENT_NAMESPACE}/${APP_NAME}" }}&lt;/span&gt;
            &lt;span class="s"&gt;{{- range $secret_key, $secret_value := .Data.data }}&lt;/span&gt;
            &lt;span class="s"&gt;export {{ $secret_key }}={{ $secret_value }}&lt;/span&gt;
            &lt;span class="s"&gt;{{- end }}&lt;/span&gt;
          &lt;span class="s"&gt;{{ end }}&lt;/span&gt;
        &lt;span class="na"&gt;vault.hashicorp.com/auth-path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${AUTH_PATH}&lt;/span&gt;
        &lt;span class="na"&gt;vault.hashicorp.com/role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${APP_NAME}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then templates inside templates. With commands and scripts invocations inside dynamic DSL wrapped into matrices.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;yarn-cache&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ needs.init.outputs.yarn-cache-dir }}&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}&lt;/span&gt;
          &lt;span class="na"&gt;restore-keys&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
            &lt;span class="s"&gt;${{ runner.os }}-yarn-&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore artifact from cache (if exists)&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/cache@v3&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;artifact.tar&lt;/span&gt;
          &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;artifact-${{ needs.init.outputs.checksum }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Check artifact&lt;/span&gt;
        &lt;span class="na"&gt;if&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;always()&lt;/span&gt;
        &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;check-artifact&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;echo "::set-output name=exists::$([ -e "artifact.tar" ] &amp;amp;&amp;amp; echo &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="s"&gt; || echo &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="s"&gt;)"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;As we can see, syntax complexity increases as the cost of declarativeness. It's still unclear how this problem can be mitigated. Perhaps new specialized formats will appear or more strict forms (schemas) of using existing ones will be introduced.&lt;/p&gt;
&lt;h2&gt;
  
  
  Budget loss
&lt;/h2&gt;

&lt;p&gt;Anyway, &lt;code&gt;::$([&lt;/code&gt; is definitely not an &lt;em&gt;optimal&lt;/em&gt; solution. Сonfusing, fragile and overcomplicated for the most developers. For example, here is how Python Engineer was fighting against &lt;code&gt;kube.yaml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fix vault in kube yaml Jul 04   XS      
fix vault in kube yaml Jul 04   XS
fix vault in kube yaml Jul 04   XS
fix vault in kube yaml Jul 04   XS
fix vault in kube yaml Jul 04   XS
fix vault in kube yaml Jul 04   XS
fix vault in kube yaml Jul 04   XS
fix vault in kube yaml Jul 04   XS
fix vault in kube yaml Jul 03   XS
fix vault in kube yaml Jul 03   XS
fix vault in kube yaml Jul 03   XS
fix vault in kube yaml Jul 03   XS
fix vault in kube yaml Jul 03   XS
fix vault in kube yaml Jul 03   XS
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;This is definitely not &lt;em&gt;configuring&lt;/em&gt; but more &lt;em&gt;guessing&lt;/em&gt;. On a company scale, such exercises are a significant waste of resources. And this &lt;em&gt;experience&lt;/em&gt; is almost one-time only, which cannot be formalized and transmitted except by copy-paste. Every time we see the same thing, with a different number of attempts.&lt;/p&gt;
&lt;h2&gt;
  
  
  What we need
&lt;/h2&gt;

&lt;p&gt;The overcomplexity problem seems to have arisen from the fact that we combined resolving, processing and accessing data into one structure. Although the entire theory of programming / CS instructs us to do exactly the opposite. &lt;a href="https://en.wikipedia.org/wiki/Separation_of_concerns"&gt;Separation of concerns&lt;/a&gt;: imagine a config which explicitly divides value resolutions, compositions and operations.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;how to expose values&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;how to resolve values&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"cmds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;available cmds/ops/fns&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Let &lt;code&gt;data&lt;/code&gt; to represent how the result structure may be built if all the required transformations were made — like a &lt;em&gt;mapping&lt;/em&gt;.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$b.some.nested.prop.value.of.b"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"c"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$external.prop.of.prop"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Templating bases on regular substring replacements:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo %s"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"bar"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;                   &lt;span class="c1"&gt;// gives 'foobar'&lt;/span&gt;
&lt;span class="c1"&gt;// But positional contract is enhanced with named refmap&lt;/span&gt;
&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"foo $a $b $a"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s"&gt;"a"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"A"&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"b"&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"B"&lt;/span&gt;&lt;span class="o"&gt;})&lt;/span&gt; &lt;span class="c1"&gt;// returns 'foo A B A'&lt;/span&gt;
&lt;span class="c1"&gt;//            ↑ data chunks ↑ sources map&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Let &lt;code&gt;sources&lt;/code&gt; to describe how to obtain and process values for referencing in &lt;code&gt;data&lt;/code&gt; map. Like &lt;em&gt;reducing&lt;/em&gt; pipelines.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"a"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;pipeline 1&amp;gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"b"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;lt;pipeline 2&amp;gt;"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Let &lt;code&gt;pipeline&lt;/code&gt; to compose actions in natural &lt;del&gt;human&lt;/del&gt; dev-readable format like CLI:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cmd param &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cmd2 param param &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ... &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; cmd3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Let intermediate values be referenced by lateral (bubbling concept) or nested contexts.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "sources": {
    "a" : "cmd param",
    "b": "cmd $a" // b refers to a
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Apply &lt;a href="https://en.wikipedia.org/wiki/Directed_acyclic_graph"&gt;DAG&lt;/a&gt; walker for consistency checks and processing.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Working draft
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/antongolub/misc/tree/master/packages/topoconfig"&gt;https://github.com/antongolub/misc/tree/master/packages/topoconfig&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Install
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add topoconfig@draft
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  API
&lt;/h3&gt;


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

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;topoconfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="c1"&gt;// define functions to use in pipelines: sync or async&lt;/span&gt;
  &lt;span class="na"&gt;cmds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;bar&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;baz&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;v&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;v&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;qux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// pipelines to resolve intermediate variables&lt;/span&gt;
  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;foo &amp;gt; baz&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// pipeline returns 'barqux'&lt;/span&gt;
    &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;            &lt;span class="c1"&gt;// b refers to b.data&lt;/span&gt;
      &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;d&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;e&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="c1"&gt;// output value&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// $name.inner.path populates var ref with its value&lt;/span&gt;
    &lt;span class="na"&gt;x&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$b.c.d&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="c1"&gt;// 'e'&lt;/span&gt;
    &lt;span class="na"&gt;y&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;z&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;     &lt;span class="c1"&gt;// 'barqux'&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Real-world usage example may look like:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;topoconfig&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;topoconfig&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;topoconfig&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://some.url&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;param&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;regular param value&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;num&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="mi"&gt;123&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s1"&gt;$to.prevent.value.inject.use.&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;&lt;span class="s1"&gt;.prefix&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$b.some.nested.prop.value.of.b&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;$external.prop.of.prop&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="s2"&gt;`$loglevel`&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;a&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file ./file.json utf8&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;b&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;json $a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;c&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;        &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;get $b &amp;gt; assert type number&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;cwd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file $cwd/schema.json utf8 &amp;gt; json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fetch http://foo.example.com &amp;gt; get .body &amp;gt; json &amp;gt; get .prop &amp;gt; ajv $schema&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;extended&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;extend $b $external&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;loglevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;find $env.LOG_LEVEL $argv.log-level $argv.log.level info&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`dot {{? $name }}
&amp;lt;div&amp;gt;Oh, I love your name, {{=$name}}!&amp;lt;/div&amp;gt;
{{?? $age === 0}}
&amp;lt;div&amp;gt;Guess nobody named you yet!&amp;lt;/div&amp;gt;
{{??}}
You are {{=$age}} and still don't have a name?
{{?}} &amp;gt; assert $foo`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;cmds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// http://olado.github.io/doT/index.html&lt;/span&gt;
    &lt;span class="na"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;dot&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;template&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;chunks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt; &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;))({}),&lt;/span&gt;
    &lt;span class="na"&gt;extend&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;   &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;assign&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="na"&gt;file&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;readFile&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;file&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="nx"&gt;lodash&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;minimist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;argv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;      &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;find&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;args&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="na"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;    &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;fromEntries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;body&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;code&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;//...&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Notes
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;TData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="nx"&gt;TData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TCmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TCmds&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;symbol&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TCmd&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TConfigDeclaration&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;TConfigDeclaration&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nx"&gt;cmds&lt;/span&gt;&lt;span class="p"&gt;?:&lt;/span&gt; &lt;span class="nx"&gt;TCmds&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;&lt;code&gt;TConfigDeclaration&lt;/code&gt; defines two sections: &lt;code&gt;data&lt;/code&gt; and &lt;code&gt;sources&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;data&lt;/code&gt; describes how to build the result value based on the bound sources: it populates &lt;code&gt;$&lt;/code&gt;-prefixed refs with their values in every place.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sources&lt;/code&gt; is a map, which declares the &lt;em&gt;algorithm&lt;/em&gt; to resolve intermediate values through &lt;code&gt;cmd&lt;/code&gt; calls composition. To fetch data from remote, to read from file, to convert, etc.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"data"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"$res"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sources"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"res"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"fetch https://example.com &amp;gt; get .body &amp;gt; json"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;cmd&lt;/code&gt; is a provider that performs a specific action.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;TCmd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(...&lt;/span&gt;&lt;span class="nx"&gt;opts&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;[])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;directive&lt;/code&gt; is a template for defining a value transformation pipeline
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// fetch http://foo.example.com &amp;gt; get body &amp;gt; json &amp;gt; get .prop
// ↑ cmd ↑opts                  ↑ pipes delimiter
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Next steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Add ternaries: &lt;code&gt;cmd ? cmd1 &amp;gt; cmd2 ... : cmd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Handle &lt;em&gt;or&lt;/em&gt; statement: &lt;code&gt;cmd &amp;gt; cmd || cmd &amp;gt; cmd&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Expose commands presets: &lt;code&gt;import {cmds} from 'topoconfig/cmds'&lt;/code&gt; or &lt;code&gt;@topoconfig/cmds&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Provide lazy-loading for cmds:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cmds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;some/package&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;bar&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./local/plugin.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Support a pipeline factory as cmd declaration.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;cmds&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;readjson&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;path $0 resolve &amp;gt; file $1 &amp;gt; json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Use vars as cmd refs:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;glob ./*.*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="na"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;detect $files&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="na"&gt;foo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;file $files.0 &amp;gt; $reader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Bring smth like watchers to trigger graph re-resolution from the specified vertex&lt;/li&gt;
&lt;/ul&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/antongolub"&gt;
        antongolub
      &lt;/a&gt; / &lt;a href="https://github.com/antongolub/misc"&gt;
        misc
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Experiment on maintaining a multi-project monorepository
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;h2&gt;
&amp;lt;misc&amp;gt;&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;An experiment on maintaining a multi-project monorepository&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;&lt;a href="https://codeclimate.com/github/antongolub/misc/maintainability" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/bb10cb97b7afcca84e5ed32f2d219f822795f5fc0e638e71866f0e9eff14e462/68747470733a2f2f6170692e636f6465636c696d6174652e636f6d2f76312f6261646765732f31653730313038623332373334373034313563372f6d61696e7461696e6162696c697479" alt="Maintainability"&gt;&lt;/a&gt;
&lt;a href="https://codeclimate.com/github/antongolub/misc/test_coverage" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/ff8928cb1e96794c44da17440c3c7937b2a418deb1657d1c68bdbd2f3508379c/68747470733a2f2f6170692e636f6465636c696d6174652e636f6d2f76312f6261646765732f31653730313038623332373334373034313563372f746573745f636f766572616765" alt="Test Coverage"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
Statuses&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Blueprint/B&lt;/code&gt; marks the project as an idea w/o any implementation provided. Just a contract proposal.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;PoC/C&lt;/code&gt; — proof of concept that shows the declared behavior in action.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Working draft/W&lt;/code&gt; — the project work is in progress. Some known corner cases are not covered, but it's already mostly usable.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Production ready/R&lt;/code&gt; — the implementation is stable, documented, tested and ready for use.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Deprecated/D&lt;/code&gt; — the project is no longer maintained.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
Contents&lt;/h2&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Package&lt;/th&gt;
&lt;th&gt;Description&lt;/th&gt;
&lt;th&gt;Latest&lt;/th&gt;
&lt;th&gt;Status&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/blank"&gt;@antongolub/blank&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Blank TS project&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/infra"&gt;@antongolub/infra&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Repo infra assets&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/topoconfig/cmds"&gt;@topoconfig/cmds&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Topoconfig basic cmds preset&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@topoconfig/cmds" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/1c7b235630d31e1b3985d8ac38840dc73151dc816013ad062a91b7882bd8239a/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f40746f706f636f6e6669672f636d64732f6c61746573742e7376673f6c6162656c3d26636f6c6f723d7768697465" alt="npm (scoped)"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/topoconfig/extends"&gt;@topoconfig/extends&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Flexible config extender&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/@topoconfig/extends" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/5d8b9604ce8df04a38586044e6e231751836c973859c671f207e2493505cfff1/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f40746f706f636f6e6669672f657874656e64732f6c61746573742e7376673f6c6162656c3d26636f6c6f723d7768697465" alt="npm (scoped)"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/dep/depseek"&gt;depseek&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Seeks for dependency references in JS/TS code&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/depseek" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/54a7a969d98a8109506d86e48bf4e43a967fe0ec556fb3a6036306e0dff110ac/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f6465707365656b2f6c61746573742e7376673f6c6162656c3d26636f6c6f723d7768697465" alt="npm (scoped)"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/dep/depshot"&gt;depshot&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Gathers deps snapshot by analyzing sources&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/depshot" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/3709868dccca07366b6e10033b52f4a7272a76b46107b3ecb644ba9f9343bf0f/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f64657073686f742f6c61746573742e7376673f6c6162656c3d26636f6c6f723d7768697465" alt="npm (scoped)"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;B&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/env/envader"&gt;envader&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Occupies env vars for data storage&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/envader" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/56dd740f1d91dc86b74ad6131c2200e7d2d5037fa997d4e3b72b7ca8ae77f620/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f656e76616465722f6c61746573742e7376673f6c6162656c3d26636f6c6f723d7768697465" alt="npm (scoped)"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/env/envimist"&gt;envimist&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Applies minimist to process.env&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/envimist" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/88c5f621a628846896d821fafe9e4d429f55d1dd65bedd2f6abf4941ef113fea/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f656e76696d6973742f6c61746573742e7376673f6c6162656c3d26636f6c6f723d7768697465" alt="npm (scoped)"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;W&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/esbuild/c"&gt;esbuild-c&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Empowers esbuild with config processing&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/esbuild-c" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/05c6539b0e855eea232a139e7fdae4bcca71ee2fe26184ad04df48a0062e1608/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f65736275696c642d632f6c61746573742e7376673f6c6162656c3d26636f6c6f723d7768697465" alt="npm (scoped)"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/esbuild/plugin-entry-chunks"&gt;esbuild-plugin-entry-chunks&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;Esbuild plugin to compose entryPoints as chunks&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.npmjs.com/package/esbuild-plugin-entry-chunks" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/27ed0df6c9b7443b4274705451fb8c719e2bcb651f81e0c0d63cead657b74e9d/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f762f65736275696c642d706c7567696e2d656e7472792d6368756e6b732f6c61746573742e7376673f6c6162656c3d26636f6c6f723d7768697465" alt="npm (scoped)"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;C&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a href="https://github.com/antongolub/misc./packages/lcov-utils"&gt;lcov-utils&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;…&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/antongolub/misc"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  Refs and Inspirations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/cosmiconfig/cosmiconfig"&gt;cosmiconfig&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/node-config/node-config"&gt;node-config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/qiwi/uniconfig"&gt;uniconfig&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/semrel-extra/toposource"&gt;toposource&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/symfony/config"&gt;symfony/config&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/gookit/config"&gt;gookit/config&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
      <category>config</category>
      <category>programming</category>
      <category>node</category>
    </item>
    <item>
      <title>NestJS + esbuild workarounds</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Tue, 13 Sep 2022 20:31:07 +0000</pubDate>
      <link>https://dev.to/antongolub/nestjs-esbuild-workarounds-99i</link>
      <guid>https://dev.to/antongolub/nestjs-esbuild-workarounds-99i</guid>
      <description>&lt;p&gt;Q: Do we need ESM for backend apps?&lt;br&gt;
A: Yes. More and more pkgs are moving to the ESM format, this is inevitable.&lt;/p&gt;

&lt;p&gt;Q: Do we need bundles?&lt;br&gt;
A: Probably, yes. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If your Nestjs app is distributed as a container with &lt;code&gt;node_modules&lt;/code&gt; inside, bundling can greatly reduce its size.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;du -hs ./node_modules
495M   ./node_modules
du -hs ./output.js   
10M    ./output.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Bundle is the best point for a thorough ISEC audit: to search for &lt;a href="https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS"&gt;redos&lt;/a&gt;, suspicious API usages, protestware, etc.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The fewer fs I/O, the faster the application starts.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;(TS) bundle absorbs the all required contents, so there is no &lt;a href="https://github.com/antongolub/tsc-esm-fix"&gt;tsc-esm transpilation issues&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Q: esbuild, swc, rollup, parcel or babel?&lt;br&gt;
A: esbuild.&lt;/p&gt;
&lt;h2&gt;
  
  
  Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/nestjs/nest"&gt;Nestjs 9.x&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/evanw/esbuild"&gt;esbuild 0.15.x&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  :-(
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node18&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es2021&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Problems
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/nestjs/nest-cli/issues/1157"&gt;https://github.com/nestjs/nest-cli/issues/1157&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/nestjs/swagger/issues/1450"&gt;https://github.com/nestjs/swagger/issues/1450&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/evanw/esbuild/pull/509"&gt;https://github.com/evanw/esbuild/pull/509&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/evanw/esbuild/issues/566"&gt;https://github.com/evanw/esbuild/issues/566&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;0. Some Nestjs internals are lazy-loaded, so sometimes they may be omitted, but in another cases they should be bundled. How to handle? &lt;a href="https://esbuild.github.io/api/#external"&gt;https://esbuild.github.io/api/#external&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mqtt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amqplib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class-transformer/storage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;1. &lt;code&gt;openapi&lt;/code&gt; is not defined. &lt;a href="https://github.com/nestjs/swagger/issues/1450"&gt;https://github.com/nestjs/swagger/issues/1450&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;__decorate&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nx"&gt;Post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;event-unsafe-batch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;HttpCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;openapi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ApiResponse&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nx"&gt;__param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Body&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="nx"&gt;__param&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Req&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
  &lt;span class="nx"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design:type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nx"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design:paramtypes&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="nx"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design:returntype&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;EventUnsafeController&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;prototype&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;logEventBatch&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2. openapi / class-validator DTOs are referenced by &lt;code&gt;require&lt;/code&gt; API. &lt;a href="https://github.com/microsoft/TypeScript/issues/43329"&gt;https://github.com/microsoft/TypeScript/issues/43329&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nx"&gt;CspReportDto&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;_OPENAPI_METADATA_FACTORY&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;csp-report&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./csp.dto.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;CspReport&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;3. NodeJS builtins are referenced via &lt;code&gt;require&lt;/code&gt; API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;require_async4&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;__commonJS&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node_modules/resolve/lib/async.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module2&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;fs2&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;fs&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;4. esbuild-compiled ESM bundle cannot refer to &lt;code&gt;views/redoc.handlebars&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;redocFilePath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path_1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__dirname&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;..&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;views&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;redoc.handlebars&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;5. &lt;code&gt;_OPENAPI_METADATA_FACTORY&lt;/code&gt; class fields may be empty, so the swagger declaration cannot be properly rendered.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// →&lt;/span&gt;
&lt;span class="kd"&gt;var&lt;/span&gt; &lt;span class="nx"&gt;Meta&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nx"&gt;_OPENAPI_METADATA_FACTORY&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;appName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;appHost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;appVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;appNamespace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_a3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;_a3&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;deviceInfo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_b3&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;Record&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;_b3&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;userAgent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;  &lt;span class="nb"&gt;String&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;envProfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;required&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;  &lt;span class="na"&gt;enum&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;  &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;import_substrate2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EnvironmentProfile&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;import_substrate2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;EnvironmentProfile&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;_c&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;6. Extra type wrappers cannot be processed by openapi / &lt;code&gt;class-validator&lt;/code&gt; / &lt;code&gt;class-transformer&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design:type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_d&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;_d&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design:type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_e&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;import_substrate2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LogLevel&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;undefined&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;import_substrate2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;function&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;_e&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="c1"&gt;// →&lt;/span&gt;
  &lt;span class="nx"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design:type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="nx"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;design:type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;import_substrate2&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;LogLevel&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How to fix?
&lt;/h2&gt;

&lt;p&gt;The right solution is certainly to improve the tools: create issues, discuss, suggest PRs.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to fix it right here and right now?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/anatine/esbuildnx"&gt;@anatine/esbuild-decorators&lt;/a&gt; + &lt;a href="https://github.com/antongolub/nestjs-esm-fix"&gt;old good monkey patching&lt;/a&gt;.&lt;br&gt;
Fragile. Wrong. Terrible.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// build.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esbuild&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node:path&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;esbuildDecorators&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@anatine/esbuild-decorators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;cwd&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;outfile&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;output.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tsconfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tsconfig.json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entryPoints&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;src/main/ts/index.ts&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;config&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;  &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;node18&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;es2021&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;esm&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;bundle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;keepNames&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nx"&gt;esbuildDecorators&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="nx"&gt;tsconfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="nx"&gt;cwd&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="nx"&gt;tsconfig&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;entryPoints&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;outfile&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;external&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;kafkajs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;mqtt&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amqplib&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;amqp-connection-manager&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;nats&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@grpc/grpc-js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@grpc/proto-loader&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@nestjs/websockets/socket-module&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;class-transformer/storage&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node build.js &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; nestjs-esm-fix &lt;span class="nt"&gt;--target&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;./output.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But it works.&lt;/p&gt;

</description>
      <category>nestjs</category>
      <category>esbuild</category>
      <category>javascript</category>
      <category>node</category>
    </item>
    <item>
      <title>npm-registry-firewall</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Wed, 13 Apr 2022 10:44:15 +0000</pubDate>
      <link>https://dev.to/antongolub/npm-registry-firewall-12ai</link>
      <guid>https://dev.to/antongolub/npm-registry-firewall-12ai</guid>
      <description>&lt;p&gt;I couldn't google it, so I wrote my own: &lt;br&gt;
&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--A9-wwsHG--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/antongolub"&gt;
        antongolub
      &lt;/a&gt; / &lt;a href="https://github.com/antongolub/npm-registry-firewall"&gt;
        npm-registry-firewall
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      npm registry proxy with on-the-fly filtering
    &lt;/h3&gt;
  &lt;/div&gt;
&lt;/div&gt;


&lt;h2&gt;
  
  
  Motivation
&lt;/h2&gt;

&lt;p&gt;To mitigate security and legal risks.&lt;/p&gt;

&lt;p&gt;Open Source is essential for modern software development. &lt;a href="https://www.perforce.com/blog/vcs/using-open-source-code-in-proprietary-software"&gt;According to various estimates&lt;/a&gt;, at least 60% of the resulting codebase is composed of open repositories, libraries and packages. And keeps growing. &lt;a href="https://www.synopsys.com/content/dam/synopsys/sig-assets/reports/rep-ossra-2021.pdf"&gt;Synopsys OSSRA 2021 report&lt;/a&gt; found that 98% of applications have open source components.&lt;/p&gt;

&lt;p&gt;But &lt;em&gt;open&lt;/em&gt; does not mean &lt;em&gt;free&lt;/em&gt;. The price is the risk that you take:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Availability&lt;/li&gt;
&lt;li&gt;Security &lt;/li&gt;
&lt;li&gt;Legality / license&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's consider these problems in the context of the JS universe.&lt;/p&gt;

&lt;h3&gt;
  
  
  Availability risks
&lt;/h3&gt;

&lt;p&gt;JS packages are distributed in various ways: git repos, cdns and package registries. Regardless of the method, there are only two entry types that are finally resolved by any pkg manager: git-commit pointers and tarball links.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"yaf"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git://github.com/antongolub/yarn-audit-fix.git#commit-hash"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"yaf2"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"antongolub/yarn-audit-fix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"yarn-audit-fix"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"*"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;yaf2@antongolub/yarn-audit-fix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;version "9.2.1"&lt;/span&gt;
  &lt;span class="s"&gt;resolved "https://codeload.github.com/antongolub/yarn-audit-fix/tar.gz/706646bab3b4c7209596080127d90eab9a966be2"&lt;/span&gt;
  &lt;span class="s"&gt;dependencies&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@types/find-cache-dir"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^3.2.1"&lt;/span&gt;
    &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@types/fs-extra"&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;^9.0.13"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"node_modules/yaf"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"yarn-audit-fix"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9.2.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"git+ssh://git@github.com/antongolub/yarn-audit-fix.git#706646bab3b4c7209596080127d90eab9a966be2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"license"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"MIT"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"node_modules/yarn-audit-fix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"9.2.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"resolved"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://registry.npmjs.org/yarn-audit-fix/-/yarn-audit-fix-9.2.1.tgz"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"integrity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sha512-4biFNP4ZLOHboB2cNVuhYyelTFR/twlfmGMQ2TgJgGRORMDM/rQdQqhJdVLuKvfdMLFEPJ832z6Ws5OoCnFcfA=="&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"dependencies"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So the implementation of mirroring is fundamentally quite simple: we just need to save and expose these assets from an alternative ssh/https entry point. Luckily this has already happened. The main repository for JS code is &lt;a href="https://registry.npmjs.org/"&gt;registry.npmjs.org&lt;/a&gt;. And at least 5 public replicas are always available as alternatives:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://registry.yarnpkg.com/"&gt;https://registry.yarnpkg.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.npmmirror.com"&gt;https://registry.npmmirror.com&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://r.cnpmjs.org/"&gt;https://r.cnpmjs.org&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://skimdb.npmjs.com/registry/"&gt;https://skimdb.npmjs.com/registry&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://registry.npm.taobao.org/"&gt;https://registry.npm.taobao.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If this reliability level is not enough, you can easily run one more registry:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://help.sonatype.com/repomanager3/nexus-repository-administration/formats/npm-registry"&gt;sonatype-nexus&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://verdaccio.org/"&gt;verdaccio.org&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Security risks
&lt;/h3&gt;

&lt;p&gt;Any code may not work properly. Due to error or malice. Keep in mind that most OSS licenses &lt;strong&gt;exclude any liability for damages&lt;/strong&gt;. It's also important to always remember that oss code is &lt;strong&gt;not verified&lt;/strong&gt; before being published.&lt;br&gt;
These two circumstances sometimes give rise to dangerous incidents like &lt;a href="https://security.snyk.io/vuln/SNYK-JS-COLORS-2331906"&gt;colors.js&lt;/a&gt; or &lt;a href="https://snyk.io/blog/peacenotwar-malicious-npm-node-ipc-package-vulnerability/"&gt;node-ipc&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;The independent audit process is expensive, time consuming, so only setting a delay before using new pkg version might be effective countermeasure.&lt;/p&gt;
&lt;h3&gt;
  
  
  Legal risks
&lt;/h3&gt;

&lt;p&gt;License agreement is an attribute of the moment: it can suddenly change and affect the development process (for example, &lt;a href="https://blog.typicode.com/husky-5/"&gt;husky-5&lt;/a&gt;). Uncontrolled use of new versions may have legal and financial consequences. Therefore, automated license checks should be part of CI/CD pipeline or the registry's own feature.&lt;/p&gt;
&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Restricts access to remote packages by predicate: &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;org&lt;/code&gt;, &lt;code&gt;version&lt;/code&gt; (&lt;a href="https://github.com/npm/node-semver#ranges"&gt;semver range&lt;/a&gt;), &lt;code&gt;license&lt;/code&gt;, &lt;code&gt;dateRange&lt;/code&gt;, &lt;code&gt;username&lt;/code&gt;, &lt;code&gt;age&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Multi-configuration: define as many &lt;code&gt;port/context-path/rules&lt;/code&gt; combinations as you need.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://expressjs.com/en/guide/using-middleware.html"&gt;Expressjs&lt;/a&gt;-inspired server implementation.&lt;/li&gt;
&lt;li&gt;Has no deps. Literally zero.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Requirements
&lt;/h2&gt;

&lt;p&gt;Node.js &amp;gt;= 14&lt;/p&gt;
&lt;h2&gt;
  
  
  Install
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# npm&lt;/span&gt;
npm i npm-registry-firewall

&lt;span class="c"&gt;# yarn&lt;/span&gt;
yarn add npm-registry-firewall
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Usage
&lt;/h2&gt;
&lt;h3&gt;
  
  
  CLI
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm-registry-firewall /path/to/config.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  JS API
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;npm-registry-firewall&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;server&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;port&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;3001&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;firewall&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;registry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://registry.npmmirror.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;allow&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;org&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@qiwi&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;policy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;deny&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@babel/*&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Config
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  "server": {
    "host": "localhost",        // Defaults to 127.0.0.1
    "port": 3000,               // 8080 by default
    "secure": {                 // Optional. If declared serves via https
      "cert": "ssl/cert.pem",
      "key": "ssl/key.pem"
    },
    "base": "/",                // Optional. Defaults to '/'
    "healthcheck": "/health",   // Optional. Defaults to '/healthcheck'. Pass null to disable
    "keepAliveTimeout": 15000,  // Optional. Defaults 61000
    "headersTimeout": 20000,    // Optional. Defaults 62000
    "requestTimeout": 10000     // Optional. Defaults 30000
  },
  "firewall": {
    "registry": "https://registry.npmmirror.com",  // Remote registry
    "entrypoint": "https://r.qiwi.com/npm",        // Optional. Defaults to `${server.secure ? 'https' : 'http'}://${server.host}:${server.port}${route.base}`
    "base": "/",                // Optional. Defaults to '/'
    "rules": [
      {
        "policy": "allow",
        "org": "@qiwi"
      },
      {
        "policy": "allow",
        "name": ["@babel/*", "@jest/*", "lodash"] // string[] or "comma,separated,list". * works as .+ in regexp
      },
      {
        "policy": "deny",
        "name": "colors",
        "version": "&amp;gt;= v1.4.0"  // Any semver range: https://github.com/npm/node-semver#ranges
      },
      {
        "policy": "deny",
        "license": "dbad"       // Comma-separated license types or string[]
      },
      {
        "policy": "allow",
        "username": ["sindresorhus", "isaacs"] // Trusted npm authors.
      },
      {
        "policy": "allow",
        "name": "d",
        // `allow` is upper, so it protects `&amp;lt; 1.0.0`-ranged versions that might be omitted on next steps
        "version": "&amp;lt; 1.0.0"
      },
      {
        "policy": "deny",
        // Checks pkg version publish date against the range
        "dateRange": ["2010-01-01T00:00:00.000Z", "2025-01-01T00:00:00.000Z"]
      },
      {
        "policy": "allow",
        "age": 5    // Check the package version is older than 5 days. Like quarantine
      }
    ]
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  Multi-config
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Array at the top level
[
  // Two servers (for example, http and https) share the same preset
  {
    "server": [
      {"port": 3001},
      {"port": 3002},
    ],
    "firewall": {
      "registry": "https://registry.yarnpkg.com",
      "rules": {"policy": "deny", "org": "@qiwi"}
    }
  },
  // One server has a pair of separately configured endpoints
  {
    "server": {"port": 3003},
    "firewall": [
      {"base": "/foo", "registry": "https://registry.npmjs.org", "rules": {"policy": "deny", "org": "@qiwi"}},
      {"base": "/bar", "registry": "https://registry.yarnpkg.com", "rules": {"policy": "deny", "org": "@babel"}}
    ]
  }
]

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

&lt;/div&gt;


&lt;p&gt;&lt;strong&gt;.npmrc&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;registry=https://localhost:3000&lt;/span&gt;
&lt;span class="s"&gt;strict-ssl=false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;npm view&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm-registry-firewall % npm view d versions                          
&lt;span class="o"&gt;[&lt;/span&gt; &lt;span class="s1"&gt;'0.1.0'&lt;/span&gt;, &lt;span class="s1"&gt;'0.1.1'&lt;/span&gt; &lt;span class="o"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;output&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;node src/main/js/cli.js config.json
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"INFO"&lt;/span&gt;,&lt;span class="s2"&gt;"timestamp"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-04-11T20:56:47.031Z"&lt;/span&gt;,&lt;span class="s2"&gt;"message"&lt;/span&gt;:&lt;span class="s2"&gt;"npm-registry-firewall is ready for connections: https://localhost:3000"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"INFO"&lt;/span&gt;,&lt;span class="s2"&gt;"timestamp"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-04-11T20:56:49.568Z"&lt;/span&gt;,&lt;span class="s2"&gt;"traceId"&lt;/span&gt;:&lt;span class="s2"&gt;"44f21c050d8c6"&lt;/span&gt;,&lt;span class="s2"&gt;"clientIp"&lt;/span&gt;:&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;,&lt;span class="s2"&gt;"message"&lt;/span&gt;:&lt;span class="s2"&gt;"GET /d"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;{&lt;/span&gt;&lt;span class="s2"&gt;"level"&lt;/span&gt;:&lt;span class="s2"&gt;"INFO"&lt;/span&gt;,&lt;span class="s2"&gt;"timestamp"&lt;/span&gt;:&lt;span class="s2"&gt;"2022-04-11T20:56:50.015Z"&lt;/span&gt;,&lt;span class="s2"&gt;"traceId"&lt;/span&gt;:&lt;span class="s2"&gt;"44f21c050d8c6"&lt;/span&gt;,&lt;span class="s2"&gt;"clientIp"&lt;/span&gt;:&lt;span class="s2"&gt;"127.0.0.1"&lt;/span&gt;,&lt;span class="s2"&gt;"message"&lt;/span&gt;:&lt;span class="s2"&gt;"HTTP 200 446ms"&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Contributing
&lt;/h2&gt;

&lt;p&gt;Feel free to open any issues: bug reports, feature requests or questions.&lt;br&gt;
You're always welcome to suggest a PR. Just fork this repo, write some code, put some tests and push your changes.&lt;br&gt;
Any feedback is appreciated.&lt;/p&gt;

&lt;h2&gt;
  
  
  License
&lt;/h2&gt;

&lt;p&gt;MIT&lt;/p&gt;

</description>
      <category>npm</category>
      <category>javascript</category>
      <category>security</category>
      <category>node</category>
    </item>
    <item>
      <title>The missing `yarn audit fix` for Yarn 2+ Berry</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Sun, 12 Dec 2021 16:12:44 +0000</pubDate>
      <link>https://dev.to/antongolub/the-missing-yarn-audit-fix-for-yarn-2-berry-1p8</link>
      <guid>https://dev.to/antongolub/the-missing-yarn-audit-fix-for-yarn-2-berry-1p8</guid>
      <description>&lt;p&gt;As everybody knows, &lt;code&gt;audit --fix&lt;/code&gt; feature is out of scope of Yarn 1 Classic &lt;a href="https://github.com/yarnpkg/yarn/issues/7075"&gt;yarn/7075&lt;/a&gt; and it also has not been implemented yet (2021-12-12) for Yarn 2+ &lt;a href="https://github.com/yarnpkg/berry/issues/3582"&gt;berry/3582&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Let's just fix it.&lt;/p&gt;

&lt;h2&gt;
  
  
  tldr
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;npm_config_yes=true npx yarn-audit-fix@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  1.
&lt;/h2&gt;

&lt;p&gt;First of all, we need a lib to read/write yarnlock v2 files. &lt;a href="https://www.npmjs.com/package/@yarnpkg/lockfile"&gt;@yarnpkg/lockfile&lt;/a&gt; seems the best choice, but it works with v1 only. Maybe &lt;a href="https://github.com/snyk/nodejs-lockfile-parser/"&gt;nodejs-lockfile-parser&lt;/a&gt;?&lt;br&gt;
Missed the mark again. It &lt;a href="https://github.com/snyk/nodejs-lockfile-parser/issues/98"&gt;swaps checksums&lt;/a&gt; and does not provide &lt;code&gt;dump/format&lt;/code&gt; API what is expected for the parser :). It turns out that we are missing yet another one lockfile processor. No problem. If we look closely, the new shiny &lt;code&gt;yarn.lock&lt;/code&gt; v2 is a regular &lt;code&gt;yaml&lt;/code&gt; with a little strange formatting like extra empty line delimiters, extra quotes, and so on.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;yaml&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;js-yaml&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;TLockfileObject&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;__metadata&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;format&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lockfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TLockfileObject&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keymap&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;entries&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lockfile&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;resolution&lt;/span&gt; &lt;span class="p"&gt;}])&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
      &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;k&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lockfile&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Record&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;keymap&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolution&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nx"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;__metadata&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;cacheKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`# This file is generated by running "yarn install" inside your project.
# Manual changes might be lost - proceed with caution!

&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;yaml&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dump&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;quotingType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;flowLevel&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;lineWidth&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\n([^\s&lt;/span&gt;&lt;span class="sr"&gt;"&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s1"&gt;"$1":&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\n(\S)&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n\n&lt;/span&gt;&lt;span class="s1"&gt;$1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/resolution: &lt;/span&gt;&lt;span class="se"&gt;([^\n&lt;/span&gt;&lt;span class="sr"&gt;"&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;/g&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;resolution: "$1"&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2.
&lt;/h2&gt;

&lt;p&gt;We need to fetch audit data. Yarn Berry integrated audit API is much better than previous Classic, which returned the report &lt;a href="https://github.com/antongolub/yarn-audit-fix/blob/4af5c435122f82dcac874e44d693df65cca88b33/src/test/fixtures/lockfile/v1/yarn-audit-report.json#L1"&gt;in the form of chunks&lt;/a&gt;.&lt;br&gt;
&lt;code&gt;yarn npm audit --all --recursive --json&lt;/code&gt; gives exactly what we need:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"actions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[],&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"advisories"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"1004946"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"findings"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"version"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4.1.0"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="nl"&gt;"paths"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"ts-patch&amp;gt;strip-ansi&amp;gt;ansi-regex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"lerna&amp;gt;npmlog&amp;gt;gauge&amp;gt;ansi-regex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"lerna&amp;gt;@lerna/bootstrap&amp;gt;npmlog&amp;gt;gauge&amp;gt;ansi-regex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="err"&gt;...&lt;/span&gt;&lt;span class="w"&gt;
          &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"metadata"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"vulnerable_versions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;2.1.1 &amp;lt;5.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"module_name"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ansi-regex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"severity"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"moderate"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"github_advisory_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"GHSA-93q8-gq69-wqmw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cves"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"CVE-2021-3807"&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"access"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"public"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"patched_versions"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&amp;gt;=5.0.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"updated"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-09-23T15:45:50.000Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"recommendation"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Upgrade to version 5.0.1 or later"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"cwe"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"CWE-918"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"found_by"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"deleted"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;1004946&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"references"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"- https://nvd.nist.gov/vuln/detail/CVE-2021-3807&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- https://github.com/chalk/ansi-regex/commit/8d1d7cdb586269882c4bdc1b7325d0c58c8f76f9&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- https://huntr.dev/bounties/5b3cf33b-ede0-4398-9974-800876dfd994&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- https://github.com/chalk/ansi-regex/issues/38#issuecomment-924086311&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- https://app.snyk.io/vuln/SNYK-JS-ANSIREGEX-1583908&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- https://github.com/chalk/ansi-regex/issues/38#issuecomment-925924774&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s2"&gt;- https://github.com/advisories/GHSA-93q8-gq69-wqmw"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"created"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2021-11-18T16:00:48.472Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"reported_by"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"title"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;" Inefficient Regular Expression Complexity in chalk/ansi-regex"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"npm_advisory_id"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"overview"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"ansi-regex is vulnerable to Inefficient Regular Expression Complexity"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
      &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://github.com/advisories/GHSA-93q8-gq69-wqmw"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We take only significant fields: &lt;code&gt;vulnerable_versions, module_name, patched_versions&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;parseAuditReport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;TAuditReport&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;advisories&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;reduce&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;TAuditReport&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;vulnerable_versions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;patched_versions&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="kr"&gt;any&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;m&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;patched_versions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;vulnerable_versions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;m&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{},&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  3.
&lt;/h2&gt;

&lt;p&gt;Almost done. Now we need to replace vulnerable packages versions in lockfile with the advisories, taking into account semver-compatibility and to remove the prev &lt;code&gt;checksum&lt;/code&gt; fields. This brilliant idea was &lt;a href="https://github.com/hfour/yarn-audit-fix-ng/blob/a2fa7f9c8a110ee8a7eafa64c8c3b5c255b4dec9/src/index.ts#L98"&gt;suggested by G. Kosev&lt;/a&gt;. &lt;strong&gt;Important note&lt;/strong&gt; Seems like &lt;code&gt;dependencies&lt;/code&gt; of the patched entries are not reloaded automatically by Yarn, so they need to be requested from the registry by hand: &lt;code&gt;yarn npm info react --fields dependencies --json&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;patchEntry&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TLockfileEntry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;newVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;npmBin&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;TLockfileEntry&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newVersion&lt;/span&gt;
  &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;resolution&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@npm:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;

  &lt;span class="c1"&gt;// NOTE seems like deps are not updated by `yarn mode='--update-lockfile'`, only checksums&lt;/span&gt;
  &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dependencies&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;invoke&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;npmBin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;view&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dependencies&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;--json&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cwd&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;null&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;

  &lt;span class="k"&gt;delete&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checksum&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;entry&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_patch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;lockfile&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TLockfileObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TAuditReport&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bins&lt;/span&gt; &lt;span class="p"&gt;}:&lt;/span&gt; &lt;span class="nx"&gt;TContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;lockfileType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;TLockfileType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;TLockfileObject&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;silent&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Audit check found no issues&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;lockfile&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="na"&gt;upgraded&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

  &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;depSpec&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lockfile&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// @babel/code-frame@^7.0.0&lt;/span&gt;
    &lt;span class="c1"&gt;// @babel/code-frame@npm:^7.0.0&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="nx"&gt;pkgName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;desiredRange&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
      &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;?[^&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;@&lt;/span&gt;&lt;span class="se"&gt;(?:\w&lt;/span&gt;&lt;span class="sr"&gt;+:&lt;/span&gt;&lt;span class="se"&gt;)?(&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;depSpec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pkgAudit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;report&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pkgName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;pkgAudit&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;continue&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pkgSpec&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;lockfile&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;depSpec&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;satisfies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkgSpec&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pkgAudit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;vulnerable_versions&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fix&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;minVersion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkgAudit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patched_versions&lt;/span&gt;&lt;span class="p"&gt;)?.&lt;/span&gt;&lt;span class="nx"&gt;format&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fix&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Can't find satisfactory version for&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;pkgAudit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;module_name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;pkgAudit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patched_versions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;sv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;satisfies&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;desiredRange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;flags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;force&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Can't find patched version that satisfies&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;depSpec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;in&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="nx"&gt;pkgAudit&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;patched_versions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;upgraded&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;pkgName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="nx"&gt;patchEntry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pkgSpec&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pkgName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fix&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;bins&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;npm&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  4.
&lt;/h2&gt;

&lt;p&gt;The final step is to update the checksums.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn install mode='--update-lockfile'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Refs
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/antongolub/yarn-audit-fix"&gt;yarn-audit-fix&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/snyk/nodejs-lockfile-parser/"&gt;nodejs-lockfile-parser&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.section.io/engineering-education/npm-vs-yarn-which-one-to-choose/"&gt;npm-vs-yarn-which-one-to-choose&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dev.to/antongolub/yarn-audit-fix-workaround-i2a"&gt;yarn-audit-fix-workaround&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>yarn</category>
      <category>javascript</category>
      <category>security</category>
    </item>
    <item>
      <title>Actually you don’t need 'semantic-release' for semantic release</title>
      <dc:creator>Anton Golub</dc:creator>
      <pubDate>Sun, 23 May 2021 12:32:10 +0000</pubDate>
      <link>https://dev.to/antongolub/you-don-t-need-semantic-release-sometimes-3k6k</link>
      <guid>https://dev.to/antongolub/you-don-t-need-semantic-release-sometimes-3k6k</guid>
      <description>&lt;p&gt;I’m a big fan of &lt;a href="https://semantic-release.gitbook.io/semantic-release/" rel="noopener noreferrer"&gt;semantic-release&lt;/a&gt; since it appeared. I followed its development, studied its inners. I made in-house reports, held workshops and finally brought &lt;strong&gt;semrel&lt;/strong&gt; to &lt;a href="https://github.com/qiwi/semantic-release-toolkit" rel="noopener noreferrer"&gt;our build infrastructure&lt;/a&gt;. I wrote &lt;a href="https://github.com/qiwi/semantic-release-gh-pages-plugin" rel="noopener noreferrer"&gt;plugins&lt;/a&gt;, &lt;a href="https://github.com/qiwi/semantic-release-toolkit/tree/master/packages/plugin-creator" rel="noopener noreferrer"&gt;plugin-factories&lt;/a&gt; and &lt;a href="https://github.com/qiwi/semantic-release-toolkit/tree/master/packages/testing-suite" rel="noopener noreferrer"&gt;testing-tools&lt;/a&gt; for it. For several years now, I've been trying to combine semantic releases and monorepositories in &lt;em&gt;many&lt;/em&gt; OSS projects:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/qiwi/semantic-release-monorepo-hooks" rel="noopener noreferrer"&gt;https://github.com/qiwi/semantic-release-monorepo-hooks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/dhoulb/multi-semantic-release" rel="noopener noreferrer"&gt;https://github.com/dhoulb/multi-semantic-release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/qiwi/multi-semantic-release" rel="noopener noreferrer"&gt;https://github.com/qiwi/multi-semantic-release&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Etc, etc, so on. I’m just trying to say, that &lt;strong&gt;semrel&lt;/strong&gt; had a significant impact on my professional life.&lt;/p&gt;

&lt;h2&gt;
  
  
  Semrel goal
&lt;/h2&gt;

&lt;p&gt;The main purpose of semantic-release is to transform semantic (conventional) commits into build artifacts and deployments. With version bumping, changelogs, tagging, pkg publishing. “Fully-automated release” — is the true. There are also &lt;a href="https://semantic-release.gitbook.io/semantic-release/extending/plugins-list" rel="noopener noreferrer"&gt;dozens on plugins&lt;/a&gt;, so you’ll most likely find a solution for any standard case. It really saves times.&lt;/p&gt;

&lt;h2&gt;
  
  
  But sometimes
&lt;/h2&gt;

&lt;p&gt;You may need a &lt;em&gt;minor&lt;/em&gt; tweak up. For example, push some pkg to both public and internal registries. Ooops. &lt;a href="https://github.com/semantic-release/npm/issues/194" rel="noopener noreferrer"&gt;&lt;em&gt;"...publishing to two different registry is not a good idea"&lt;/em&gt;&lt;/a&gt;. In this case you can not rely on stable, convenient and tested in millions runs &lt;a href="https://github.com/semantic-release/npm" rel="noopener noreferrer"&gt;semrel/npm plugin&lt;/a&gt;, and you have to just write a pair of commands by hand with &lt;a href="https://github.com/semantic-release/exec" rel="noopener noreferrer"&gt;semantic-release/exec&lt;/a&gt; instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"//npm-registry.domain.com/:_authToken=&lt;/span&gt;&lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;” &amp;gt;&amp;gt; .npmrc
echo "&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;jq &lt;span class="s1"&gt;'.name="@scope/pkg-name”’ package.json\`" &amp;gt; package.json
npm config set registry https://npm-registry.domain.com
npm publish --no-git-tag-version
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Another instance — disabling &lt;a href="https://github.com/semantic-release/semantic-release/blob/4c1c9928697569f0efa3b786a7e3da0fd01fcee9/lib/branches/index.js#L24" rel="noopener noreferrer"&gt;git notes fetching&lt;/a&gt;. &lt;a href="https://github.com/semantic-release/semantic-release/issues/1646" rel="noopener noreferrer"&gt;&lt;em&gt;"Afraid that won't be possible"&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Of course, you may fork &lt;strong&gt;semrel&lt;/strong&gt; and remove the mentioned line. Or create a plugin/hook, that will override loaded &lt;strong&gt;execa&lt;/strong&gt; module with patched one version, than just skips &lt;code&gt;git notes&lt;/code&gt; invocation (this is really frustrating, &lt;a href="https://github.com/dhoulb/multi-semantic-release/blob/561a8e66133d422d88008c32c479d1148876aba4/lib/execaHook.js#L5" rel="noopener noreferrer"&gt;I did smth similar&lt;/a&gt;). Or… &lt;code&gt;{{ another crazy workaround goes here }}&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;This is a watershed moment. Once you start to fight against the tool, it's time to just pick another one. The new dilemma:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Spend days and days for searching, tuning and testing analogs.&lt;/li&gt;
&lt;li&gt;Write your own semantic-release.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;My opinionated suggestion: if your case is very simple or, conversely, very complex, the second option will be optimal. Release script — is not a rocket science!&lt;/p&gt;
&lt;h2&gt;
  
  
  140 lines alternative
&lt;/h2&gt;

&lt;p&gt;Let's take a look at what exactly each release consists of, if we discard the high-level tool contracts. I use &lt;strong&gt;&lt;a href="https://github.com/google/zx" rel="noopener noreferrer"&gt;zx&lt;/a&gt;&lt;/strong&gt; in the examples, but it could be &lt;strong&gt;execa&lt;/strong&gt; or native &lt;strong&gt;child_process.exec&lt;/strong&gt; too.&lt;/p&gt;
&lt;h3&gt;
  
  
  1. Git configuration
&lt;/h3&gt;

&lt;p&gt;To make a commit you need a committer: just name and email that will be associated with author. Also &lt;a href="https://docs.github.com/en/github/authenticating-to-github/keeping-your-account-and-data-secure/creating-a-personal-access-token" rel="noopener noreferrer"&gt;PAT&lt;/a&gt; or &lt;a href="https://stackoverflow.com/questions/26372417/github-oauth2-token-how-to-restrict-access-to-read-a-single-private-repo" rel="noopener noreferrer"&gt;SSH token&lt;/a&gt; is required to push the commit.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;GIT_COMMITTER_NAME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GIT_COMMITTER_EMAIL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_TOKEN&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;GIT_COMMITTER_NAME&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;GIT_COMMITTER_EMAIL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;env.GITHUB_TOKEN, env.GIT_COMMITTER_NAME &amp;amp; env.GIT_COMMITTER_EMAIL must be set&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;gitAuth&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;GIT_COMMITTER_NAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;originUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git config --get remote.origin.url`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[,,&lt;/span&gt;&lt;span class="nx"&gt;repoHost&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;repoName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;originUrl&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\.&lt;/span&gt;&lt;span class="sr"&gt;git/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/.+&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;@|&lt;/span&gt;&lt;span class="se"&gt;\/\/)([^/]&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\/(&lt;/span&gt;&lt;span class="sr"&gt;.+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repoPublicUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoHost&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;repoAuthedUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`https://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;gitAuth&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoHost&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git config user.name &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;GIT_COMMITTER_NAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git config user.email &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;GIT_COMMITTER_EMAIL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git remote set-url origin &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoAuthedUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  2. Commit analysis
&lt;/h3&gt;

&lt;p&gt;Conventional commits are just a prefixed strings in git log. We should define some rules on how to associate messages substrings with corresponding release types:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;semanticTagPattern&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;v&lt;/span&gt;&lt;span class="se"&gt;?)(\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\.(\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)\.(\d&lt;/span&gt;&lt;span class="sr"&gt;+&lt;/span&gt;&lt;span class="se"&gt;)&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;releaseSeverityOrder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;major&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;minor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;patch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;semanticRules&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Features&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;minor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;prefixes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;feat&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fixes &amp;amp; improvements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;patch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;prefixes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fix&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;perf&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;refactor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;docs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BREAKING CHANGES&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;major&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BREAKING CHANGE&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BREAKING CHANGES&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]},&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Then we search for the prev release tag, that satisfies &lt;a href="https://semver.org/" rel="noopener noreferrer"&gt;semver&lt;/a&gt; pattern:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git tag -l --sort=-v:refname`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;lastTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;semanticTagPattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;And make commits cut from the found ref:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newCommits&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastTag&lt;/span&gt;
  &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git log --format=+++%s__%b__%h__%H &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git rev-list -1 &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastTag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;..HEAD`&lt;/span&gt;
  &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git log --format=+++%s__%b__%h__%H HEAD`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;+++&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;msg&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;subj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;short&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;__&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;raw&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;subj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;short&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Now we just need to parse them:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;semanticChanges&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newCommits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;subj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;short&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;semanticRules&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;prefixes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prefixMatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;prefixes&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`^(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;prefixes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;)(&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;(&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;w+&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;))?:&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;s.+$`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;keywordsMatcher&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;keywords&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;RegExp&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;keywords&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;|&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;):&lt;/span&gt;&lt;span class="se"&gt;\\&lt;/span&gt;&lt;span class="s2"&gt;s(.+)`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;subj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefixMatcher&lt;/span&gt;&lt;span class="p"&gt;)?.[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;keywordsMatcher&lt;/span&gt;&lt;span class="p"&gt;)?.[&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;subj&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;short&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;hash&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Ta-da. Semantic changes:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;semanticChanges&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fixes &amp;amp; improvements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;patch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;perf: use git for tags sorting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;perf: use git for tags sorting&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;short&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a1abdae&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;a1abdaea801824d0392e69f9182daf4d5f4b97db&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fixes &amp;amp; improvements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;patch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;refactor: minor simplifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;refactor: minor simplifications&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;short&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be847a2&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;be847a26e2b0583e889403ec00db45f9f9555e30&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;group&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Fixes &amp;amp; improvements&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;patch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fix: fix commit url template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;subj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;fix: fix commit url template&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;short&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3669edd&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;3669edd7eb440e29dc0fcf493c76fbfc04271023&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  3. Resolve next version:
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextReleaseType&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;releaseSeverityOrder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;semanticChanges&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;nextReleaseType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;No semantic changes - no semantic release.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextVersion&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;lastTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;releaseType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;lastTag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[,&lt;/span&gt; &lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;c3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;semanticTagPattern&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;exec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;lastTag&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;releaseType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;major&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-~&lt;/span&gt;&lt;span class="nx"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0.0`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;releaseType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;minor&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-~&lt;/span&gt;&lt;span class="nx"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.0`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;releaseType&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;patch&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c1&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;c2&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;.&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="o"&gt;-~&lt;/span&gt;&lt;span class="nx"&gt;c3&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})(&lt;/span&gt;&lt;span class="nx"&gt;lastTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;nextReleaseType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nextTag&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;v&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;nextVersion&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  4. Generate release notes
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;releaseDiffRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`## [&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nextVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;](&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoPublicUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/compare/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;lastTag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;...&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nextTag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;) (&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;toISOString&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;releaseDetails&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Object&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;values&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;semanticChanges&lt;/span&gt;
&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;reduce&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;short&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;acc&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt; &lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;commitRef&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`* &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;change&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; ([&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;short&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;](&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoPublicUrl&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/commit/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;hash&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;))`&lt;/span&gt;

      &lt;span class="nx"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;commitRef&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;acc&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{}))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(({&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;`
### &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;group&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;
&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;commits&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;releaseNotes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;releaseDiffRef&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;releaseDetails&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  5. Update CHANGELOG.md
&lt;/h3&gt;

&lt;p&gt;Attach &lt;code&gt;releaseNotes&lt;/code&gt; to file. Just one string.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`echo &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;releaseNotes&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"\n$(cat ./CHANGELOG.md)" &amp;gt; ./CHANGELOG.md`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  6. Update package version
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`npm --no-git-tag-version version &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nextVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  7. Git release.
&lt;/h3&gt;

&lt;p&gt;Create commit. Create tag. Push them.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;releaseMessage&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`chore(release): &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nextVersion&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; [skip ci]`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git add -A .`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git commit -am &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;releaseMessage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git tag -a &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;nextTag&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; HEAD -m &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;releaseMessage&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`git push --follow-tags origin HEAD:refs/heads/master`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  8. GitHub release
&lt;/h3&gt;

&lt;p&gt;Just a single &lt;strong&gt;curl&lt;/strong&gt; POST to &lt;a href="https://docs.github.com/en/rest/reference/repos#releases" rel="noopener noreferrer"&gt;gh rest api&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;releaseData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nextTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;tag_name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;nextTag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;releaseNotes&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`curl -u &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;GIT_COMMITTER_NAME&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;:&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; -H "Accept: application/vnd.github.v3+json" https://api.github.com/repos/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/releases -d &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;releaseData&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h3&gt;
  
  
  9. Npm publish
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`npm publish --no-git-tag-version`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Need several registries? NP.&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`npm config set registry https://registry.npmjs.org`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`npm publish --no-git-tag-version`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`echo "&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;jq '.name="@&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;repoName&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"' package.json&lt;/span&gt;&lt;span class="se"&gt;\`&lt;/span&gt;&lt;span class="s2"&gt;" &amp;gt; package.json`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`npm config set registry https://npm.pkg.github.com`&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;$&lt;/span&gt;&lt;span class="s2"&gt;`npm publish --no-git-tag-version`&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  Conclusions
&lt;/h2&gt;

&lt;p&gt;This solution does not cover corner cases and has significant limitations of usage. Ultimately, you don't care if other tools have 99.99999% applicability until they ignore just one specific case — &lt;strong&gt;yours&lt;/strong&gt;. But now you have completely taken back release flow control. You're able to improve and modify this snippet as you wish and whenever you like.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/semrel-extra/zx-semrel/blob/master/release.mjs" rel="noopener noreferrer"&gt;release.mjs&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/semrel-extra/zx-semrel/blob/master/.github/workflows/release.yml" rel="noopener noreferrer"&gt;gh release.yaml&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/semrel-extra/zx-semrel/runs/2649051545?check_suite_focus=true" rel="noopener noreferrer"&gt;release log&lt;/a&gt;&lt;/p&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fassets.dev.to%2Fassets%2Fgithub-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/semrel-extra" rel="noopener noreferrer"&gt;
        semrel-extra
      &lt;/a&gt; / &lt;a href="https://github.com/semrel-extra/zx-semrel" rel="noopener noreferrer"&gt;
        zx-semrel
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      `zx`-based release script as `semantic-release` alternative (PoC)
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;zx-semrel&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;&lt;a href="https://github.com/semrel-extra/zx-semrel/actions" rel="noopener noreferrer"&gt;&lt;img src="https://github.com/semrel-extra/zx-semrel/workflows/Release/badge.svg" alt="Release"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://github.com/google/zx" rel="noopener noreferrer"&gt;zx&lt;/a&gt; -based release script as &lt;a href="https://github.com/semantic-release/semantic-release" rel="noopener noreferrer"&gt;semantic-release&lt;/a&gt; alternative (PoC)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Sometimes bloody enterprise enforces you not to use any third-party solutions for sensitive operations (like release, deploy, so on)
Old good script &lt;strong&gt;copy-paste&lt;/strong&gt; hurries to the rescue!&lt;/p&gt;
&lt;p&gt;Btw, here's an adaptation for monorepos: &lt;a href="https://github.com/semrel-extra/zx-bulk-release" rel="noopener noreferrer"&gt;zx-bulk-release&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Requirements&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;macOS / linux&lt;/li&gt;
&lt;li&gt;Node.js &amp;gt;= 14.13.1&lt;/li&gt;
&lt;li&gt;git &amp;gt;= 2.0&lt;/li&gt;
&lt;li&gt;zx &amp;gt;= 1.6.0&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Key features&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Zero dependencies&lt;/li&gt;
&lt;li&gt;Zero configuration&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/semrel-extra/zx-semrel/actions" rel="noopener noreferrer"&gt;Pretty fast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/semrel-extra/zx-semrel/blob/master/release.mjs" rel="noopener noreferrer"&gt;Tiny&lt;/a&gt;, less than 140 lines with comments&lt;/li&gt;
&lt;li&gt;Reliability, safety, simplicity and maintainability (sarcasm)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Functionality&lt;/h3&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Poor &lt;a href="https://www.conventionalcommits.org/en/v1.0.0/" rel="nofollow noopener noreferrer"&gt;conventional commits&lt;/a&gt; analysis&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;CHANGELOG.md&lt;/code&gt; generation&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;package.json&lt;/code&gt; version bumping&lt;/li&gt;
&lt;li&gt;Git release commit creation&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.github.com/en/github/administering-a-repository/releasing-projects-on-github/managing-releases-in-a-repository#creating-a-release" rel="noopener noreferrer"&gt;GitHub Release&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;Package publishing to both &lt;a href="https://registry.npmjs.org" rel="nofollow noopener noreferrer"&gt;npmjs&lt;/a&gt; and &lt;a href="http://npm.pkg.github.com" rel="noopener noreferrer"&gt;gh&lt;/a&gt; registries&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🚀 Usage&lt;/h3&gt;

&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Copy&lt;/li&gt;
&lt;li&gt;Tweak up, inject tokens, etc&lt;/li&gt;
&lt;li&gt;Run&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;curl https://raw.githubusercontent.com/semrel-extra/zx-semrel/master/release.mjs &lt;span class="pl-k"&gt;&amp;gt;&lt;/span&gt; ./release.mjs
zx ./release.mjs&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;or this like if &lt;code&gt;zx&lt;/code&gt; is not installed:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Just replace GIT* env values with your own&lt;/span&gt;
GIT_COMMITTER_NAME=antongolub GIT_COMMITER_EMAIL=mailbox@antongolub.ru GITHUB_TOKEN=token npx zx ./release.mjs&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;or just run it without any edits though &lt;strong&gt;npx&lt;/strong&gt;:&lt;/p&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;…
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/semrel-extra/zx-semrel" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>javascript</category>
      <category>github</category>
      <category>opensource</category>
      <category>productivity</category>
    </item>
  </channel>
</rss>
