<?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>Forem: mktbsh</title>
    <description>The latest articles on Forem by mktbsh (@hsb).</description>
    <link>https://forem.com/hsb</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%2F3769829%2F66012e20-1e28-4336-904c-39adf7cc3cf9.jpeg</url>
      <title>Forem: mktbsh</title>
      <link>https://forem.com/hsb</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/hsb"/>
    <language>en</language>
    <item>
      <title>Releasing @hsblabs/web-stream-extras: small utilities for WHATWG byte stream pipelines</title>
      <dc:creator>mktbsh</dc:creator>
      <pubDate>Wed, 04 Mar 2026 18:30:00 +0000</pubDate>
      <link>https://forem.com/hsb/releasing-hsblabsweb-stream-extras-small-utilities-for-whatwg-byte-stream-pipelines-5fil</link>
      <guid>https://forem.com/hsb/releasing-hsblabsweb-stream-extras-small-utilities-for-whatwg-byte-stream-pipelines-5fil</guid>
      <description>&lt;p&gt;I published &lt;a href="https://www.npmjs.com/package/@hsblabs/web-stream-extras" rel="noopener noreferrer"&gt;@hsblabs/web-stream-extras&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;It started as internal plumbing I kept copy-pasting across projects. Enough utility collected around &lt;code&gt;ReadableStream&amp;lt;Uint8Array&amp;gt;&lt;/code&gt; that it made sense to package it properly.&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; @hsblabs/web-stream-extras
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Node.js ≥22 and modern browsers. No runtime dependencies.&lt;/p&gt;




&lt;h2&gt;
  
  
  What's in the root package
&lt;/h2&gt;

&lt;p&gt;The root export handles the everyday byte stream operations.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;readableFromChunks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;readAllBytes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;stringToBinary&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;binaryToString&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hsblabs/web-stream-extras&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;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;readableFromChunks&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
  &lt;span class="nf"&gt;stringToBinary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;hello&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
  &lt;span class="nf"&gt;stringToBinary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; world&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;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;readAllBytes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;binaryToString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt; &lt;span class="c1"&gt;// "hello world"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;readableFromChunks&lt;/code&gt; wraps an array of &lt;code&gt;Uint8Array&lt;/code&gt; chunks into a proper &lt;code&gt;ReadableStream&lt;/code&gt;. &lt;code&gt;readAllBytes&lt;/code&gt; collects everything from a stream into a single &lt;code&gt;Uint8Array&lt;/code&gt;. The string and buffer conversion helpers — &lt;code&gt;stringToBinary&lt;/code&gt;, &lt;code&gt;binaryToString&lt;/code&gt;, &lt;code&gt;toU8Array&lt;/code&gt;, &lt;code&gt;toArrayBuffer&lt;/code&gt;, &lt;code&gt;concatU8Arrays&lt;/code&gt; — exist because converting between &lt;code&gt;string&lt;/code&gt;, &lt;code&gt;Uint8Array&lt;/code&gt;, and &lt;code&gt;ArrayBuffer&lt;/code&gt; is repetitive and easy to get subtly wrong.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;ByteTransformStream&lt;/code&gt; is an abstract base for building typed binary transform pipelines. &lt;code&gt;ByteQueue&lt;/code&gt; handles internal buffering with a clean read/write interface — useful when you need to track partial byte reads across chunks.&lt;/p&gt;

&lt;p&gt;Base64url encoding is included too: &lt;code&gt;encodeBase64Url&lt;/code&gt; and &lt;code&gt;decodeBase64Url&lt;/code&gt;, without padding.&lt;/p&gt;




&lt;h2&gt;
  
  
  Stream encryption
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;encryption&lt;/code&gt; subpath adds encryption built on the Web Crypto API.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;encryptStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;decryptStream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@hsblabs/web-stream-extras/encryption&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;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;crypto&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getRandomValues&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Uint8Array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;32&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;encrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;encryptStream&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;plaintext&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;decrypted&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;decryptStream&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;encrypted&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;encryptStream&lt;/code&gt; and &lt;code&gt;decryptStream&lt;/code&gt; take a &lt;code&gt;ReadableStream&lt;/code&gt; and return a &lt;code&gt;ReadableStream&lt;/code&gt;. The underlying &lt;code&gt;EncryptionStream&lt;/code&gt; and &lt;code&gt;DecryptionStream&lt;/code&gt; are exposed as &lt;code&gt;TransformStream&lt;/code&gt; wrappers if you need to compose them differently.&lt;/p&gt;

&lt;p&gt;For cases where you need key management, &lt;code&gt;webCryptoStream(masterKey)&lt;/code&gt; handles deriving and encrypting per-stream keys with an &lt;code&gt;AES-GCM&lt;/code&gt; master key.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Web Streams
&lt;/h2&gt;

&lt;p&gt;The WHATWG Streams API is available natively in Node.js ≥18 and all modern browsers. Using it directly avoids Node.js-specific stream abstractions and keeps the code portable. If you're targeting environments where both sides — browser and server — need to handle the same byte pipeline, the WHATWG API is the right base layer.&lt;/p&gt;




&lt;p&gt;&lt;a href="https://www.npmjs.com/package/@hsblabs/web-stream-extras" rel="noopener noreferrer"&gt;npm&lt;/a&gt; · &lt;a href="https://github.com/hsblabs/web-stream-extras" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Issues and feedback welcome.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>typescript</category>
      <category>javascript</category>
      <category>node</category>
    </item>
  </channel>
</rss>
