<?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: Axel Navarro</title>
    <description>The latest articles on DEV Community by Axel Navarro (@navarroaxel).</description>
    <link>https://dev.to/navarroaxel</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F199189%2F618b73a6-4aa2-47fe-b338-23b630a41456.jpeg</url>
      <title>DEV Community: Axel Navarro</title>
      <link>https://dev.to/navarroaxel</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/navarroaxel"/>
    <language>en</language>
    <item>
      <title>Implementing effortless rollbacks in Git</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Tue, 22 Oct 2024 13:17:59 +0000</pubDate>
      <link>https://dev.to/cloudx/implementing-effortless-rollbacks-in-git-1474</link>
      <guid>https://dev.to/cloudx/implementing-effortless-rollbacks-in-git-1474</guid>
      <description>&lt;p&gt;In this series we've learned how to merge or split commits using the &lt;code&gt;git rebase&lt;/code&gt; command, now it's time to learn how to make rollbacks using the &lt;a href="https://git-scm.com/docs/git-revert" rel="noopener noreferrer"&gt;&lt;code&gt;git revert&lt;/code&gt;&lt;/a&gt; understanding how this works and how to rollback a revert and when this can be useful. 🪄&lt;/p&gt;

&lt;h2&gt;
  
  
  The commit ID
&lt;/h2&gt;

&lt;p&gt;The commit ID (SHA) is used by Git to identify a commit, and it can be regenerated using &lt;a href="https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---force-rebase" rel="noopener noreferrer"&gt;&lt;code&gt;git rebase --force-rebase&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase &lt;span class="nt"&gt;--force-rebase&lt;/span&gt; HEAD~1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By default, the rebase makes a &lt;em&gt;fast-forward&lt;/em&gt; when it's possible 🤔. This means that the commits aren't re-created when they're being applied over the same previous commit, keeping the commit ID unchanged.&lt;/p&gt;

&lt;p&gt;This also happens with you use &lt;a href="https://git-scm.com/docs/git-cherry-pick" rel="noopener noreferrer"&gt;&lt;code&gt;git cherry-pick&lt;/code&gt;&lt;/a&gt;, this command applies changes changing the parent commit and that means a new commit ID is used for these picked changes.&lt;/p&gt;

&lt;p&gt;When is a new commit ID useful? This may be useful in those sad days when GitHub has issues detecting new commits in the repository and fails to trigger the GitHub Actions or the pull request is out of sync with the Git repo. 🌚&lt;/p&gt;

&lt;p&gt;Also, this could be useful when you revert a failed merge and you need to remerge the same code now expecting to work properly 🤞 without &lt;em&gt;reverting the reversion&lt;/em&gt;  😵‍💫. Let's explain this complex scenario a little more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Making a rollback of a merge
&lt;/h2&gt;

&lt;p&gt;For this example we merge a topic branch into a shared branch as always.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout staging
git merge mytopic-branch
git push origin staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But let's say you merged these changes too soon, or you need another topic to be merged into &lt;code&gt;staging&lt;/code&gt; first. Then you need to &lt;a href="https://git-scm.com/docs/git-revert" rel="noopener noreferrer"&gt;revert&lt;/a&gt; the merge.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git revert mytopic-branch
git push origin staging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And we've reverted to a Git merge effortlessly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Remerging a reverted merge
&lt;/h3&gt;

&lt;p&gt;After pushing more commits into &lt;code&gt;staging&lt;/code&gt; it's time to remerge your topic 🎉 again, then you get a Git output worse than a merge conflict. 🙀&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ git merge mytopic-branch
Already up to date.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0k4f4c3ktcg6iuoybvv1.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0k4f4c3ktcg6iuoybvv1.jpg" alt="The developer is sad because he received an " width="507" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Why are we getting &lt;code&gt;Already up to date&lt;/code&gt; if the merge of &lt;code&gt;mytopic-branch&lt;/code&gt; was reverted? Since Git is inspired by the &lt;a href="https://en.wikipedia.org/wiki/Patch_(computing)" rel="noopener noreferrer"&gt;patch-set&lt;/a&gt; workflow of open-source development, the &lt;code&gt;mytopic-branch&lt;/code&gt;'s commits already exist in the &lt;code&gt;staging&lt;/code&gt; branch even if they have been reverted. You can still see the reverted commit in the log:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;commit 68ab202e93cb059d01feaaae1cf7d988e2c470a1 (HEAD -&amp;gt; staging)
Author: Axel N &amp;lt;...@gmail.com&amp;gt;
Date:   Wed Oct 16 19:09:13 2024 -0000

    Revert "Upgrade to Rust v1.81 (#87)"

    This reverts commit 0680aa76b96a29b3052482c2e38f817363ac88a8.

commit 0680aa76b96a29b3052482c2e38f817363ac88a8 (origin/staging)
Author: Axel N &amp;lt;...@gmail.com&amp;gt;
Date:   Wed Oct 16 10:46:05 2024 -0000

    Upgrade to Rust v1.81 (#87)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's use &lt;code&gt;git rebase&lt;/code&gt; to recreate this commit of the topic branch:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout mytopic-branch
git rebase &lt;span class="nt"&gt;--force-rebase&lt;/span&gt; HEAD~1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, let's do the merge again!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout staging
git merge mytopic-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And, it's done!&lt;/p&gt;

&lt;p&gt;What if &lt;code&gt;mytopic-branch&lt;/code&gt; is a shared branch like &lt;code&gt;development&lt;/code&gt; and I can't rewrite its history? You could &lt;em&gt;revert the reversion&lt;/em&gt; using this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git checkout staging
git revert 68ab202 &lt;span class="c"&gt;# SHA of the revert commit.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this latest scenario we're undoing the reversion and you can get Git conflicts too. Just resolve it as always. You can choose any method to do this but if anyone is not too familiar with Git a revert of a revert could be too hard to understand by just checking the Git log. Try to keep the commit message clear enough about what happened and what commits you're reapplying.&lt;/p&gt;

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

&lt;p&gt;We have learned how to rollback a merge and how to recreate commits when it's needed.&lt;/p&gt;

&lt;p&gt;Always remember that the commit ID is used by Git for traceability, If you change the commit ID of a set of changes this is not the same commit for Git.&lt;/p&gt;

&lt;p&gt;I hope you find this post useful. Let me know in the comments how you resolved the &lt;em&gt;revert the reversion&lt;/em&gt;. 👇 See ya!&lt;/p&gt;

</description>
      <category>git</category>
      <category>bash</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>A step-by-step guide to splitting commits using Git rebase</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Fri, 27 Sep 2024 15:08:10 +0000</pubDate>
      <link>https://dev.to/cloudx/a-step-by-step-guide-to-splitting-commits-using-git-rebase-4bik</link>
      <guid>https://dev.to/cloudx/a-step-by-step-guide-to-splitting-commits-using-git-rebase-4bik</guid>
      <description>&lt;p&gt;In the previous posts we checked how to use the &lt;a href="https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---interactive" rel="noopener noreferrer"&gt;&lt;code&gt;git rebase --interactive&lt;/code&gt;&lt;/a&gt; and how to squash your commits using the &lt;code&gt;fixup&lt;/code&gt; command in the &lt;code&gt;git-rebase-todo&lt;/code&gt; file.&lt;/p&gt;

&lt;p&gt;Now, we're going to learn how to split commits using the terminal. But why splitting a commit? This helps to organize your work. For instance, you may need to rearrange your changes to make backporting hotfixes to older released versions easier, likely using &lt;code&gt;git cherry-pick&lt;/code&gt;. Let’s dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  Splitting commits
&lt;/h2&gt;

&lt;p&gt;You can use &lt;code&gt;git rebase&lt;/code&gt; to split your commits. There are several alternatives to do it and we're going to check the &lt;code&gt;git rebase --interactive&lt;/code&gt; way. You can use &lt;code&gt;git rebase --interactive main&lt;/code&gt; to reapply all your new commits to the top of the &lt;code&gt;main&lt;/code&gt; branch, or just &lt;code&gt;git rebase --interactive HEAD~2&lt;/code&gt; to apply the latest 2 commits.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pick 0680aa7 fix header responsiveness
exec npm test
edit 68ab202 fix grid responsiveness and upgrade bootstrap
exec npm test
break
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 You can &lt;code&gt;pick&lt;/code&gt; the rebased commits in any order! but this can cause Git conflicts 😞. Also you can &lt;code&gt;pick&lt;/code&gt; commits from other branches like &lt;a href="https://git-scm.com/docs/git-cherry-pick" rel="noopener noreferrer"&gt;&lt;code&gt;git cherry-pick&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Well, Git will &lt;code&gt;pick&lt;/code&gt; the commit &lt;code&gt;0680aa7&lt;/code&gt; as-is and run &lt;code&gt;npm test&lt;/code&gt; and if the command exists then Git will pick the commit &lt;code&gt;68ab202&lt;/code&gt; and break, returning the control of the shell to you allowing to &lt;code&gt;edit&lt;/code&gt; this last commit. Let's see how to edit the last commit.&lt;/p&gt;

&lt;p&gt;🧠 To clarify: &lt;code&gt;edit&lt;/code&gt; stops after applying the commit, so that you can edit the files or the commit message, amend the commit, and continue rebasing.&lt;/p&gt;

&lt;p&gt;First, undo it keeping all the changes in the working tree using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git reset HEAD^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you are able to make any edition to your code. Then you can use &lt;code&gt;git add&lt;/code&gt; to add the changes to the index to commit it in parts splitting the original commit. As the following example shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add package.json package-lock.json
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'upgrade bootstrap'&lt;/span&gt;
git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s1"&gt;'fix grid responsiveness'&lt;/span&gt;
git rebase &lt;span class="nt"&gt;--continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Git will continue running the second &lt;code&gt;exec&lt;/code&gt; command that makes an &lt;code&gt;npm test&lt;/code&gt;. If this fails you will need to fix your code; otherwise Git will stop at the &lt;code&gt;break&lt;/code&gt; command returning the control of the shell to you again. Here you are able to run your application, make the final checks, etc.&lt;/p&gt;

&lt;p&gt;If you're happy with the changes, run &lt;code&gt;git rebase --continue&lt;/code&gt; to finish the rebase, or &lt;code&gt;git rebase --abort&lt;/code&gt; to cancel all changes. 👀&lt;/p&gt;

&lt;p&gt;Let me know in the comments 👇 what other method you prefer to split or edit commits.&lt;/p&gt;

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

&lt;p&gt;Remember that it's useful to keep your commits small and semantic. Committing atomic changes makes it easier to check where a crazy bug was introduced. Splitting commits is an easy process to perform and helps you to keep your Git history more readable. Remember, more commits is not an issue if they are small and semantic.&lt;/p&gt;

&lt;p&gt;In the next post we're going to check the &lt;code&gt;git rebase --force-rebase&lt;/code&gt; command. See ya!&lt;/p&gt;

</description>
      <category>git</category>
      <category>bash</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Git rebase exec to validate every commit</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Mon, 16 Sep 2024 13:10:33 +0000</pubDate>
      <link>https://dev.to/cloudx/git-rebase-exec-to-validate-every-commit-32o</link>
      <guid>https://dev.to/cloudx/git-rebase-exec-to-validate-every-commit-32o</guid>
      <description>&lt;p&gt;In the previous post we checked out how the &lt;code&gt;git rebase&lt;/code&gt; works and how to use the &lt;code&gt;--interactive&lt;/code&gt; mode to keep your topic branch up-to-date with the target (e.g. &lt;code&gt;main&lt;/code&gt;). We also talked about how to use the &lt;code&gt;exec&lt;/code&gt; command into the &lt;code&gt;git-rebase-todo&lt;/code&gt; file to run custom commands for the rebased commits.&lt;/p&gt;

&lt;p&gt;Now, let's check how to use&lt;code&gt;exec&lt;/code&gt; in a short way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Exec, exec, exec
&lt;/h2&gt;

&lt;p&gt;If you need to run the build, tests, or any format tool while rebasing because you must guarantee that every commit is valid, you can speed things up using the &lt;code&gt;git rebase --exec&lt;/code&gt; command.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase main &lt;span class="nt"&gt;--exec&lt;/span&gt; &lt;span class="s2"&gt;"npm test"&lt;/span&gt; &lt;span class="nt"&gt;--exec&lt;/span&gt; &lt;span class="s2"&gt;"npm run build"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the command fails 😩 you will need to fix your code and then resume the rebase with &lt;code&gt;git rebase --continue&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;--amend&lt;/span&gt;
git rebase &lt;span class="nt"&gt;--continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Reschedule failed exec
&lt;/h3&gt;

&lt;p&gt;If an &lt;code&gt;exec&lt;/code&gt; command exits with a failure, your rebase will return the shell control to you. The &lt;code&gt;git rebase --continue&lt;/code&gt; command will resume the execution but it won't run the failed &lt;code&gt;exec&lt;/code&gt; again. To avoid this you can use &lt;a href="https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt---reschedule-failed-exec" rel="noopener noreferrer"&gt;&lt;code&gt;--reschedule-failed-exec&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase main &lt;span class="nt"&gt;--exec&lt;/span&gt; &lt;span class="s2"&gt;"npm test"&lt;/span&gt; &lt;span class="nt"&gt;--exec&lt;/span&gt; &lt;span class="s2"&gt;"npm run build"&lt;/span&gt; &lt;span class="nt"&gt;--reschedule-failed-exec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can make this the default behavior by using the &lt;a href="https://git-scm.com/docs/git-rebase#Documentation/git-rebase.txt-rebaserescheduleFailedExec" rel="noopener noreferrer"&gt;rebase.rescheduleFailedExec&lt;/a&gt; setting:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; rebase.rescheduleFailedExec &lt;span class="nb"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To turn this off when the &lt;code&gt;rescheduleFailedExec&lt;/code&gt; is globally &lt;code&gt;true&lt;/code&gt; you should use the &lt;code&gt;--no-reschedule-failed-exe&lt;/code&gt; flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase main &lt;span class="nt"&gt;--exec&lt;/span&gt; &lt;span class="s2"&gt;"npm test"&lt;/span&gt; &lt;span class="nt"&gt;--no-reschedule-failed-exec&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, you can unset this globally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase &lt;span class="nt"&gt;--global&lt;/span&gt; &lt;span class="nt"&gt;--unset&lt;/span&gt; rebase.rescheduleFailedExec
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;If your commits will live on your Git repository forever (e.g. inside the &lt;code&gt;main&lt;/code&gt; branch) without being squashed (combined into one) it's useful to guarantee the validity of each commit individually. Git provides this &lt;code&gt;exec&lt;/code&gt; mechanism to automate this process.&lt;/p&gt;

&lt;p&gt;How often do you check the tests and build for every commit of your created pull request? 😉&lt;/p&gt;

&lt;p&gt;In the next chapter we're going to see how to split the commits 🔪 using &lt;code&gt;git rebase&lt;/code&gt; too.&lt;/p&gt;

</description>
      <category>git</category>
      <category>bash</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Rewriting the history with Git rebase</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Fri, 16 Aug 2024 13:07:44 +0000</pubDate>
      <link>https://dev.to/cloudx/rewriting-the-history-with-git-rebase-30gh</link>
      <guid>https://dev.to/cloudx/rewriting-the-history-with-git-rebase-30gh</guid>
      <description>&lt;p&gt;As when we work in a new topic (feature or bugfix) it is recommended to keep our branch up-to-date with the target branch (&lt;code&gt;main&lt;/code&gt;, &lt;code&gt;development&lt;/code&gt;, etc.), we can use the &lt;a href="https://git-scm.com/docs/git-rebase" rel="noopener noreferrer"&gt;&lt;code&gt;git rebase&lt;/code&gt;&lt;/a&gt; command instead of &lt;code&gt;git merge&lt;/code&gt; in order to keep our Git history clean.&lt;/p&gt;

&lt;p&gt;💡 The Git documentation uses &lt;em&gt;topic branch&lt;/em&gt; as a generic term for &lt;em&gt;feature branch&lt;/em&gt; and &lt;em&gt;bugfix branch&lt;/em&gt;, giving to branches like &lt;code&gt;main&lt;/code&gt;, &lt;code&gt;development&lt;/code&gt;, etc. the name of &lt;em&gt;shared branch&lt;/em&gt; because are being used for multiple developers.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;git rebase&lt;/code&gt; command reapplies our commits on top of the target branch, rewriting the Git history. This is not recommended on shared branches because it causes divergence (and therefore the need of conflict solving) and panic on your teammates 😬 since it is sometimes hard to resolve.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics
&lt;/h2&gt;

&lt;p&gt;To be clear about how &lt;code&gt;git rebase&lt;/code&gt; works we're going to use the simplest example: you have 3 commits in your topic branch and you want to get the latest changes from &lt;code&gt;main&lt;/code&gt;. To do so you can simply run the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase main
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're lucky to get no conflicts then you'll have a new Git history with your 3 commits on the top of the &lt;code&gt;main&lt;/code&gt; branch and you just need to override (&lt;code&gt;--force&lt;/code&gt;) the remote branch with your new history.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git push &lt;span class="nt"&gt;--force&lt;/span&gt; origin mytopic-branch
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But what if we have conflicts? Well, you're reapplying 3 commits so any of these could bring conflicts. Just resolve conflicts as always and then continue rebasing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git rebase &lt;span class="nt"&gt;--continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's check a more dynamic scenario.&lt;/p&gt;

&lt;h2&gt;
  
  
  Squashing commits interactively
&lt;/h2&gt;

&lt;p&gt;If you made several commits while you were preparing your work but now you want to combine your commits into one before creating the pull request then you can rebase against your target branch using the &lt;code&gt;--interactive&lt;/code&gt; argument &lt;code&gt;git rebase --interactive main&lt;/code&gt;. A &lt;code&gt;git-rebase-todo&lt;/code&gt; file will be open in the default editor (&lt;code&gt;nano&lt;/code&gt;, &lt;code&gt;vim&lt;/code&gt;, etc.) where you can edit the commands that Git will execute to reapply your commits. Let's check this example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pick 0680aa7 fix grid responsiveness
fixup 68ab202 fix unit tests
exec npm test
reword 76d14ac fix grid layout for iOS
exec npm test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 The&lt;code&gt;git-rebase-todo&lt;/code&gt; file is a sequence of commands to be executed by Git, and you can run any shell command using the &lt;code&gt;exec&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;pick&lt;/code&gt; is used to take a commit as-is. The commit message is displayed by Git only to help you to identify what changes are there (Git will ignore if you edit it).&lt;/p&gt;

&lt;p&gt;Then we have the &lt;code&gt;fixup&lt;/code&gt; command, which combines a specific commit into the previous one without changing the commit message.&lt;/p&gt;

&lt;p&gt;If the &lt;code&gt;npm test&lt;/code&gt; fails you will see the following message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;warning: execution failed: npm test
You can fix the problem, and then run

  git rebase --continue
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sad time when you need to fix the tests 😢. Before continuing the rebase you will need to commit these changes creating a new commit; or integrating there in the latest one using the classic &lt;a href="https://git-scm.com/docs/git-commit#Documentation/git-commit.txt---amend" rel="noopener noreferrer"&gt;&lt;code&gt;git commit --amend&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git add &lt;span class="nb"&gt;.&lt;/span&gt;
git commit &lt;span class="nt"&gt;--amend&lt;/span&gt;
git rebase &lt;span class="nt"&gt;--continue&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;reword&lt;/code&gt; command that we put into the &lt;code&gt;git-rebase-todo&lt;/code&gt; file will open your default editor (e.g. nano) with the current commit message for &lt;code&gt;76d14ac&lt;/code&gt;. Edit it and save to continue.&lt;/p&gt;

&lt;p&gt;💡 The &lt;code&gt;git rebase&lt;/code&gt; reapplies commits, you can do this without needing a target branch (&lt;code&gt;main&lt;/code&gt;): just indicate how many commits you want to reapply for the current branch (&lt;code&gt;HEAD&lt;/code&gt;). For our example of 3 latest commits:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git rebase &lt;span class="nt"&gt;--interactive&lt;/span&gt; HEAD~3
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 Remember that &lt;code&gt;HEAD&lt;/code&gt; is a pointer to either your current branch or your current commit when you're in a &lt;strong&gt;detached HEAD&lt;/strong&gt; (meaning you are located in a commit not associated with any branch).&lt;/p&gt;

&lt;p&gt;And, this is the usage of &lt;code&gt;git rebase --interactive&lt;/code&gt; 🎉. Let's continue with the &lt;code&gt;git rebase --exec&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Do you either prefer &lt;code&gt;rebase&lt;/code&gt; or &lt;code&gt;merge&lt;/code&gt; to keep your branch up to date? 👇 Let me know in the comments.&lt;/p&gt;

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

&lt;p&gt;You can manipulate the commits in a useful way using &lt;code&gt;git rebase&lt;/code&gt;, maybe just using it to keep your work up to date using a linear history, optionally combining your editions into one commit using &lt;code&gt;fixup&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I hope this guide helps you understand more about Git and how to manipulate your commits safely because you can always &lt;code&gt;git rebase --abort&lt;/code&gt; if the things aren't going how is planned.&lt;/p&gt;

&lt;p&gt;In the following post of this series of &lt;code&gt;git rebase&lt;/code&gt; we're going to see how to run tests for the rebased commits automatically. 😬&lt;/p&gt;

</description>
      <category>git</category>
      <category>bash</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Node.js v20: .env files, import modules, and Permission Model</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Fri, 09 Aug 2024 12:23:27 +0000</pubDate>
      <link>https://dev.to/cloudx/nodejs-v20-env-files-import-modules-and-permission-model-2fd9</link>
      <guid>https://dev.to/cloudx/nodejs-v20-env-files-import-modules-and-permission-model-2fd9</guid>
      <description>&lt;p&gt;Node.js v20.6 was released with amazing new features that are part of the LTS versions from October 24th, 2023. Let's see!&lt;/p&gt;

&lt;h2&gt;
  
  
  The INI configuration files
&lt;/h2&gt;

&lt;p&gt;Say goodbye to the &lt;code&gt;dotenv&lt;/code&gt; package, now Node.js can load &lt;a href="https://nodejs.org/docs/latest/api/cli.html#--env-fileconfig" rel="noopener noreferrer"&gt;environment variables&lt;/a&gt; from a &lt;code&gt;.env&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--env-file&lt;/span&gt; path/to/.env index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 The path to the &lt;a href="https://en.wikipedia.org/wiki/INI_file" rel="noopener noreferrer"&gt;INI file&lt;/a&gt; is required because Node.js didn't choose a default name for the INI file.&lt;/p&gt;

&lt;p&gt;🧠 If the INI file does not exist, the node process will not fail; instead, it will start running without the environment variables.&lt;/p&gt;

&lt;h3&gt;
  
  
  Loading &lt;code&gt;NODE_OPTIONS&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;You can load Node.js' &lt;a href="https://nodejs.org/docs/latest/api/cli.html#environment-variables" rel="noopener noreferrer"&gt;specific environment variables&lt;/a&gt; (like &lt;code&gt;NODE_OPTIONS&lt;/code&gt;) using an INI configuration file like the following example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;NODE_NO_WARNINGS=1
NODE_OPTIONS="--experimental-permission --allow-fs-read=*"
TZ=Pacific/Honolulu
UV_THREADPOOL_SIZE=5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can use this with the same method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--env-file&lt;/span&gt; .env index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Preload ES modules
&lt;/h2&gt;

&lt;p&gt;Preload ES modules at startup using the &lt;code&gt;--import&lt;/code&gt; flag, the module will be loaded before any application code runs, even the entry point.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--import&lt;/span&gt; path/to/file.js index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This flag is similar to the well known &lt;code&gt;--require&lt;/code&gt; flag used to load CommonJS modules.&lt;/p&gt;

&lt;p&gt;💡 Modules preloaded with --require will run before modules preloaded with --import.&lt;/p&gt;

&lt;h2&gt;
  
  
  Permission Model
&lt;/h2&gt;

&lt;p&gt;We have a new mechanism to restrict access to specific resources during the execution of a Node.js process called &lt;a href="https://nodejs.org/docs/latest/api/permissions.html#permission-model" rel="noopener noreferrer"&gt;Permission Model&lt;/a&gt;. The API exists behind a flag &lt;a href="https://nodejs.org/docs/latest/api/cli.html#--experimental-permission" rel="noopener noreferrer"&gt;&lt;code&gt;--experimental-permission&lt;/code&gt;&lt;/a&gt; which, when enabled, will restrict access to all resources not explicitly allowed.&lt;/p&gt;

&lt;h3&gt;
  
  
  File System permissions
&lt;/h3&gt;

&lt;p&gt;The &lt;a href="https://nodejs.org/docs/latest/api/cli.html#--allow-fs-read" rel="noopener noreferrer"&gt;&lt;code&gt;--allow-fs-read&lt;/code&gt;&lt;/a&gt; flag allows all &lt;code&gt;FileSystemRead&lt;/code&gt; operations using &lt;code&gt;*&lt;/code&gt;, or to specific paths using absolute routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-permission&lt;/span&gt; &lt;span class="nt"&gt;--allow-fs-read&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="k"&gt;*&lt;/span&gt; index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To only allow access to specific paths you should use absolute routes&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-permission&lt;/span&gt; &lt;span class="nt"&gt;--allow-fs-read&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/index.js &lt;span class="nt"&gt;--allow-fs-read&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/path/to/directory index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 The initializer module also needs to be allowed. Otherwise &lt;code&gt;index.js&lt;/code&gt; file cannot be loaded by the Node.js process itself.&lt;/p&gt;

&lt;p&gt;💡 You can use &lt;code&gt;.&lt;/code&gt; to allow access to the working directory, but you can't use it to specify the path to a file (e.g. &lt;code&gt;./index.js&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-permission&lt;/span&gt; &lt;span class="nt"&gt;--allow-fs-read&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://nodejs.org/docs/latest/api/cli.html#--allow-fs-write" rel="noopener noreferrer"&gt;&lt;code&gt;--allow-fs-write&lt;/code&gt;&lt;/a&gt; flag allows access to specific paths or the whole file system using &lt;code&gt;*&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-permission&lt;/span&gt; &lt;span class="nt"&gt;--allow-fs-read&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--allow-fs-write&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/tmp/ index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Child Process
&lt;/h3&gt;

&lt;p&gt;When the Permission Model is enabled, the process will not be able to spawn any child process by default, you should use the &lt;a href="https://nodejs.org/docs/latest/api/cli.html#--allow-child-process" rel="noopener noreferrer"&gt;--allow-child-process&lt;/a&gt; to allow this operation. Let's use the following code for the &lt;code&gt;index.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;childProcess&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;node:child_process&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;childProcess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;spawn&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="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="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;require("fs").writeFileSync("./new-file.txt", "Hello, World!")&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;To run this snippet with the Permission Model enabled you should execute &lt;code&gt;index.js&lt;/code&gt; using the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;node &lt;span class="nt"&gt;--experimental-permission&lt;/span&gt; &lt;span class="nt"&gt;--allow-fs-read&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;--allow-child-process&lt;/span&gt; index.js
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 The child process doesn't inherit the Permission Model by default, that's why the &lt;code&gt;new-file.txt&lt;/code&gt; is created successfully.&lt;/p&gt;

&lt;h3&gt;
  
  
  More options
&lt;/h3&gt;

&lt;p&gt;You can check the &lt;a href="https://nodejs.org/docs/latest/api/cli.html#--allow-worker" rel="noopener noreferrer"&gt;--allow-worker&lt;/a&gt; flag if you want to create Worker Threads under this Permission Model and &lt;a href="https://nodejs.org/docs/latest/api/cli.html#--allow-wasi" rel="noopener noreferrer"&gt;--allow-wasi&lt;/a&gt; to allow the creation of WASI instances&lt;/p&gt;

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

&lt;p&gt;We have a lot of new tools to load environment variables for our application, a method to import preload ES modules required in our code and a new Permission Model to increase the security of our systems.&lt;/p&gt;

&lt;p&gt;Stay tuned to the &lt;a href="https://nodejs.org/en/blog" rel="noopener noreferrer"&gt;Node.js' blog&lt;/a&gt;, this team is adding awesome features in every version! We have initial TypeScript support and a Network Inspection using the DevTools in &lt;a href="https://github.com/nodejs/node/releases/tag/v22.6.0" rel="noopener noreferrer"&gt;v22.6.0&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Git autocorrect needs more marketing</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Tue, 23 Jul 2024 18:33:57 +0000</pubDate>
      <link>https://dev.to/cloudx/git-autocorrect-needs-more-marketing-20gg</link>
      <guid>https://dev.to/cloudx/git-autocorrect-needs-more-marketing-20gg</guid>
      <description>&lt;p&gt;Git comes with a lot of configurations to improve your experience with the command-line. We already talked about how to &lt;a href="https://dev.to/cloudx/how-to-color-the-moved-code-in-git-10ei"&gt;color moved blocks of code&lt;/a&gt;, but I recently found an awesome configuration called &lt;a href="https://git-scm.com/docs/git-config#Documentation/git-config.txt-helpautoCorrect" rel="noopener noreferrer"&gt;autocorrect&lt;/a&gt;. 🤩&lt;/p&gt;

&lt;p&gt;This feature allows Git to detect typos in your input and display similar valid commands. Git can also suggest the correct one or even run the suggestion automatically. Let's see!&lt;/p&gt;

&lt;h2&gt;
  
  
  The default value
&lt;/h2&gt;

&lt;p&gt;By default Git displays suggested commands if we had a typo in our command:&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;git pll
git: &lt;span class="s1"&gt;'pll'&lt;/span&gt; is not a git command. See &lt;span class="s1"&gt;'git --help'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;

The most similar &lt;span class="nb"&gt;command &lt;/span&gt;is
      pull
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But we can get more than just a suggestion… 👀&lt;/p&gt;

&lt;h2&gt;
  
  
  Check before running the suggestion
&lt;/h2&gt;

&lt;p&gt;If you configure autoCorrect with &lt;code&gt;prompt&lt;/code&gt; Git will ask for a confirmation to run the suggested command.&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;git config &lt;span class="nt"&gt;--global&lt;/span&gt; help.autocorrect prompt
&lt;span class="nv"&gt;$ &lt;/span&gt;git pll
WARNING: You called a Git &lt;span class="nb"&gt;command &lt;/span&gt;named &lt;span class="s1"&gt;'pll'&lt;/span&gt;, which does not exist.
Run &lt;span class="s1"&gt;'pull'&lt;/span&gt; instead &lt;span class="o"&gt;[&lt;/span&gt;y/N]?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Running the suggestion without manual intervention
&lt;/h2&gt;

&lt;p&gt;If you use a numeric value for autoCorrect and Git &lt;strong&gt;finds only 1 valid command&lt;/strong&gt; to fix the typo, then the valid one will be executed without confirmation. 🚀&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;git config &lt;span class="nt"&gt;--global&lt;/span&gt; help.autoCorrect 30
&lt;span class="nv"&gt;$ &lt;/span&gt;git pll
WARNING: You called a Git &lt;span class="nb"&gt;command &lt;/span&gt;named &lt;span class="s1"&gt;'pll'&lt;/span&gt;, which does not exist.
Continuing &lt;span class="k"&gt;in &lt;/span&gt;3 seconds, assuming that you meant &lt;span class="s1"&gt;'pull'&lt;/span&gt;&lt;span class="nb"&gt;.&lt;/span&gt;
Already up to date.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 You can stop its execution using &lt;code&gt;Ctrl + C&lt;/code&gt; or let Git run the assumption.&lt;/p&gt;

&lt;p&gt;The numeric value is the timeout in deciseconds (0.1 seconds), but if you want Git to run the fixed command without any wait you should use &lt;code&gt;immediate&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;🧠 The value &lt;code&gt;0&lt;/code&gt; disables the auto execution and only displays suggestions.&lt;/p&gt;

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

&lt;p&gt;If you combine this autocorrect feature with popular aliases you achieve a customized experience with the command-line to rock on your productivity. 🎸&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.st &lt;span class="s2"&gt;"status --short --branch"&lt;/span&gt;
git config &lt;span class="nt"&gt;--global&lt;/span&gt; alias.co checkout
git config &lt;span class="nt"&gt;--global&lt;/span&gt; help.autoCorrect immediate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Also, you can check &lt;a href="https://dev.to/cloudx/delta-a-new-git-diff-tool-to-rock-your-productivity-2773"&gt;Delta&lt;/a&gt; to improve your Developer Experience using the &lt;code&gt;git diff&lt;/code&gt; command.&lt;/p&gt;

&lt;p&gt;Write in the comments which configuration do you like more to use Git in the terminal! 👇&lt;/p&gt;

</description>
      <category>git</category>
      <category>bash</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Setting Up a Modem in Bridge Mode for a UDM router: A Step-by-Step Guide</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Sat, 29 Jun 2024 11:51:30 +0000</pubDate>
      <link>https://dev.to/cloudx/setting-up-a-modem-in-bridge-mode-for-a-udm-router-a-step-by-step-guide-2c09</link>
      <guid>https://dev.to/cloudx/setting-up-a-modem-in-bridge-mode-for-a-udm-router-a-step-by-step-guide-2c09</guid>
      <description>&lt;p&gt;I bought a Unifi Dream Machine (UDM) and then I called my ISP to change the modem to bridge mode, but they don't provide support for that. You should do it by yourself.&lt;/p&gt;

&lt;p&gt;The simplest possible explanation of what we are doing in this post is: we're going to redirect the fiber signal from the ISP's modem to our UDM using an ethernet port of our router and the PPPoE is going to be resolved in our UDM.&lt;/p&gt;

&lt;p&gt;Here I'm gonna show you a brief guide using my modem RTF8115VW provided by Movistar. I'll assume you know the basics and how to go to the control panel of your ISP's modem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #0, the backup
&lt;/h2&gt;

&lt;p&gt;I couldn't find the backup configuration page on my modem, but if you have luck you have remembered to make a backup. It's nice to have a lifeguard if you make a mistake.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yvrpgprj9azgb0gq4ci.png" class="article-body-image-wrapper"&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-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3yvrpgprj9azgb0gq4ci.png" alt="Backup the router configuration page"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #1- the WAN interface
&lt;/h2&gt;

&lt;p&gt;At first we need to remove the PPPoE WAN interface because the WAN connection is going to be resolved by our UDM.&lt;/p&gt;

&lt;p&gt;💡 Remember to copy both user and password of your PPPoE connection.&lt;/p&gt;

&lt;p&gt;Just select the PPPoE connection type in the grid and click on &lt;code&gt;Delete&lt;/code&gt;. This operation can take up to a minute.&lt;/p&gt;

&lt;p&gt;🧠 Take note of the VLAN of your PPPoE connection, you'll need it later.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjeqlrs07bly9c5x1io3.png" class="article-body-image-wrapper"&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-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffjeqlrs07bly9c5x1io3.png" alt="WAN interfaces list on the modem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #2 - Ingress Filtering
&lt;/h2&gt;

&lt;p&gt;Now we're going to associate one ethernet port of our modem to the WAN port using the proper VLAN. For this, I used the ethernet port #4 for this, but it could be done with any eth port. Just check the &lt;code&gt;eth0.4&lt;/code&gt; and click on &lt;code&gt;delete&lt;/code&gt; to remove the current LAN configuration for this port.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1i62fcge1u1ucu1hyaym.png" class="article-body-image-wrapper"&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-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1i62fcge1u1ucu1hyaym.png" alt="Ingress Filtering list on the modem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then, click the &lt;code&gt;Add&lt;/code&gt; button to associate the port to the WAN interface with the following values:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Order&lt;/code&gt;: Lowest&lt;br&gt;
&lt;code&gt;Ingress Interface&lt;/code&gt;: your selected Ethernet port, &lt;code&gt;eth0.4&lt;/code&gt; in this example.&lt;br&gt;
&lt;code&gt;Associated Bridge&lt;/code&gt;: Here just select the WAN interface.&lt;br&gt;
&lt;code&gt;Ingress Packet&lt;/code&gt;: &lt;code&gt;All&lt;/code&gt; the packages. 😬&lt;br&gt;
&lt;code&gt;VLAN ID&lt;/code&gt;: the VLAN number that you copy from the previous step, 6 in this example.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b1qggwxm3op23yzl8r8.png" class="article-body-image-wrapper"&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-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F3b1qggwxm3op23yzl8r8.png" alt="Completed Ingress Filtering configuration form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then hit &lt;code&gt;Apply&lt;/code&gt;, to complete this step. If you want to know more about ingress filtering you check it &lt;a href="https://en.wikipedia.org/wiki/Ingress_filtering" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #3 - Egress Marking
&lt;/h2&gt;

&lt;p&gt;We're going to mark the packages from the ethernet port number 4, but first we should delete the current configuration for this. Just click the &lt;code&gt;eth0.4&lt;/code&gt; interface and then on the &lt;code&gt;Delete&lt;/code&gt; button.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfccdspeoxyr86c6qr8u.png" class="article-body-image-wrapper"&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-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzfccdspeoxyr86c6qr8u.png" alt="Egress Marking list on the modem"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, we're going to mark the packages from &lt;code&gt;eth0.4&lt;/code&gt; with the following values:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Ingress Interface&lt;/code&gt;: your Ethernet interface, &lt;code&gt;eth0.4&lt;/code&gt; in this example.&lt;br&gt;
&lt;code&gt;Associated Bridge&lt;/code&gt;: select your WAN interface.&lt;br&gt;
&lt;code&gt;Accepted Type&lt;/code&gt;: &lt;code&gt;Tagged&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;VLAN ID Re-Mark&lt;/code&gt;: &lt;code&gt;-1&lt;/code&gt;.&lt;br&gt;
&lt;code&gt;Priority Re-Mark&lt;/code&gt;: &lt;code&gt;1&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc83ylyy7rbfhx13kt4q.png" class="article-body-image-wrapper"&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-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbc83ylyy7rbfhx13kt4q.png" alt="Completed Egress Marking configuration form"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;💡 But is not &lt;code&gt;-1&lt;/code&gt; a misconfiguration value for VLAN ID? 🤔 Well, I understand that this misconfiguration value overrides the &lt;code&gt;6&lt;/code&gt; value that we used in our end. If you know more about this please comment, I want to hear from you! 💬&lt;/p&gt;

&lt;p&gt;If you want to learn more about egress marking you can check the &lt;a href="https://www.cisco.com/c/en/us/td/docs/routers/ios/config/17-x/qos/b-quality-of-service/m_qos-mrkg.html" rel="noopener noreferrer"&gt;QoS Packet Marking&lt;/a&gt; page from Cisco.&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #4 - the Wi-Fi
&lt;/h2&gt;

&lt;p&gt;The most simple step with our modem, just turn off the Wi-Fi radio for 2.4 and 5 GHz and reboot your&lt;/p&gt;

&lt;h2&gt;
  
  
  Step #5 - Ubiquiti
&lt;/h2&gt;

&lt;p&gt;Now, we need to configure the WAN interface in the UDM, for me this is here: &lt;code&gt;/network/default/settings/internet&lt;/code&gt;. Click on the WAN interface and let's configure it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Advanced&lt;/code&gt;: I used &lt;code&gt;Manual&lt;/code&gt; configuration.&lt;br&gt;
&lt;code&gt;VLAN ID&lt;/code&gt;: you should enter the same number used in the modem configuration, &lt;code&gt;6&lt;/code&gt; in this example.&lt;br&gt;
&lt;code&gt;IPv4 Connection&lt;/code&gt;: &lt;code&gt;PPPoE&lt;/code&gt; in the scenario.&lt;br&gt;
&lt;code&gt;Username&lt;/code&gt; and &lt;code&gt;Password&lt;/code&gt;: credentials for your ISP.&lt;br&gt;
&lt;code&gt;DNS Server&lt;/code&gt;: you can use &lt;code&gt;Auto&lt;/code&gt;, or Cloudflare (&lt;code&gt;1.1.1.1&lt;/code&gt;), or Google (&lt;code&gt;8.8.8.8&lt;/code&gt;), or anyone you want.&lt;br&gt;
&lt;code&gt;IPv6 Connection&lt;/code&gt;: I used &lt;code&gt;Disabled&lt;/code&gt; for me.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwmaq5uxqqij0zw29mqm.png" class="article-body-image-wrapper"&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-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftwmaq5uxqqij0zw29mqm.png" alt="WAN interface configuration in the Unifi Dream Machine"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Finally submit your changes and wait for the internet connection to be established in our Unifi network. 🤞&lt;/p&gt;

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

&lt;p&gt;We've learned a few networking concepts that could be used to configure several modems in bridge mode, but be careful because different models or providers could require a little tweaks to make it work! And if you have another router, like a TP-Link, the PPPoE configuration is similar to what I show you in the UDM panel.&lt;/p&gt;

&lt;p&gt;I want to thank Salvathore, because I couldn't make it without his &lt;a href="https://www.youtube.com/watch?v=A8CX1GWHECc" rel="noopener noreferrer"&gt;video tutorial&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I hope you enjoy your UDM as much as I do. 🖖&lt;/p&gt;

</description>
      <category>networking</category>
      <category>network</category>
      <category>unifi</category>
      <category>modem</category>
    </item>
    <item>
      <title>Validate an OpenID Connect JWT using a public key in JWKS</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Thu, 30 Mar 2023 14:45:36 +0000</pubDate>
      <link>https://dev.to/cloudx/validate-an-openid-connect-jwt-using-a-public-key-in-jwks-14jh</link>
      <guid>https://dev.to/cloudx/validate-an-openid-connect-jwt-using-a-public-key-in-jwks-14jh</guid>
      <description>&lt;p&gt;You may have used OpenID Connect in the Front-end, where an IDP (IDentity Provider) authenticates a user, gives you a bunch of tokens in the browser and then you can add the &lt;code&gt;Authorization&lt;/code&gt; header to your HTTP requests to your own Back-End because you trust this IDP. But what happens when you can't find where you can verify the &lt;code&gt;id_token&lt;/code&gt; (or &lt;code&gt;access_token&lt;/code&gt;) using some endpoint in the IDP?&lt;/p&gt;

&lt;p&gt;Well, I found how an &lt;a href="https://curity.io/resources/learn/validating-an-id-token/"&gt;OpenID Connect &lt;code&gt;id_token&lt;/code&gt; should be validated&lt;/a&gt;. It wasn't straightforward in my case: I had to do a lot of research to validate my &lt;code&gt;id_token&lt;/code&gt;. Let's see how to make this easy using Node.js and the &lt;a href="https://github.com/auth0/node-jsonwebtoken"&gt;&lt;code&gt;jsonwebtoken&lt;/code&gt;&lt;/a&gt; npm package made by &lt;a href="https://auth0.com"&gt;Auth0&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Understanding JWT to know how to validate it
&lt;/h2&gt;

&lt;p&gt;A JSON Web Token (JWT) is a string built with 2 JSON objects encoded in &lt;code&gt;base64&lt;/code&gt; and a signature; these parts are joined by a period (&lt;code&gt;.&lt;/code&gt;) with the following structure: &lt;code&gt;&amp;lt;header&amp;gt;.&amp;lt;payload&amp;gt;.&amp;lt;signature&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;💡 This is why a JWT always starts with &lt;code&gt;ey&lt;/code&gt;, because it is the result of encoding &lt;code&gt;{"&lt;/code&gt; using &lt;code&gt;base64&lt;/code&gt;, which is the beginning of any JSON.&lt;/p&gt;

&lt;p&gt;In the &lt;code&gt;header&lt;/code&gt; part we can find which signature algorithm was used in the &lt;a href="https://www.rfc-editor.org/rfc/rfc7515#section-4.1.1"&gt;&lt;code&gt;alg&lt;/code&gt;&lt;/a&gt; parameter (e.g. &lt;em&gt;RS256&lt;/em&gt;) to sign the JWT, and the &lt;a href="https://www.rfc-editor.org/rfc/rfc7515#section-4.1.4"&gt;&lt;code&gt;kid&lt;/code&gt;&lt;/a&gt; parameter tells which &lt;em&gt;Key ID&lt;/em&gt; from the JSON Web Key Set (JWKS) was used for a given token.&lt;/p&gt;

&lt;p&gt;🧠 Remember that when the JWT &lt;code&gt;header&lt;/code&gt; has a &lt;em&gt;Key ID&lt;/em&gt; (&lt;code&gt;kid&lt;/code&gt;), JWKS is used.&lt;/p&gt;

&lt;p&gt;And here is where the problem starts. Where do I find the JWKS to get the public key so we can verify the integrity of this token? 😫&lt;/p&gt;

&lt;h2&gt;
  
  
  The issuer
&lt;/h2&gt;

&lt;p&gt;The issuer is the one who created and signed the JWT, and we can know this by checking the value &lt;code&gt;iss&lt;/code&gt; in the payload of our JWT - using &lt;code&gt;jwt.decode&lt;/code&gt; from &lt;code&gt;jsonwebtoken&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;jwt&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="s1"&gt;jsonwebtoken&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;payload&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your_jwt_here&amp;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;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="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iss&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Alternatively, you can paste your JWT into &lt;a href="https://jwt.io"&gt;https://jwt.io&lt;/a&gt; (don't worry, it's a safe website from Auth0), and &lt;code&gt;iss&lt;/code&gt; is there too!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In OpenID Connect, the issuer should be a URL, but it could just be the name of the IDP. In that case you should read more docs to find where the JWKS URI is. 😭&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For our example we can use &lt;a href="https://sandrino.auth0.com"&gt;https://sandrino.auth0.com&lt;/a&gt; as an issuer, so you can know the OpenID configuration using the well-know URI &lt;a href="https://sandrino.auth0.com/.well-known/openid-configuration"&gt;https://sandrino.auth0.com/.well-known/openid-configuration&lt;/a&gt;, and in the &lt;code&gt;jwks_uri&lt;/code&gt; attribute is where you can find the JWKS for our issuer. You can check this same value in another issuer &lt;a href="https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration"&gt;https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration&lt;/a&gt;, the &lt;code&gt;jwks_uri&lt;/code&gt; is there too! 😉&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;jwt&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="s1"&gt;jsonwebtoken&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;printJwksUri&lt;/span&gt; &lt;span class="o"&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;issuer&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;response&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;fetch&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;issuer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.well-known/openid-configuration`&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;jwks_uri&lt;/span&gt;&lt;span class="p"&gt;}&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;response&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;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="nx"&gt;jwks_uri&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your_jwt&amp;gt;&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="nx"&gt;iss&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;printJwksUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;iss&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We have the JWKS URI programmatically in Node.js! 🥳&lt;/p&gt;

&lt;h2&gt;
  
  
  Verifying the token
&lt;/h2&gt;

&lt;p&gt;Now that we know enough about JWKS, we can write a Node.js code to validate an OpenID token that discovers the JWKS URI if you don't know where it is.&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;promisify&lt;/span&gt;&lt;span class="p"&gt;}&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="s1"&gt;node:util&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;jwt&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="s1"&gt;jsonwebtoken&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;jwksClient&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="s1"&gt;jwks-rsa&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;fetchJwksUri&lt;/span&gt; &lt;span class="o"&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;issuer&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;response&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;fetch&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;issuer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.well-known/openid-configuration`&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;jwks_uri&lt;/span&gt;&lt;span class="p"&gt;}&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;response&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jwks_uri&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;getKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwksUri&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;header&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;callback&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;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwksClient&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;jwksUri&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getSigningKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;header&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;kid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&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;=&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;err&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;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;publicKey&lt;/span&gt; &lt;span class="o"&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;rsaPublicKey&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="cm"&gt;/**
 * Verify an OpenID Connect ID Token
 * @param {string} token - The JWT Token to verify
 */&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;verify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nx"&gt;token&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="na"&gt;iss&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;issuer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;decode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&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;jwksUri&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;fetchJwksUri&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;issuer&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;promisify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwt&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;)(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getKey&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;jwksUri&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;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;&amp;lt;your_jwt&amp;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;verify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;then&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;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;Token verified successfully.&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;catch&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;⚠️ Did you notice that I didn't check who is the issuer? This code will accept any of them, even a malicious one. 😨 To control this just accept issuers from an allowed list.&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;allowedIssuers&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;https://login.microsoftonline.com/common/v2.0&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;https://sandrino.auth0.com&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;fetchJwksUri&lt;/span&gt; &lt;span class="o"&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;issuer&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;allowedIssuers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;issuer&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="nb"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`The issuer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;issuer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt; is not trusted here!`&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;response&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;fetch&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;issuer&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/.well-known/openid-configuration`&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;jwks_uri&lt;/span&gt;&lt;span class="p"&gt;}&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;response&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="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;jwks_uri&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;With this little change you're safe now 🔒&lt;/p&gt;

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

&lt;p&gt;Like I mentioned in &lt;a href="https://dev.to/cloudx/build-a-discoverable-change-password-form-3f9m"&gt;this post&lt;/a&gt;, these well-known URIs are a standard method to get information for specific features, and issuers should implement the  &lt;code&gt;/.well-known/openid-configuration&lt;/code&gt; to integrate it easier to your authentication flow.&lt;/p&gt;

&lt;p&gt;🧠 OpenID Connect is a safe way to authenticate users, but you always have to verify the token's integrity in the Back-End side and check if it was created by a trusted issuer.&lt;/p&gt;

&lt;p&gt;🔑 Remember that this scenario only works with JWKS (when the certificate is pre-distributed to the clients the JWT &lt;code&gt;header&lt;/code&gt; has &lt;a href="https://www.rfc-editor.org/rfc/rfc7515#section-4.1.7"&gt;&lt;code&gt;x5t&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;kid&lt;/code&gt;). You can find examples with &lt;code&gt;public.pem&lt;/code&gt; files in the &lt;a href="https://github.com/auth0/node-jsonwebtoken"&gt;&lt;code&gt;jsonwebtoken&lt;/code&gt;&lt;/a&gt; package.&lt;/p&gt;

&lt;p&gt;🪙 Bonus tip if you use Autenticar service from the Argentinian government, a.k.a AFIP Clave Fiscal, you can use this code to validate the &lt;code&gt;id_token&lt;/code&gt; with the JWKS.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>node</category>
      <category>security</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Running graphic apps in Docker: AWS WorkSpaces</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Thu, 29 Dec 2022 12:19:18 +0000</pubDate>
      <link>https://dev.to/cloudx/running-graphic-apps-in-docker-aws-workspaces-1jj3</link>
      <guid>https://dev.to/cloudx/running-graphic-apps-in-docker-aws-workspaces-1jj3</guid>
      <description>&lt;p&gt;As a Linux user, I need to connect to an Amazon WorkSpace but the client app can only be run in specific old, but maintained, versions of Ubuntu. I've tried with the latest Ubuntu version but it haven't worked, so created a virtual machine with Ubuntu Focal 20.04 but this setup used 14GB of disk space 😵 only to open a window application. This didn't make any sense and also included issues when sharing the clipboard from the host to the VM and then to the virtual desktop hosted in Amazon. 🙄&lt;br&gt;
Then, I remembered that we can run graphic applications using Docker. Let's see how to connect a container to the host's &lt;a href="https://en.wikipedia.org/wiki/X_Window_System"&gt;X Window System&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Creating the Docker image
&lt;/h2&gt;

&lt;p&gt;The following Dockerfile builds the image using Ubuntu Focal 20.04, and installs the Amazon WorkSpace client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:focal&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DEBIAN_FRONTEND=noninteractive&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libusb-1.0-0 wget xauth &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  wget &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; - https://workspaces-client-linux-public-key.s3-us-west-2.amazonaws.com/ADB332E7.asc | apt-key add - &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [arch=amd64] https://d3nt0h4h6pmmc4.cloudfront.net/ubuntu focal main"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/apt/sources.list.d/amazon-workspaces-clients.list &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt remove &lt;span class="nt"&gt;-y&lt;/span&gt; wget &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; workspacesclient &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;touch&lt;/span&gt; /root/.Xauthority &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 &lt;code&gt;libusb-1.0-0&lt;/code&gt; is a dependency of Amazon WorkSpaces.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;DEBIAN_FRONTEND=noninteractive&lt;/code&gt; environment is used to skip the Ubuntu’s prompt asking for our time zone.&lt;/p&gt;

&lt;p&gt;Build the image with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; workspace &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Docker image's size is 1.2GB, a reduction of 91% in comparison to the 14GB of the virtual machine.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting the container
&lt;/h2&gt;

&lt;p&gt;Now, we can run the container!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; ws &lt;span class="nt"&gt;--network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host &lt;span class="nt"&gt;-e&lt;/span&gt; DISPLAY &lt;span class="nt"&gt;-v&lt;/span&gt; /tmp/.X11-unix &lt;span class="nt"&gt;-it&lt;/span&gt; workspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We need to share the &lt;code&gt;.X11-unix/&lt;/code&gt; directory where the connection &lt;a href="https://en.wikipedia.org/wiki/Unix_domain_socket"&gt;socket&lt;/a&gt; is located, where &lt;code&gt;DISPLAY&lt;/code&gt; is the name of the host's display.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;--network=host&lt;/code&gt; argument makes the container use the same host's network so the container does not get its own IP address. This avoids the container being network isolated. 🪛&lt;/p&gt;

&lt;h2&gt;
  
  
  Starting the GUI application
&lt;/h2&gt;

&lt;p&gt;Before starting the app we must authenticate the X Window System of the container with the host.&lt;/p&gt;

&lt;p&gt;Get the authorization entry of the host using the &lt;code&gt;xauth&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xauth list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the output of the command and run this in the container (replacing the placeholders with the values from the clipboard):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;xauth add &amp;lt;display_name&amp;gt; &amp;lt;protocol_name&amp;gt; &amp;lt;hex_key&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you are able to start GUI applications inside the container! 🎉&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/opt/workspacesclient/workspacesclient
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Alternative: use VNC instead of X11
&lt;/h2&gt;

&lt;p&gt;We need to make a few changes to run a VNC server in the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:focal&lt;/span&gt;

&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; DEBIAN_FRONTEND=noninteractive&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; libusb-1.0-0 gpg wget x11vnc xvfb &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  wget &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="nt"&gt;-O&lt;/span&gt; - https://workspaces-client-linux-public-key.s3-us-west-2.amazonaws.com/ADB332E7.asc | apt-key add - &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"deb [arch=amd64] https://d3nt0h4h6pmmc4.cloudfront.net/ubuntu focal main"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/apt/sources.list.d/amazon-workspaces-clients.list &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt remove &lt;span class="nt"&gt;-y&lt;/span&gt; wget &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; workspacesclient &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"exec /opt/workspacesclient/workspacesclient"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ~/.xinitrc &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;chmod&lt;/span&gt; +x ~/.xinitrc

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["x11vnc", "-create", "-forever"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;x11vnc&lt;/code&gt; command will start an X11 session in the container launching the application specified in the &lt;code&gt;~/.xinitrc&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; workspace &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, we can run the container:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt; ws &lt;span class="nt"&gt;-it&lt;/span&gt; workspace
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But how can we know the IP address of the container? Just check it with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker inspect &lt;span class="nt"&gt;--format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}'&lt;/span&gt; ws
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can connect to the VNC server. 🔌&lt;/p&gt;

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

&lt;p&gt;We can run graphic applications that work in specific Linux distros using Docker 🐋, by connecting directly to our X Window System. This uses less memory and disk space than a VM.&lt;/p&gt;

&lt;p&gt;You can get the same in Windows as the host using &lt;a href="https://sourceforge.net/projects/xming/"&gt;Xming&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;xauth&lt;/code&gt; could be used in conjunction with SSH to run remote GUI apps 😉 in our local, but that's for another post.&lt;/p&gt;

&lt;p&gt;Alternatively, you can start a VNC server in a container allowing more flexibility either if you use macOS, or if you can't connect using X11 because you're using &lt;a href="https://en.wikipedia.org/wiki/Wayland_(display_server_protocol)"&gt;Wayland&lt;/a&gt;. But this doesn't feel like a native window on your OS.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>docker</category>
      <category>aws</category>
      <category>security</category>
    </item>
    <item>
      <title>Connecting through OpenVPN with deprecated ciphers, using Docker</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Mon, 14 Nov 2022 12:33:19 +0000</pubDate>
      <link>https://dev.to/cloudx/connecting-through-openvpn-with-deprecated-ciphers-using-docker-1hmk</link>
      <guid>https://dev.to/cloudx/connecting-through-openvpn-with-deprecated-ciphers-using-docker-1hmk</guid>
      <description>&lt;p&gt;Caution! This is a developer horror story 👻, Halloween 2022 comes with a nightmare for those who use a VPN. OpenSSL released the version &lt;code&gt;3.0.7&lt;/code&gt; as the latest stable, if you compare that we're talking about a jump from &lt;code&gt;1.1.1&lt;/code&gt; to &lt;code&gt;3.0.7&lt;/code&gt; we can think that any already deprecated protocol was removed to guarantee the security of our systems! And, it happened.&lt;br&gt;
Arch Linux, with its philosophy of using the latest stable release of everything, applied the OpenSSL v3 on Saturday morning November 5.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: Treating option '--ncp-ciphers' as  '--data-ciphers' (renamed in OpenVPN 2.5).&lt;br&gt;
WARNING: Compression for receiving enabled. Compression has been used in the past to break encryption. Sent packets are not compressed unless "allow-compression yes" is also set.&lt;br&gt;
--cipher is not set. Previous OpenVPN version defaulted to BF-CBC as fallback when cipher negotiation failed in this case. If you need this fallback please add '--data-ciphers-fallback BF-CBC' to your configuration and/or add BF-CBC to --data-ciphers.&lt;br&gt;
Unsupported cipher in --data-ciphers: BF-CBC&lt;br&gt;
Options error: NCP cipher list contains unsupported ciphers or is too long.&lt;br&gt;
Use --help for more information.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Surprise! You can't use the &lt;code&gt;BF-CBC&lt;/code&gt; cipher on OpenVPN anymore, because it was removed from OpenSSL itself; OpenVPN plans to remove it on 2.7 but we're currently in 2.5.8 at the moment.&lt;/p&gt;

&lt;p&gt;Downgrading the &lt;code&gt;openssl&lt;/code&gt; package is a possible solution but not the best. Should I move my development environment to a virtual machine? 🙀 Change to an old Ubuntu version? Not for me. Here is when the hero 🦸 of this story appears to save us, and its name is Docker 🐳.&lt;/p&gt;

&lt;p&gt;💡 You can receive an alternative error message in Ubuntu or other Linux distributions:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;OpenVPN 2.5.5 x86_64-pc-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] built on Jul 14 2022&lt;br&gt;
library versions: OpenSSL 3.0.2 15 Mar 2022, LZO 2.10&lt;br&gt;
WARNING: --ns-cert-type is DEPRECATED.  Use --remote-cert-tls instead.&lt;br&gt;
OpenSSL: error:0A00018E:SSL routines::ca md too weak&lt;br&gt;
Cannot load inline certificate file&lt;br&gt;
Exiting due to fatal error&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  The solution
&lt;/h2&gt;

&lt;p&gt;The containers can use the same network of the host, this avoids the container being network isolated, and the VPN tunnel is shared with our host system.&lt;/p&gt;
&lt;h3&gt;
  
  
  Creating the image
&lt;/h3&gt;

&lt;p&gt;For this example I'll use a simple &lt;code&gt;ovpn&lt;/code&gt; file, but with a few tweaks I'm sure this will work for you too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; ubuntu:focal&lt;/span&gt;

&lt;span class="k"&gt;RUN &lt;/span&gt;apt update &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  apt &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-y&lt;/span&gt; openvpn &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;  &lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; /var/lib/apt/lists/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; profile.ovpn /etc/openvpn/client/&lt;/span&gt;

&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["openvpn", "/etc/openvpn/client/profile.ovpn"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We based our image on Ubuntu 20.04 LTS (Focal Fossa), just install the &lt;code&gt;openvpn&lt;/code&gt; package and copy your OpenVPN configuration file inside. Maybe you need a &lt;code&gt;.p12&lt;/code&gt;, &lt;code&gt;.key&lt;/code&gt;, or &lt;code&gt;.crt&lt;/code&gt; files just to copy them too.&lt;/p&gt;

&lt;p&gt;🧠 We can't use Ubuntu 22.04 LTS (Jammy Jellyfish) because it received the update to OpenSSL 3.0.2.&lt;/p&gt;

&lt;p&gt;Build the image with the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; openvpn &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Starting the container
&lt;/h3&gt;

&lt;p&gt;Now, we can run the container!&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;docker run &lt;span class="nt"&gt;--name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;vpn &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cap-add&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;NET_ADMIN &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;host &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--device&lt;/span&gt; /dev/net/tun &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-it&lt;/span&gt; openvpn
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The container needs the Linux kernel capability of network administration to create a VPN tunnel with the &lt;code&gt;--cap-add=NET_ADMIN&lt;/code&gt; argument. Also &lt;code&gt;--device /dev/net/tun&lt;/code&gt; gives access to the tunnel device of the host.&lt;/p&gt;

&lt;p&gt;⚠️ Don't give kernel capabilities or access to devices if you don't trust the publisher.&lt;/p&gt;

&lt;p&gt;Once started, the container could ask for your credentials (username and password) to establish the VPN connection and it's done! 🎉&lt;/p&gt;

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

&lt;p&gt;Some changes can't be applied in enterprise environments without analyzing all possible scenarios, and the change of this cipher could take some time. That's where Docker could help us to continue using old software in a controlled way for specific tasks without compromising the security of our system.&lt;/p&gt;

&lt;p&gt;I hope you don't get scared with this story, but Super-Docker saves the day, one more time. Tell me in the comments if this helped you, or if you found another solution.&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>docker</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Querying installed packages with npm query</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Wed, 02 Nov 2022 17:39:20 +0000</pubDate>
      <link>https://dev.to/cloudx/querying-installed-packages-with-npm-query-389f</link>
      <guid>https://dev.to/cloudx/querying-installed-packages-with-npm-query-389f</guid>
      <description>&lt;p&gt;Do you know if you have any package that makes use of &lt;code&gt;postinstall&lt;/code&gt; scripts? Do you have a package installed twice? Since version 8.18.0 we can use &lt;a href="https://docs.npmjs.com/cli/v8/commands/npm-query"&gt;&lt;code&gt;npm query&lt;/code&gt;&lt;/a&gt; to find packages in the &lt;code&gt;node_modules&lt;/code&gt; directory matching a specific query.&lt;br&gt;
This is helpful in identifying possible issues and fixing them.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why is a postinstall script important?
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;postinstall&lt;/code&gt; script allows a package to run code when it's installed and this could be used in a malicious way. The npm security team receives &lt;a href="https://docs.npmjs.com/reporting-malware-in-an-npm-package"&gt;reports of malware&lt;/a&gt; in the registry and works to keep the ecosystem safe, but did you check which dependencies are running in &lt;code&gt;postinstall&lt;/code&gt; scripts? Do you trust them?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s2"&gt;":attr(scripts, [postinstall])"&lt;/span&gt; | jq &lt;span class="s1"&gt;'map(.name)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Review the list of dependencies. then to uninstall these packages with a &lt;code&gt;postinstall&lt;/code&gt; script use the following command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s2"&gt;":attr(scripts, [postinstall])"&lt;/span&gt; | jq &lt;span class="s1"&gt;'map(.name) | join("\n")'&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; | xargs &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; npm uninstall &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 The &lt;code&gt;jq&lt;/code&gt; program is essential to filter data in JSON format.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finding duplicate packages
&lt;/h2&gt;

&lt;p&gt;Sometimes we find some errors on the DevTools' console when React or Angular is being loaded twice on our website. This happens when a dependency is incompatible with the &lt;a href="https://semver.org"&gt;semver&lt;/a&gt; of the same package declared on our &lt;code&gt;package.json&lt;/code&gt;. E.g. We declared &lt;code&gt;react@^18.2.0&lt;/code&gt; in our &lt;code&gt;package.json&lt;/code&gt; and some dependency has &lt;code&gt;react@^16.8.0 || ^17.0.0&lt;/code&gt; as dependency in their own &lt;code&gt;package.json&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We can find every installed copy of a specific package using:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s1"&gt;'#react'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or, if we're looking for old versions of React&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s1"&gt;'#react:semver(&amp;lt;18.0.0)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🧠 Remember that the selectors for &lt;code&gt;npm query&lt;/code&gt; follow the &lt;a href="https://www.w3schools.com/cssref/css_selectors.asp"&gt;CSS selectors format&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Check the dependency licenses
&lt;/h2&gt;

&lt;p&gt;Perhaps you can't use code with a specific license in your app. You can check the variety of licenses installed in your &lt;code&gt;node_modules&lt;/code&gt; printing a dedupe list with this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s1"&gt;'*'&lt;/span&gt; | jq &lt;span class="s1"&gt;'.[] | select(.license | type == "string") | .license'&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;🎁 Some packages, like &lt;code&gt;rimraf&lt;/code&gt;, declare an object in the license field of the &lt;code&gt;package.json&lt;/code&gt;, so you can use this example for these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s1"&gt;'*'&lt;/span&gt; | jq &lt;span class="s1"&gt;'.[] | select(.license | type == "object") | .license'&lt;/span&gt;
&lt;span class="c"&gt;# or using attr selector&lt;/span&gt;
npm query &lt;span class="s1"&gt;':attr(license, [url])'&lt;/span&gt; | jq &lt;span class="s1"&gt;'map(.license)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then you can look up packages with specific licenses, or without a license.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s1"&gt;'[license=MIT], [license=ISC], [license=]'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Other usages
&lt;/h2&gt;

&lt;p&gt;You can create the query that matches your needs, but we can review a few additional scenarios.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dependencies from Git
&lt;/h3&gt;

&lt;p&gt;You can install dependencies from Git branches or tags instead of the &lt;em&gt;npm registry&lt;/em&gt; and then you can check which of them are installed in your project.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s1"&gt;':type(git)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Who needs these packages? The &lt;code&gt;npm why&lt;/code&gt; command can tell us:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s2"&gt;":type(git)"&lt;/span&gt; | jq &lt;span class="s1"&gt;'map(.name) | join("\n")'&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; | xargs &lt;span class="nt"&gt;-I&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; npm why &lt;span class="o"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  The peerDependencies
&lt;/h3&gt;

&lt;p&gt;If you run your app and you get an error for a missing dependency, check if a peer dependency of your direct dependencies is required. Maybe you should add it in your &lt;code&gt;package.json&lt;/code&gt; file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm query &lt;span class="s1"&gt;':root &amp;gt; * &amp;gt; .peer'&lt;/span&gt; | jq &lt;span class="s1"&gt;'map(.name)'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;📚 You could have example pages for &lt;a href="https://github.com/tldr-pages/tldr/blob/main/pages/common/npm-query.md"&gt;&lt;code&gt;npm-query&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/tldr-pages/tldr/blob/main/pages/common/jq.md"&gt;&lt;code&gt;jq&lt;/code&gt;&lt;/a&gt; in your terminal for quick usage thanks to the &lt;a href="https://github.com/tldr-pages/tldr"&gt;tldr-pages&lt;/a&gt; open source project.&lt;/p&gt;

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

&lt;p&gt;Using &lt;code&gt;npm query&lt;/code&gt; in conjunction with &lt;code&gt;jq&lt;/code&gt; you can check some constraints in your dependencies to make sure you don't have duplicate dependencies, or licenses that are incompatible with your app or company's policy.&lt;/p&gt;

&lt;p&gt;I hope this helps you to resolve your issues with npm packages. Tell me in the comments which queries you found useful.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>node</category>
      <category>react</category>
    </item>
    <item>
      <title>Improve your website performance using Partytown type in script tags</title>
      <dc:creator>Axel Navarro</dc:creator>
      <pubDate>Mon, 03 Oct 2022 12:33:27 +0000</pubDate>
      <link>https://dev.to/cloudx/improve-your-website-performance-using-partytown-type-in-script-tags-70b</link>
      <guid>https://dev.to/cloudx/improve-your-website-performance-using-partytown-type-in-script-tags-70b</guid>
      <description>&lt;p&gt;Can we use Google Tag Manager from a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API" rel="noopener noreferrer"&gt;web worker&lt;/a&gt;? Is &lt;code&gt;&amp;lt;script type="text/partytown"&amp;gt;&lt;/code&gt; a new standard? Let's see what this new &lt;code&gt;script&lt;/code&gt; type is and how we can easily move workloads from the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Main_thread" rel="noopener noreferrer"&gt;main thread&lt;/a&gt; to a worker with an example using GTM and Next.js. After that you can implement this with vanilla JS and other third party scripts like Facebook Pixel, Mixpanel, Amplitude, etc.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is Partytown 🎉?
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://partytown.builder.io" rel="noopener noreferrer"&gt;Partytown&lt;/a&gt; is a library that helps us to relocate scripts into a web worker creating communication channels, like &lt;code&gt;dataLayer.push()&lt;/code&gt; which is used in GTM. If GTM was loaded into a worker, every call to &lt;code&gt;dataLayer.push()&lt;/code&gt; should be forwarded from the main thread to the worker thread, and Partytown solves that automatically! Also, it allows code executed from the web worker to access DOM &lt;em&gt;synchronously&lt;/em&gt;, so third-party scripts can run exactly how they were coded and without any alterations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhpqei2iiyz9f1vrkurn.png" class="article-body-image-wrapper"&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-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnhpqei2iiyz9f1vrkurn.png" alt="Without Partytown and With Partytown: Your code and third-party code compete for main thread resources" width="800" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating script tags for workers
&lt;/h3&gt;

&lt;p&gt;To run code in the worker just use &lt;code&gt;type="text/partytown"&lt;/code&gt;. The browser won’t recognize that type ignoring the &lt;code&gt;script&lt;/code&gt; tag and then Partytown will load that &lt;code&gt;script&lt;/code&gt; inside a web worker. This works for inline code or files.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/partytown"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;/* GTM Script Here */&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/partytown"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"https://example.com/analytics.js"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Where is the &lt;code&gt;forward&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;To &lt;code&gt;forward&lt;/code&gt; a function call to the worker we just need to add the configuration to the &lt;code&gt;window&lt;/code&gt;  object before the inlined Partytown &lt;code&gt;script&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;script&amp;gt;&lt;/span&gt;
  &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;partytown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;forward&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;dataLayer.push&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="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/partytown"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cm"&gt;/* GTM script here */&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you can use &lt;code&gt;window.dataLayer.push()&lt;/code&gt; from the main thread. 🙌&lt;/p&gt;

&lt;h2&gt;
  
  
  Using workers in Next.js
&lt;/h2&gt;

&lt;p&gt;Setting up Partytown in a Next.js website is very easy! We just need Next.js &lt;a href="https://github.com/vercel/next.js/releases/tag/v12.1.1" rel="noopener noreferrer"&gt;v12.1.1&lt;/a&gt; or later.&lt;/p&gt;

&lt;p&gt;🧠 Remember that this is an experimental feature in Next.js and it’s not covered by semver. 🙀&lt;/p&gt;

&lt;p&gt;First, we should enable the experimental flag in the &lt;code&gt;next.config.js&lt;/code&gt; file.&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;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;experimental&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;nextScriptWorkers&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;p&gt;And install the Partytown package.&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;--save-dev&lt;/span&gt; @builder.io/partytown
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Next.js workers don't require extra configurations, but we should &lt;code&gt;forward&lt;/code&gt; the &lt;code&gt;dataLayer.push&lt;/code&gt; for this example. We need to include this configuration in a &lt;code&gt;&amp;lt;Head&amp;gt;&lt;/code&gt; component inside a custom &lt;code&gt;_document.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Html&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Head&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Main&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;NextScript&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;next/document&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;Document&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Html&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Head&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;script&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;partytown&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;config&lt;/span&gt;
        &lt;span class="nx"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
          &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
            partytown = {
              lib: '/_next/static/~partytown/',
              debug: true,
              forward: ['dataLayer.push']
            };
          `&lt;/span&gt;
        &lt;span class="p"&gt;}}&lt;/span&gt;
      &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Head&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Main&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;NextScript&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/body&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Html&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;Document&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;💡 The &lt;code&gt;debug: true&lt;/code&gt; setting gives us nice info in the DevTools' console.&lt;/p&gt;

&lt;p&gt;Finally, we can add the GTM script in our &lt;code&gt;_app.js&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;Script&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;next/script&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;MyApp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;pageProps&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="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Script&lt;/span&gt;
      &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;gtm-script&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;strategy&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worker&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;
      &lt;span class="nx"&gt;dangerouslySetInnerHTML&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="na"&gt;__html&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`/* GTM script here */`&lt;/span&gt;
      &lt;span class="p"&gt;}}&lt;/span&gt;
    &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;{...&lt;/span&gt;&lt;span class="nx"&gt;pageProps&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;MyApp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;id&lt;/code&gt; property is required for &lt;em&gt;inline scripts&lt;/em&gt; in order to let Next.js track and optimize the script.&lt;/p&gt;

&lt;p&gt;And it's done! Your GTM runs in a worker that you can communicate with using the &lt;code&gt;dataLayer.push&lt;/code&gt; - as always.&lt;/p&gt;

&lt;p&gt;You can find installation instructions for &lt;a href="https://partytown.builder.io/react" rel="noopener noreferrer"&gt;React&lt;/a&gt;, &lt;a href="https://partytown.builder.io/angular" rel="noopener noreferrer"&gt;Angular&lt;/a&gt;, &lt;a href="https://partytown.builder.io/nuxt" rel="noopener noreferrer"&gt;Nuxt&lt;/a&gt; and &lt;a href="https://partytown.builder.io/integrations" rel="noopener noreferrer"&gt;more&lt;/a&gt;.&lt;/p&gt;

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

&lt;p&gt;Too much JavaScript can block DOM construction, delaying how quickly pages can render. Reducing JavaScript in our main thread is a good strategy to reduce the time until interaction. This will free up main thread resources so they can be used for the primary web app execution, e.g. network requests.&lt;/p&gt;

&lt;p&gt;With Partytown we can use read and write main thread DOM operations &lt;em&gt;synchronously&lt;/em&gt; from within a web worker; I wonder if this can be used in a microfrontend application to increase the responsiveness of our website.&lt;/p&gt;

&lt;p&gt;I hope you find this helpful If you came here looking for performance improvements for your website. Do you have production experience with workers? Have you used Partytown before? Tell me in the comments.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>webdev</category>
      <category>performance</category>
      <category>react</category>
    </item>
  </channel>
</rss>
