<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Beyond Syntax]]></title><description><![CDATA[Code, Life, and Debugging the Human Experience.]]></description><link>https://hamidreza.tech</link><generator>RSS for Node</generator><lastBuildDate>Sat, 06 Jun 2026 17:36:28 GMT</lastBuildDate><atom:link href="https://hamidreza.tech/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Agentic Engineering Manifesto — Practical Principles for AI-Driven Development]]></title><description><![CDATA[The full manifesto is available here: Agentic Engineering Manifesto.

AI agents are changing how we build software, but I do not think the important change is only that code gets written faster.
The b]]></description><link>https://hamidreza.tech/agentic-engineering-manifesto</link><guid isPermaLink="true">https://hamidreza.tech/agentic-engineering-manifesto</guid><category><![CDATA[AI]]></category><category><![CDATA[agentic AI]]></category><category><![CDATA[Software Engineering]]></category><dc:creator><![CDATA[Hamidreza Mahdavipanah]]></dc:creator><pubDate>Sat, 23 May 2026 18:21:08 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>The full manifesto is available here: <a href="https://mahdavipanah.github.io/agentic-engineering-manifesto/">Agentic Engineering Manifesto</a>.</p>
</blockquote>
<p>AI agents are changing how we build software, but I do not think the important change is only that code gets written faster.</p>
<p>The bigger change is where engineering judgment moves.</p>
<p>When working with agents, the human role becomes less about typing every line of code and more about shaping the problem, giving useful context, choosing the direction, reviewing the approach, and making sure there is a good feedback loop.</p>
<p>That is why I wrote the Agentic Engineering Manifesto.</p>
<p>The main idea is simple: agents are useful, but they need to be guided in the right way. A good prompt is not just a task description. It should tell the agent where to look, what matters, how to think about the change, and how to verify the result.</p>
<p>I also think codebases now need to be easier for agents to navigate. Names, structure, examples, tests, and discoverable patterns matter more when another participant in the development process is searching and reasoning through the codebase.</p>
<p>Another important shift is refactoring. Since agents can make broad changes more cheaply, we should be more willing to ask whether a feature is hard because the feature is actually hard, or because the current architecture is resisting it.</p>
<p>The manifesto is my attempt to write down these ideas in a small and clear form.</p>
<p>It is not about trusting AI blindly. It is almost the opposite. Agentic engineering needs planning, verification, tests, build output, logs, and review. The better the feedback loop, the more useful the agent becomes.</p>
<p>I see this as an early version of how software teams will work with agents: humans leading design and direction, agents taking more ownership during implementation, and both depending on codebases that are easy to understand, change, and verify.</p>
]]></content:encoded></item><item><title><![CDATA[A Practical Specification for Roles, Permissions, and Policy Decisions]]></title><description><![CDATA[The full specification is available here: Authorization Model Specification.

Authorization logic often starts simple.
At first, a role like admin, editor, or viewer is enough. Then the product grows.]]></description><link>https://hamidreza.tech/a-practical-specification-for-roles-permissions-and-policy-decisions</link><guid isPermaLink="true">https://hamidreza.tech/a-practical-specification-for-roles-permissions-and-policy-decisions</guid><category><![CDATA[authorization]]></category><category><![CDATA[rbac]]></category><category><![CDATA[Auth0]]></category><category><![CDATA[authentication]]></category><dc:creator><![CDATA[Hamidreza Mahdavipanah]]></dc:creator><pubDate>Sun, 17 May 2026 12:12:12 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>The full specification is available here: <a href="https://mahdavipanah.github.io/authorization-model/">Authorization Model Specification</a>.</p>
</blockquote>
<p>Authorization logic often starts simple.</p>
<p>At first, a role like <code>admin</code>, <code>editor</code>, or <code>viewer</code> is enough. Then the product grows. You add organizations, projects, machine clients, service accounts, field-level rules, exceptions, audit logs, and suddenly the question "can this principal do this action?" is answered differently in different parts of the codebase.</p>
<p>This specification is my attempt to make that question explicit, portable, and easy to evaluate.</p>
<p>The goal is not to define authentication, token issuance, identity provisioning, or transport security. Those are separate concerns. This spec focuses only on authorization decisions: how roles, permissions, scopes, and policy evaluation should work together.</p>
<hr />
<h2>The Core Idea</h2>
<p>The model is built around a small chain:</p>
<pre><code class="language-plaintext">Principal -&gt; Role -&gt; Permission -&gt; allow | deny
</code></pre>
<p>A principal can be a human user, a service account, or a machine-to-machine client. A role is a named bundle of permission statements, such as <code>auditor</code>, <code>editor</code>, or <code>billing-admin</code>.</p>
<p>Roles are not magical. Their names do not grant authority by themselves. A role only matters because of the permission statements it contains, and a principal only receives authority through an explicit binding:</p>
<pre><code class="language-plaintext">(principal, role, scope)
</code></pre>
<p>This is important because it keeps the model auditable. If someone has access, there should be a concrete binding and a concrete permission statement that explains why.</p>
<hr />
<h2>The Design Principles</h2>
<p>The specification is based on five principles:</p>
<p><strong>Default-deny</strong>: if nothing explicitly allows an action, the answer is deny.</p>
<p><strong>Deny-overrides</strong>: if both an allow and a deny match the same request, the deny wins.</p>
<p><strong>Explicit over implicit</strong>: authority is never inferred from role names, group names, or conventions.</p>
<p><strong>Decision and enforcement separation</strong>: application code should ask a Policy Decision Point (PDP) for a decision. It should not spread authorization rules across controllers, services, and handlers.</p>
<p><strong>Auditability</strong>: decisions should be loggable with enough context to reconstruct what happened.</p>
<p>These principles make the model predictable. There is no hidden privilege, no role-name guessing, and no ambiguous conflict resolution.</p>
<hr />
<h2>Permission Strings</h2>
<p>Permission statements are serialized as compact strings:</p>
<pre><code class="language-plaintext">&lt;organization&gt;:&lt;service&gt;/&lt;resource&gt;[:&lt;field&gt;[:&lt;resource_id&gt;]]/&lt;effect&gt;/&lt;action&gt;
</code></pre>
<p>For example:</p>
<pre><code class="language-plaintext">acme:api/suppliers/allow/update
</code></pre>
<p>This means: in the <code>acme</code> organization, for the <code>api</code> service, allow updating <code>suppliers</code>.</p>
<p>The format also supports field-level and instance-level rules:</p>
<pre><code class="language-plaintext">acme:api/contacts:email/allow/read
acme:api/suppliers:*:12345/deny/read
</code></pre>
<p>The first statement allows reading only the <code>email</code> field of contacts. The second denies reading supplier <code>12345</code>.</p>
<p>Wildcards are supported too:</p>
<pre><code class="language-plaintext">acme:api/suppliers/allow/*
acme:api/suppliers/deny/delete
</code></pre>
<p>Together, these allow every action on suppliers except deletion.</p>
<p>The string format is intentionally compact enough to be transported in places like JWT claims, while still being readable for operators and auditors.</p>
<hr />
<h2>How Evaluation Works</h2>
<p>The evaluator receives a request:</p>
<pre><code class="language-plaintext">(principal, action, resource_uri)
</code></pre>
<p>Then it evaluates the principal's effective permission set in three steps.</p>
<p>First, it keeps only the permission statements that match the request. A segment matches if it is exactly equal to the request segment or if it is <code>*</code>.</p>
<p>Second, if any matching statement has <code>effect = deny</code>, the result is deny.</p>
<p>Third, if at least one matching statement has <code>effect = allow</code> and no deny matched, the result is allow. Otherwise, the result is deny.</p>
<p>In pseudocode:</p>
<pre><code class="language-python">def evaluate(request, permissions):
    applicable = [p for p in permissions if matches(p, request)]

    if any(p.effect == "deny" for p in applicable):
        return Decision.DENY

    if any(p.effect == "allow" for p in applicable):
        return Decision.ALLOW

    return Decision.DENY
</code></pre>
<p>One important detail: the evaluator is specificity-agnostic. A specific deny does not beat a wildcard allow because it is more specific. It wins because all denies override all allows.</p>
<p>For example:</p>
<pre><code class="language-plaintext">acme:api/suppliers/allow/read
acme:api/suppliers:*:12345/deny/read
</code></pre>
<p>Reading suppliers is generally allowed, but reading supplier <code>12345</code> is denied.</p>
<hr />
<h2>What This Spec Is For</h2>
<p>This specification is useful for teams building a Policy Decision Point, integrating a Policy Enforcement Point, or simply trying to make authorization rules easier to audit.</p>
<p>It gives you:</p>
<ul>
<li><p>a portable permission string format</p>
</li>
<li><p>clear role and binding semantics</p>
</li>
<li><p>default-deny behavior</p>
</li>
<li><p>deny-overrides conflict handling</p>
</li>
<li><p>a small evaluation algorithm</p>
</li>
<li><p>decision logging requirements</p>
</li>
</ul>
<p>The main benefit is consistency. Instead of each service inventing its own interpretation of roles and permissions, services can ask the same kind of question and receive the same kind of answer.</p>
<p>Authorization should be boring, predictable, and explainable. That is what this spec is designed to support.</p>
<p>You can read the complete spec, including the grammar, validation rules, examples, and versioning notes, at <a href="https://mahdavipanah.github.io/authorization-model/">mahdavipanah.github.io/authorization-model</a>.</p>
]]></content:encoded></item><item><title><![CDATA[Javascript's Tricky "This" Keyword and Why Arrow Functions Matter]]></title><description><![CDATA[JavaScript's flexibility and dynamic nature make it a powerful language, but certain feature - like the this keyword - can lead to confusion, especially within nested functions. Understanding how this behaves is crucial for writing predictable and ma...]]></description><link>https://hamidreza.tech/javascripts-this-keyword-and-arrow-functions</link><guid isPermaLink="true">https://hamidreza.tech/javascripts-this-keyword-and-arrow-functions</guid><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[Programming Tips]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Hamidreza Mahdavipanah]]></dc:creator><pubDate>Tue, 18 Mar 2025 14:28:41 GMT</pubDate><content:encoded><![CDATA[<p>JavaScript's flexibility and dynamic nature make it a powerful language, but certain feature - like the <code>this</code> keyword - can lead to confusion, especially within nested functions. Understanding how <code>this</code> behaves is crucial for writing predictable and maintainable code.</p>
<h3 id="heading-the-this-keyword-in-javascript">The <code>this</code> Keyword in JavaScript</h3>
<p>In JavaScript, the value of <code>this</code> is determined by how a function is invoked, not where it's defined. When a regular function is called as a method of an object (<code>obj.method()</code>), <code>this</code> refers to that object. However, when a function is invoked standalone (<code>func()</code>), <code>this</code> typically refers to the global object (in non-strict mode) or is <code>undefined</code> (in strict mode) (<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this">MDN documentation</a>).</p>
<h3 id="heading-common-pitfall-losing-the-context">Common Pitfall: Losing the Context</h3>
<p>Consider the following example:</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyClass</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.factor = <span class="hljs-number">2</span>;
  }

  methodA(value) {
    <span class="hljs-keyword">return</span> value * <span class="hljs-built_in">this</span>.factor;
  }

  methodB() {
    <span class="hljs-keyword">return</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>].map(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">value</span>) </span>{
      <span class="hljs-keyword">const</span> newValue = value + <span class="hljs-number">1</span>;
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.methodA(newValue); <span class="hljs-comment">// TypeError: Cannot read properties of undefined (reading 'methodA')</span>
    });
  }
}
</code></pre>
<p>In this example, the callback passed to <code>map()</code> is a regular function, causing <code>this</code> to be <code>undefined</code> inside it. This results in a runtime error.</p>
<h3 id="heading-arrow-functions-a-concise-solution">Arrow Functions: A Concise Solution</h3>
<p>Arrow functions provide a concise syntax and lexically bind <code>this</code>, meaning they inherit <code>this</code> from their surrounding scope (<a target="_blank" href="https://google.github.io/styleguide/jsguide.html#features-functions-arrow-functions">Google JS Style Guide</a>):</p>
<pre><code class="lang-javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyClass</span> </span>{
  <span class="hljs-keyword">constructor</span>() {
    <span class="hljs-built_in">this</span>.factor = <span class="hljs-number">2</span>;
  }

  methodA(value) {
    <span class="hljs-keyword">return</span> value * <span class="hljs-built_in">this</span>.factor;
  }

  methodB() {
    <span class="hljs-keyword">return</span> [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>].map(<span class="hljs-function">(<span class="hljs-params">value</span>) =&gt;</span> {
      <span class="hljs-keyword">const</span> newValue = value + <span class="hljs-number">1</span>;
      <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>.methodA(newValue);
    });
  }
}
</code></pre>
<p>With arrow functions, the context of <code>this</code> remains bound to the instance of <code>MyClass</code>, preventing the earlier error.</p>
<h3 id="heading-best-practices">Best Practices</h3>
<p>To avoid confusion and potential errors with <code>this</code>, consider these guidelines:</p>
<ul>
<li><p><strong>Prefer Arrow Functions for Nested Functions:</strong> Arrow functions simplify the scoping of <code>this</code> in nested functions.</p>
</li>
<li><p><strong>Avoid Function Expressions for Callbacks:</strong> Regular functions can lead to unexpected bindings. Use arrow functions for callbacks.</p>
</li>
<li><p><strong>Explicit Binding with</strong> <code>bind</code>: If using regular functions, explicitly bind <code>this</code> using <code>.bind(this)</code> (<a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind">MDN bind</a>).</p>
</li>
</ul>
<p>By understanding the behavior of <code>this</code> and utilizing arrow functions appropriately, you can write more predictable and maintainable JavaScript code.</p>
]]></content:encoded></item><item><title><![CDATA[Introducing keyv-upstash: Seamless Key-Value Storage for Serverless Redis]]></title><description><![CDATA[Github: https://github.com/mahdavipanah/keyv-upstash
keyv-upstash is a storage adapter for Keyv that connects it to Upstash Redis, a serverless Redis platform. With this adapter, you get a simple, efficient, and flexible solution for key-value storag...]]></description><link>https://hamidreza.tech/introducing-keyv-upstash</link><guid isPermaLink="true">https://hamidreza.tech/introducing-keyv-upstash</guid><category><![CDATA[Redis]]></category><category><![CDATA[caching]]></category><category><![CDATA[serverless]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Databases]]></category><category><![CDATA[Node.js]]></category><dc:creator><![CDATA[Hamidreza Mahdavipanah]]></dc:creator><pubDate>Tue, 10 Dec 2024 17:27:11 GMT</pubDate><content:encoded><![CDATA[<p>Github: <a target="_blank" href="https://github.com/mahdavipanah/keyv-upstash">https://github.com/mahdavipanah/keyv-upstash</a></p>
<p><code>keyv-upstash</code> is a storage adapter for <a target="_blank" href="https://github.com/jaredwray/keyv">Keyv</a> that connects it to <a target="_blank" href="https://upstash.com/">Upstash Redis</a>, a serverless Redis platform. With this adapter, you get a simple, efficient, and flexible solution for key-value storage in serverless applications.</p>
<h2 id="heading-what-is-keyv">What is Keyv?</h2>
<p>Keyv is a versatile key-value storage library that supports multiple backends through adapters. It provides:</p>
<ul>
<li><p><strong>TTL-based Expiry</strong>: Ideal for caching or persistent storage.</p>
</li>
<li><p><strong>Namespace Support</strong>: Avoids key collisions in shared environments.</p>
</li>
<li><p><strong>Extensibility</strong>: Easy to build custom modules or add features like compression.</p>
</li>
</ul>
<p>Keyv works with many adapters, such as Redis, SQLite, MongoDB, and now, <code>keyv-upstash</code> for Upstash Redis.</p>
<hr />
<h2 id="heading-why-keyv-upstash">Why <code>keyv-upstash</code>?</h2>
<p><code>keyv-upstash</code> extends Keyv's capabilities by integrating it with Upstash Redis, offering:</p>
<ol>
<li><p><strong>Serverless Compatibility</strong>: Upstash Redis works without managing connections, scaling automatically, perfect for serverless apps.</p>
</li>
<li><p><strong>Flexible</strong>: Compatible with Keyv’s ecosystem and supports third-party extensions.</p>
</li>
<li><p><strong>Cache Layering</strong>: Combine with <a target="_blank" href="https://www.npmjs.com/package/cacheable">Cacheable</a> for multi-layered caching.</p>
</li>
<li><p><strong>No Vendor Lock-In:</strong> Is fully compatible with <a target="_blank" href="https://github.com/hiett/serverless-redis-http">serverless-redis-http</a> so you can setup your own serverless Redis and use this adapter with it.</p>
</li>
</ol>
<hr />
<h2 id="heading-getting-started">Getting Started</h2>
<p>Follow these steps to integrate <code>keyv-upstash</code>:</p>
<h3 id="heading-1-install-keyv-and-keyv-upstash">1. Install Keyv and <code>keyv-upstash</code></h3>
<p>Install Keyv and the Upstash adapter:</p>
<pre><code class="lang-bash">npm install keyv keyv-upstash
</code></pre>
<p>Optional: Install Cacheable for layered caching:</p>
<pre><code class="lang-bash">npm install cacheable
</code></pre>
<hr />
<h3 id="heading-2-set-up-keyv-upstash">2. Set Up <code>keyv-upstash</code></h3>
<p>Make sure you have a Redis database created in <a target="_blank" href="https://upstash.com/">Upstash</a>. Here’s how to use <code>keyv-upstash</code> in your project:</p>
<h4 id="heading-basic-usage">Basic Usage</h4>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> Keyv <span class="hljs-keyword">from</span> <span class="hljs-string">'keyv'</span>;
<span class="hljs-keyword">import</span> { KeyvUpstash } <span class="hljs-keyword">from</span> <span class="hljs-string">'keyv-upstash'</span>;

<span class="hljs-keyword">const</span> keyv = <span class="hljs-keyword">new</span> Keyv({
  store: <span class="hljs-keyword">new</span> KeyvUpstash({
    url: <span class="hljs-string">'your-upstash-redis-url'</span>,
    token: <span class="hljs-string">'your-upstash-redis-token'</span>,
  }),
});

<span class="hljs-comment">// Set a key-value pair</span>
<span class="hljs-keyword">await</span> keyv.set(<span class="hljs-string">'foo'</span>, <span class="hljs-string">'bar'</span>);

<span class="hljs-comment">// Retrieve the value</span>
<span class="hljs-keyword">const</span> value = <span class="hljs-keyword">await</span> keyv.get(<span class="hljs-string">'foo'</span>);
<span class="hljs-built_in">console</span>.log(value); <span class="hljs-comment">// 'bar'</span>
</code></pre>
<h4 id="heading-using-namespaces">Using Namespaces</h4>
<p>Namespaces prevent key collisions and allow scoped clearing:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> keyv = <span class="hljs-keyword">new</span> Keyv({
  store: <span class="hljs-keyword">new</span> KeyvUpstash({
    url: <span class="hljs-string">'your-upstash-redis-url'</span>,
    token: <span class="hljs-string">'your-upstash-redis-token'</span>,
    <span class="hljs-keyword">namespace</span>: <span class="hljs-string">'my-namespace'</span>,
  }),
});

<span class="hljs-keyword">await</span> keyv.set(<span class="hljs-string">'foo'</span>, <span class="hljs-string">'bar'</span>); <span class="hljs-comment">// Stored as 'my-namespace::foo'</span>
</code></pre>
<h4 id="heading-cache-layering-with-cacheable">Cache Layering with Cacheable</h4>
<p>Combine <code>keyv-upstash</code> with Cacheable for multi-layer caching:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { Cacheable } <span class="hljs-keyword">from</span> <span class="hljs-string">'cacheable'</span>;

<span class="hljs-keyword">const</span> redisStore = <span class="hljs-keyword">new</span> KeyvUpstash({
  url: <span class="hljs-string">'your-upstash-redis-url'</span>,
  token: <span class="hljs-string">'your-upstash-redis-token'</span>,
});

<span class="hljs-keyword">const</span> cache = <span class="hljs-keyword">new</span> Cacheable({
  primary: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>(), <span class="hljs-comment">// Fast in-memory caching</span>
  secondary: redisStore, <span class="hljs-comment">// Persistent Redis caching</span>
});

<span class="hljs-keyword">await</span> cache.set(<span class="hljs-string">'foo'</span>, <span class="hljs-string">'bar'</span>, { ttl: <span class="hljs-number">1000</span> }); <span class="hljs-comment">// Stores in both layers</span>
<span class="hljs-keyword">const</span> value = <span class="hljs-keyword">await</span> cache.get(<span class="hljs-string">'foo'</span>); <span class="hljs-comment">// Fast lookup from memory or Redis</span>
<span class="hljs-built_in">console</span>.log(value); <span class="hljs-comment">// 'bar'</span>
</code></pre>
<hr />
<h2 id="heading-advanced-features">Advanced Features</h2>
<h3 id="heading-batch-operations">Batch Operations</h3>
<p>Improve performance with <code>setMany</code> and <code>getMany</code>:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">await</span> keyv.setMany([
  { key: <span class="hljs-string">'key1'</span>, value: <span class="hljs-string">'value1'</span> },
  { key: <span class="hljs-string">'key2'</span>, value: <span class="hljs-string">'value2'</span> },
]);

<span class="hljs-keyword">const</span> values = <span class="hljs-keyword">await</span> keyv.getMany([<span class="hljs-string">'key1'</span>, <span class="hljs-string">'key2'</span>]);
<span class="hljs-built_in">console</span>.log(values); <span class="hljs-comment">// ['value1', 'value2']</span>
</code></pre>
<h3 id="heading-custom-configuration">Custom Configuration</h3>
<p>Customize your setup with options like <code>defaultTtl</code>, <code>keyPrefixSeparator</code>, and <code>clearBatchSize</code>.</p>
]]></content:encoded></item><item><title><![CDATA[Fast and Simple NestJS App Deployment on Vercel]]></title><description><![CDATA[This guide is beneficial if you're using Express adapter. For NestJS applications utilizing the Fastify adapter, these links may be helpful:

https://fastify.dev/docs/latest/Guides/Serverless/#vercel

https://github.com/vercel/examples/tree/main/star...]]></description><link>https://hamidreza.tech/nestjs-on-vercel</link><guid isPermaLink="true">https://hamidreza.tech/nestjs-on-vercel</guid><category><![CDATA[nestjs]]></category><category><![CDATA[Vercel]]></category><category><![CDATA[deployment]]></category><category><![CDATA[Express]]></category><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[TypeScript]]></category><dc:creator><![CDATA[Hamidreza Mahdavipanah]]></dc:creator><pubDate>Tue, 26 Nov 2024 19:10:38 GMT</pubDate><content:encoded><![CDATA[<blockquote>
<p>This guide is beneficial if you're using <a target="_blank" href="https://expressjs.com/">Express</a> adapter. For NestJS applications utilizing the <a target="_blank" href="https://fastify.dev/">Fastify</a> adapter, these links may be helpful:</p>
<ul>
<li><p><a target="_blank" href="https://fastify.dev/docs/latest/Guides/Serverless/#vercel">https://fastify.dev/docs/latest/Guides/Serverless/#vercel</a></p>
</li>
<li><p><a target="_blank" href="https://github.com/vercel/examples/tree/main/starter/fastify">https://github.com/vercel/examples/tree/main/starter/fastify</a></p>
</li>
</ul>
</blockquote>
<p>🚀 You can access the complete source code discussed in this article at this GitHub repository: <a target="_blank" href="https://github.com/mahdavipanah/nestjs-on-vercel">https://github.com/mahdavipanah/nestjs-on-vercel</a></p>
<h2 id="heading-vercels-support-for-express-apps">Vercel's Support for Express Apps</h2>
<p>Vercel offers a convenient feature for deploying your Express app by:</p>
<ol>
<li><p>Exposing the Express app object in an API.</p>
</li>
<li><p>Defining a rewrite rule that directs all incoming traffic to this single API.</p>
</li>
</ol>
<p>I followed <a target="_blank" href="https://vercel.com/guides/using-express-with-vercel">Vercel’s official guide for deploying Express</a> to deploy NestJS by similarly exposing NestJS’s underlying Express app object.</p>
<h2 id="heading-step-1-create-a-nestjs-app">Step 1 - create a NestJS app</h2>
<blockquote>
<p>Skip this step if you already have a NestJS app set up.</p>
</blockquote>
<p><a target="_blank" href="https://docs.nestjs.com/first-steps">Install NestJS</a> and create a new app:</p>
<pre><code class="lang-bash">nest new my-app
</code></pre>
<h3 id="heading-step-2-install-necessary-npm-packages"><strong>Step 2 - Install Necessary NPM Packages</strong></h3>
<pre><code class="lang-bash">npm install express @nestjs/platform-express
npm install -D @types/express
</code></pre>
<h2 id="heading-step-3-create-srcappfactoryts-file">Step 3 - Create <code>src/AppFactory.ts</code> file</h2>
<p>This file serves as a single module that manages all necessary NestJS app bootstrapping and exports both the NestJS app and its underlying Express app object.</p>
<p>Create a file named <code>AppFactory.ts</code> inside the <code>src</code> directory in your project’s root:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { ExpressAdapter } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/platform-express'</span>;
<span class="hljs-keyword">import</span> { NestFactory } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/core'</span>;
<span class="hljs-keyword">import</span> express, { Request, Response } <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-keyword">import</span> { Express } <span class="hljs-keyword">from</span> <span class="hljs-string">'express'</span>;
<span class="hljs-keyword">import</span> { INestApplication } <span class="hljs-keyword">from</span> <span class="hljs-string">'@nestjs/common'</span>;
<span class="hljs-keyword">import</span> { AppModule } <span class="hljs-keyword">from</span> <span class="hljs-string">'./app.module.js'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">class</span> AppFactory {
  <span class="hljs-keyword">static</span> create(): {
    appPromise: <span class="hljs-built_in">Promise</span>&lt;INestApplication&lt;<span class="hljs-built_in">any</span>&gt;&gt;;
    expressApp: Express;
  } {
    <span class="hljs-keyword">const</span> expressApp = express();
    <span class="hljs-keyword">const</span> adapter = <span class="hljs-keyword">new</span> ExpressAdapter(expressApp);
    <span class="hljs-keyword">const</span> appPromise = NestFactory.create(AppModule, adapter);

    appPromise
      .then(<span class="hljs-function">(<span class="hljs-params">app</span>) =&gt;</span> {
        <span class="hljs-comment">// You can add all required app configurations here</span>

        <span class="hljs-comment">/**
         * Enable cross-origin resource sharing (CORS) to allow resources to be requested from another domain.
         * @see {@link https://docs.nestjs.com/security/cors}
         */</span>
        app.enableCors({
          exposedHeaders: <span class="hljs-string">'*'</span>,
        });

        app.init();
      })
      .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> {
        <span class="hljs-keyword">throw</span> err;
      });

    <span class="hljs-comment">// IMPORTANT This express application-level middleware makes sure the NestJS app is fully initialized</span>
    expressApp.use(<span class="hljs-function">(<span class="hljs-params">req: Request, res: Response, next</span>) =&gt;</span> {
      appPromise
        .then(<span class="hljs-keyword">async</span> (app) =&gt; {
          <span class="hljs-keyword">await</span> app.init();
          next();
        })
        .catch(<span class="hljs-function">(<span class="hljs-params">err</span>) =&gt;</span> next(err));
    });

    <span class="hljs-keyword">return</span> { appPromise, expressApp };
  }
}
</code></pre>
<h2 id="heading-step-4-modify-srcmaints-file"><strong>Step 4 - Modify</strong> <code>src/main.ts</code> File</h2>
<p>By default, NestJS has a <code>src/main.ts</code> file that serves as the entry point of the application, including all configuration and bootstrapping. Modify this file to move everything to the <code>AppFactory.ts</code> file, keeping only the invocation of the <code>listen</code> method:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">import</span> { AppFactory } <span class="hljs-keyword">from</span> <span class="hljs-string">'./AppFactory.js'</span>;

<span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bootstrap</span>(<span class="hljs-params"></span>) </span>{
  <span class="hljs-keyword">const</span> { appPromise } = AppFactory.create();
  <span class="hljs-keyword">const</span> app = <span class="hljs-keyword">await</span> appPromise;

  <span class="hljs-keyword">await</span> app.listen(process.env.PORT ?? <span class="hljs-number">3000</span>);
}
bootstrap();
</code></pre>
<h2 id="heading-step-5-add-apiindexts-file"><strong>Step 5 - Add</strong> <code>api/index.ts</code> File</h2>
<p>By default, the Vercel runtime builds and serves any function created within the <code>/api</code> directory of a project to Vercel (<a target="_blank" href="https://vercel.com/docs/functions/runtimes/node-js">doc</a>). Since Vercel understands and handles the Express app object, create a function inside this directory that exports the Express app object:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/**
 * This file exports Express instance for specifically for the deployment of the app on Vercel.
 */</span>

<span class="hljs-keyword">import</span> { AppFactory } <span class="hljs-keyword">from</span> <span class="hljs-string">'../src/AppFactory.js'</span>;

<span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> AppFactory.create().expressApp;
</code></pre>
<h2 id="heading-step-6-add-verceljson-file"><strong>Step 6 - Add</strong> <code>vercel.json</code> File</h2>
<p>Create a file named <code>vercel.json</code> in the project’s root directory to configure Vercel. Here, define a rewrite rule for Vercel to use the Express app to serve all incoming traffic (<a target="_blank" href="https://vercel.com/docs/projects/project-configuration">doc</a>).</p>
<p>You can also use a <code>tsconfig.json</code> file at the <code>api</code> directory to configure the Vercel’s TypeScript compiler. Most options are supported aside from <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping"><strong>"Path Mappings"</strong></a> and <a target="_blank" href="https://www.typescriptlang.org/docs/handbook/project-references.html"><strong>"Pr</strong></a><a target="_blank" href="https://www.typescriptlang.org/docs/handbook/module-resolution.html#path-mapping"><strong>oject Reference</strong></a><a target="_blank" href="https://www.typescriptlang.org/docs/handbook/project-references.html"><strong>s"</strong></a>.</p>
<pre><code class="lang-typescript">{
  <span class="hljs-string">"version"</span>: <span class="hljs-number">2</span>,
  <span class="hljs-string">"buildCommand"</span>: <span class="hljs-string">"npm run build"</span>,
  <span class="hljs-string">"outputDirectory"</span>: <span class="hljs-string">"."</span>,
  <span class="hljs-string">"rewrites"</span>: [
    {
      <span class="hljs-string">"source"</span>: <span class="hljs-string">"/(.*)"</span>,
      <span class="hljs-string">"destination"</span>: <span class="hljs-string">"/api"</span>
    }
  ]
}
</code></pre>
<h2 id="heading-step-7-create-a-project-on-vercel"><strong>Step 7 - Create a Project on Vercel</strong></h2>
<p>Congratulations 🎉! We are almost done. Now, create a git repository and push your source code to it. Then, go to your Vercel account, create a new project, and import the git repository. You can also use <a target="_blank" href="https://github.com/mahdavipanah/nestjs-on-vercel">this article’s example GitHub repository</a>.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1732642786627/ff53c571-d062-486f-9ba0-db195dfb7b93.png" alt class="image--center mx-auto" /></p>
]]></content:encoded></item><item><title><![CDATA[Pitfalls of URL and URLSearchParams in JavaScript]]></title><description><![CDATA[It All Started With a Bug
Working with URLs in JavaScript and Node.js should be straightforward, but a recent bug in our project led me down a rabbit hole of subtle quirks in the URL and URLSearchParams APIs. This post will explore these quirks, how ...]]></description><link>https://hamidreza.tech/pitfalls-of-url-and-urlsearchparams-in-nodejs</link><guid isPermaLink="true">https://hamidreza.tech/pitfalls-of-url-and-urlsearchparams-in-nodejs</guid><category><![CDATA[Node.js]]></category><category><![CDATA[JavaScript]]></category><category><![CDATA[Web Development]]></category><category><![CDATA[APIs]]></category><category><![CDATA[encoding]]></category><category><![CDATA[debugging]]></category><category><![CDATA[axios]]></category><dc:creator><![CDATA[Hamidreza Mahdavipanah]]></dc:creator><pubDate>Fri, 22 Nov 2024 16:59:48 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-it-all-started-with-a-bug">It All Started With a Bug</h2>
<p>Working with URLs in JavaScript and Node.js should be straightforward, but a recent bug in our project led me down a rabbit hole of subtle quirks in the <a target="_blank" href="https://nodejs.org/api/url.html"><code>URL</code> and <code>URLSearchParams</code> APIs</a>. This post will explore these quirks, how they can cause problems in your code, and what you can do to avoid them.</p>
<hr />
<h2 id="heading-the-problem-url-handling-with-axios">The Problem: URL Handling with Axios</h2>
<p>We encountered this issue while generating URLs and adding hash signatures to them. The query parameters weren’t consistently percent-encoded, leading to unexpected behavior and wrong hash signatures.</p>
<p>It became clear that the interaction between <code>URL</code> and <code>URLSearchParams</code> objects required extra care.</p>
<hr />
<h3 id="heading-pitfall-1-urlsearch-vs-urlsearchparamstostring">Pitfall #1: <code>URL.search</code> vs. <code>URLSearchParams.toString()</code></h3>
<p>The first surprise was the difference between <code>URL.search</code> and <code>URLSearchParams.toString()</code>.</p>
<p>Use care when using <code>.searchParams</code> to modify the <code>URL</code> because, per the <a target="_blank" href="https://url.spec.whatwg.org/">WHATWG specification</a>, the <code>URLSearchParams</code> object uses different rules to determine which characters to percent-encode. For instance, the <code>URL</code> object will not percent-encode the ASCII tilde (<code>~</code>) character, while <code>URLSearchParams</code> will always encode it.</p>
<pre><code class="lang-typescript"><span class="hljs-comment">// Example 1</span>
<span class="hljs-keyword">const</span> url = <span class="hljs-keyword">new</span> URL(<span class="hljs-string">"https://example.com?param=foo bar"</span>);
<span class="hljs-built_in">console</span>.log(url.search); <span class="hljs-comment">// prints param=foo%20bar</span>
<span class="hljs-built_in">console</span>.log(url.searchParams.toString()); <span class="hljs-comment">// prints ?param=foo+bar</span>

<span class="hljs-comment">// Example 2</span>
<span class="hljs-keyword">const</span> myURL = <span class="hljs-keyword">new</span> URL(<span class="hljs-string">'https://example.org/abc?foo=~bar'</span>);
<span class="hljs-built_in">console</span>.log(myURL.search);  <span class="hljs-comment">// prints ?foo=~bar</span>
<span class="hljs-comment">// Modify the URL via searchParams...</span>
myURL.searchParams.sort();
<span class="hljs-built_in">console</span>.log(myURL.search);  <span class="hljs-comment">// prints ?foo=%7Ebar</span>
</code></pre>
<p>In our project, we needed to explicitly reassign <code>url.search = url.searchParams.toString()</code> to ensure the query string was encoded consistently.</p>
<hr />
<h3 id="heading-pitfall-2-the-plus-sign-dilemma">Pitfall #2: The Plus Sign Dilemma</h3>
<p>Another gotcha is how <code>URLSearchParams</code> handles <code>+</code> characters. By default, <code>URLSearchParams</code> interprets <code>+</code> as a space, which may lead to data corruption when encoding binary data or Base64 strings.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> params = <span class="hljs-keyword">new</span> URLSearchParams(<span class="hljs-string">"bin=E+AXQB+A"</span>);
<span class="hljs-built_in">console</span>.log(params.get(<span class="hljs-string">"bin"</span>)); <span class="hljs-comment">// "E AXQB A"</span>
</code></pre>
<p>One solution is to use <code>encodeURIComponent</code> before appending values to <code>URLSearchParams</code>:</p>
<pre><code class="lang-typescript">params.append(<span class="hljs-string">"bin"</span>, <span class="hljs-built_in">encodeURIComponent</span>(<span class="hljs-string">"E+AXQB+A"</span>));
</code></pre>
<p>More details are available in the <a target="_blank" href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams#preserving_plus_signs">MDN documentation</a>.</p>
<hr />
<h3 id="heading-pitfall-3-urlsearchparamsget-vs-urlsearchparamstostring">Pitfall #3: <code>URLSearchParams.get</code> vs. <code>URLSearchParams.toString()</code></h3>
<p>Another subtlety arises when comparing the outputs of <code>URLSearchParams.get</code> and <code>URLSearchParams.toString</code>. For example:</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> params = <span class="hljs-keyword">new</span> URLSearchParams(<span class="hljs-string">"?key=value&amp;key=other"</span>);
<span class="hljs-built_in">console</span>.log(params.get(<span class="hljs-string">"key"</span>)); <span class="hljs-comment">// "value" (first occurrence)</span>
<span class="hljs-built_in">console</span>.log(params.toString()); <span class="hljs-comment">// "key=value&amp;key=other" (all occurrences serialized)</span>
</code></pre>
<p>In multi-valued scenarios, <code>get</code> returns only the first value, while <code>toString</code> serializes all.</p>
<hr />
<h2 id="heading-the-fix-in-our-codebase">The Fix in Our Codebase</h2>
<p>In our project, we resolved the issue by explicitly reassigning the <code>search</code> property:</p>
<pre><code class="lang-typescript">url.search = url.searchParams.toString();
url.searchParams.set(
  <span class="hljs-string">"hash"</span>,
  cryptography.createSha256HmacBase64UrlSafe(url.href, SECRET_KEY ?? <span class="hljs-string">""</span>)
);
</code></pre>
<p>This ensured that all query parameters were properly encoded before adding the <code>hash</code> value.</p>
<hr />
<h2 id="heading-nodejs-querystring-module">Node.js <code>querystring</code> module</h2>
<p>The WHATWG <code>URLSearchParams</code> interface and the <a target="_blank" href="https://nodejs.org/api/querystring.html"><code>querystring</code> module</a> have a similar purpose, but the purpose of the <code>querystring</code> module is more general, as it allows the customization of delimiter characters (<code>&amp;</code> and <code>=</code>). On the other hand, <code>URLSearchParams</code> API is designed purely for URL query strings.</p>
<p><code>querystring</code> is more performant than <code>URLSearchParams</code> but is not a standardized API. Use <code>URLSearchParams</code> when performance is not critical or when compatibility with browser code is desirable.</p>
<p>When using <code>URLSearchParams</code> unlike <code>querystring</code> module, duplicate keys in the form of array values are not allowed. Arrays are stringified using <code>array.toString()</code>, which simply joins all array elements with commas.</p>
<pre><code class="lang-typescript"><span class="hljs-keyword">const</span> params = <span class="hljs-keyword">new</span> URLSearchParams({
  user: <span class="hljs-string">'abc'</span>,
  query: [<span class="hljs-string">'first'</span>, <span class="hljs-string">'second'</span>],
});
<span class="hljs-built_in">console</span>.log(params.getAll(<span class="hljs-string">'query'</span>));
<span class="hljs-comment">// Prints [ 'first,second' ]</span>
<span class="hljs-built_in">console</span>.log(params.toString());
<span class="hljs-comment">// Prints 'user=abc&amp;query=first%2Csecond'</span>
</code></pre>
<p>With <code>querystring</code> module, the query string <code>'foo=bar&amp;abc=xyz&amp;abc=123'</code> is parsed into:</p>
<pre><code class="lang-typescript">{
  <span class="hljs-string">"foo"</span>: <span class="hljs-string">"bar"</span>,
  <span class="hljs-string">"abc"</span>: [<span class="hljs-string">"xyz"</span>, <span class="hljs-string">"123"</span>]
}
</code></pre>
<hr />
<h2 id="heading-takeaways">Takeaways</h2>
<ol>
<li><p>Be <strong>cautious of how</strong> <code>URLSearchParams</code> handles special characters (e.g. <code>~</code>) and spaces. Use <code>encodeURIComponent</code> when necessary.</p>
</li>
<li><p><strong>Understand the difference between</strong> <code>URL.search</code>, <code>URLSearchParams.get</code>, and <code>URLSearchParams.toString</code> to avoid unexpected behavior.</p>
</li>
<li><p><strong>In Node.js use</strong> <code>querystring</code> <strong>module if</strong> you want to parse duplicate query parameter keys as an array.</p>
</li>
</ol>
]]></content:encoded></item><item><title><![CDATA[Resolving MongoDB Error When Starting with Homebrew on macOS]]></title><description><![CDATA[Introduction
While installing MongoDB Community Edition on my MacBook using Homebrew (official MongoDB doc), I encountered an issue: the mongodb-community service seemed to start successfully but was listed as error when I checked its status. If you'...]]></description><link>https://hamidreza.tech/mongodb-error-with-homebrew-on-macos</link><guid isPermaLink="true">https://hamidreza.tech/mongodb-error-with-homebrew-on-macos</guid><category><![CDATA[MongoDB]]></category><category><![CDATA[macOS]]></category><category><![CDATA[Homebrew]]></category><category><![CDATA[Databases]]></category><category><![CDATA[troubleshooting]]></category><dc:creator><![CDATA[Hamidreza Mahdavipanah]]></dc:creator><pubDate>Thu, 21 Nov 2024 11:10:55 GMT</pubDate><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>While installing MongoDB Community Edition on my MacBook using Homebrew (<a target="_blank" href="https://www.mongodb.com/docs/manual/tutorial/install-mongodb-on-os-x/">official MongoDB doc</a>), I encountered an issue: the <code>mongodb-community</code> service seemed to start successfully but was listed as <strong>error</strong> when I checked its status. If you're facing the same problem, here's a quick guide to help you fix it.</p>
<h2 id="heading-the-issue">The Issue</h2>
<p>After running:</p>
<pre><code class="lang-bash">brew services start mongodb-community
</code></pre>
<p>I received a confirmation message. However, checking the service status with:</p>
<pre><code class="lang-bash">brew services list
</code></pre>
<p>showed:</p>
<pre><code class="lang-bash">Name              Status  User Plist
mongodb-community error
</code></pre>
<p>The MongoDB log (<code>/usr/local/var/log/mongodb/mongo.log</code>) contained warnings:</p>
<ul>
<li><p><strong>Running as root user</strong>: <code>"You are running this process as the root user, which is not recommended"</code></p>
</li>
<li><p><strong>Termination signal received</strong>: <code>"Received signal","attr":{"signal":15,"error":"Terminated: 15"}}</code></p>
</li>
</ul>
<p>These indicated that MongoDB was running as the root user, which can cause permission issues and lead to the service being stopped.</p>
<h2 id="heading-the-solution">The Solution</h2>
<p>The main issue was incorrect ownership of the MongoDB data and log directories. Here's how to fix it:</p>
<h3 id="heading-steps">Steps:</h3>
<ol>
<li><p><strong>Stop the MongoDB Service</strong>:</p>
<pre><code class="lang-bash"> brew services stop mongodb-community
</code></pre>
</li>
<li><p><strong>Terminate Any Running MongoDB Processes</strong>:</p>
<pre><code class="lang-bash"> pkill -f mongod
</code></pre>
</li>
<li><p><strong>Change Ownership of MongoDB Directories</strong>:</p>
<pre><code class="lang-bash"> sudo chown -R $(whoami) /usr/<span class="hljs-built_in">local</span>/var/mongodb
 sudo chown -R $(whoami) /usr/<span class="hljs-built_in">local</span>/var/<span class="hljs-built_in">log</span>/mongodb
</code></pre>
</li>
<li><p><strong>Verify Directory Ownership</strong>:</p>
<pre><code class="lang-bash"> ls -ld /usr/<span class="hljs-built_in">local</span>/var/mongodb
 ls -ld /usr/<span class="hljs-built_in">local</span>/var/<span class="hljs-built_in">log</span>/mongodb
</code></pre>
<p> Ensure your username is listed as the owner.</p>
</li>
<li><p><strong>Start the MongoDB Service as Your User</strong>:</p>
<pre><code class="lang-bash"> brew services start mongodb-community
</code></pre>
</li>
<li><p><strong>Check the Service Status</strong>:</p>
<pre><code class="lang-bash"> brew services list
</code></pre>
<p> The service should now show as <strong>started</strong> under your username.</p>
</li>
<li><p><strong>Test the MongoDB Connection</strong>:</p>
<pre><code class="lang-bash"> mongosh
</code></pre>
<p> You should successfully connect to the MongoDB shell.</p>
</li>
</ol>
<h2 id="heading-conclusion">Conclusion</h2>
<p>By correcting the directory permissions and ensuring MongoDB runs under your user account (not as root), you can resolve the error when starting the <code>mongodb-community</code> service with Homebrew on macOS. Remember to avoid using <code>sudo</code> with <code>brew</code> commands to prevent permission conflicts.</p>
]]></content:encoded></item><item><title><![CDATA[Using @ and */ symbols inside JS multiline comments]]></title><description><![CDATA[While documenting JavaScript code using JSDoc, I stumbled upon an annoying issue. My attempts to include the /* symbol in the example code within a multiline comment caused the entire comment block to break. This happens because */ is recognized as t...]]></description><link>https://hamidreza.tech/using-special-symbols-inside-js-multiline-comments</link><guid isPermaLink="true">https://hamidreza.tech/using-special-symbols-inside-js-multiline-comments</guid><category><![CDATA[jsdoc]]></category><category><![CDATA[TypeScript]]></category><category><![CDATA[JavaScript]]></category><dc:creator><![CDATA[Hamidreza Mahdavipanah]]></dc:creator><pubDate>Fri, 12 Apr 2024 12:55:37 GMT</pubDate><content:encoded><![CDATA[<p>While documenting JavaScript code using JSDoc, I stumbled upon an annoying issue. My attempts to include the <code>/*</code> symbol in the example code within a multiline comment caused the entire comment block to break. This happens because <code>*/</code> is recognized as the ending tag for a multiline comment in JavaScript.</p>
<p>You can see the problem clearly in the blow code block!</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/**
 * Checks whether two permission strings are semantically equal or not.
 *
 * @example
 * // returns true
 * equals('a/b/c/d/allow', 'a/b/c/*⁣/*/</span>d/allow<span class="hljs-string">');
 *
 * @returns {boolean} True in case of equality and false otherwise.
*/
const equals = (first: string, second: string) =&gt; {
    // function'</span>s logic
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>; <span class="hljs-comment">// or false</span>
}
</code></pre>
<p>The solution is surprisingly simple: insert a Unicode invisible separator character between <code>*</code> and <code>/</code>. This character acts as a stealthy spacer, preventing the JavaScript interpreter from recognizing the sequence as the end of a comment. You can find and copy this invisible character <a target="_blank" href="https://unicode-explorer.com/c/2063"><strong>here</strong></a>.</p>
<p>The same problem happens if you use the <code>@</code> symbol to put a JSDoc code example of a decorator. Because the <code>@</code> symbol has a special meaning for JSDoc, it leads to unintended changes in the rendered documentation. Consider this snippet:</p>
<pre><code class="lang-typescript"><span class="hljs-comment">/**
 * A NestJS handler decorator that defines an access permission constraint and enforces it.
 *
 * @example
 * `⁣`⁣`ts
 * // the request only needs to be authenticated and doesn't need any specific permissions
 * @RequiresAccess()
 * Class MyController {}
 * `⁣`⁣`
*/</span>
<span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> RequiresAccess = Reflector.createDecorator&lt;
  PermissionPathGen | PermissionPathGen[]
&gt;({
  transform(value) {
    <span class="hljs-keyword">return</span> value == <span class="hljs-literal">undefined</span> ? MUST_BE_AUTHENTICATED : value;
  },
});
</code></pre>
<p>Solving this is the same as the previous problem, you need to put an <a target="_blank" href="https://unicode-explorer.com/c/2063">invisible separator</a> before the <code>@</code>.</p>
]]></content:encoded></item></channel></rss>