Un jour, vous avez besoin d’un ReadableStream<Uint8Array>. Vous ouvrez un fichier vide et recommencez à écrire la même ossature de contrôleur. Ensuite, il faut récupérer les octets à la fin du flux. Encore un utilitaire. Puis il faut passer ce résultat à quelque chose qui attend un ArrayBuffer plutôt qu’un Uint8Array. Encore une conversion.
Rien de tout cela n’est difficile. Tout cela fait du bruit.
J’ai donc regroupé ces morceaux dans @hsblabs/web-stream-extras.
Les trois motifs que je réécrivais sans cesse
Créer un ReadableStream à partir d’un tableau de chunks
// beforeconst stream = new ReadableStream({ start(controller) { for (const chunk of chunks) controller.enqueue(chunk); controller.close(); },});Collecter un flux dans un unique Uint8Array
// beforeconst chunks: Uint8Array[] = [];const reader = stream.getReader();while (true) { const { done, value } = await reader.read(); if (done) break; chunks.push(value);}const result = concatU8Arrays(chunks); // celui-là aussi, il fallait l'écrire soi-mêmeConvertir entre Uint8Array, ArrayBuffer et chaînes
Ces petits décalages qui vous obligent à fouiller encore pour trouver la bonne vue ou le bon schéma de copie.
After
import { readableFromChunks, readAllBytes, stringToBinary, binaryToString,} from "@hsblabs/web-stream-extras";
const stream = readableFromChunks([ stringToBinary("hello"), stringToBinary(" world"),]);
const result = await readAllBytes(stream);console.log(binaryToString(result)); // "hello world"Installation :
npm install @hsblabs/web-stream-extrasCompatible Node.js ≥22 et navigateurs modernes. Aucune dépendance runtime.
Construire des pipelines de transformation binaire
Quand il faut une étape de transformation personnalisée, ByteTransformStream fournit une classe de base typée qui permet d’étendre TransformStream sans réécrire toute la plomberie.
import { ByteTransformStream } from "@hsblabs/web-stream-extras";
class UpperCaseStream extends ByteTransformStream { transform(chunk: Uint8Array) { // traiter les octets et pousser la sortie avec this.push() }}ByteQueue gère le buffering interne lorsqu’il faut lire des octets partiels à travers plusieurs chunks. encodeBase64Url et decodeBase64Url servent aux frontières entre données binaires et texte.
Chiffrement de flux
Le chiffrement est rangé dans un sous-chemin séparé. Si vous n’en avez pas besoin, il n’est pas embarqué.
import { encryptStream, decryptStream } from "@hsblabs/web-stream-extras/encryption";
const key = crypto.getRandomValues(new Uint8Array(32));
const plaintext = readableFromChunks([stringToBinary("secret payload")]);const encrypted = encryptStream(key, plaintext);const decrypted = decryptStream(key, encrypted);
console.log(binaryToString(await readAllBytes(decrypted))); // "secret payload"Les deux fonctions prennent et renvoient un ReadableStream<Uint8Array>, ce qui les rend naturelles à brancher au reste d’un pipeline. Les EncryptionStream et DecryptionStream internes sont aussi exposés comme wrappers TransformStream si vous devez les utiliser directement.
Le format repose sur AES-GCM avec des clés et des nonce dérivés par enregistrement via HKDF. Il est conçu pour les flux, pas comme format de chiffrement de fichiers générique.
Variante avec clé maître
Si l’application gère déjà une clé maître AES-GCM distincte et veut stocker des clés par flux avec les métadonnées chiffrées, webCryptoStream encapsule ce modèle.
import { webCryptoStream } from "@hsblabs/web-stream-extras/encryption";
const masterKey = await crypto.subtle.generateKey( { name: "AES-GCM", length: 256 }, false, ["encrypt", "decrypt"],);
const crypto$ = webCryptoStream(masterKey);
const streamKey = await crypto$.createStreamKey();
const encrypted = await crypto$.encrypt(streamKey, plaintext);const decrypted = await crypto$.decrypt(streamKey, encrypted);Si vous avez déjà une clé brute de 32 octets, encryptStream / decryptStream suffisent.
Ce que le package ne fait pas
Cela vaut la peine d’être explicite :
- Pas d’authentification — il fournit de la confidentialité, pas de la vérification d’identité
- Pas de stockage de clés — l’application décide où garder les clés de flux
- Pas de dérivation de clé à partir d’un mot de passe — PBKDF2 / Argon2 restent à votre charge
- Pas de gestion d’utilisateurs — c’est un utilitaire bas niveau pour les flux, pas un framework d’auth
Si quelque chose manque, ouvrir une issue reste le moyen le plus rapide.
hsb.horse