Web Stream Tools
Web Stream Tools (available as `@openpgp/web-stream-tools` on npm) is a JavaScript utility library designed to simplify working with WhatWG Streams. It provides a comprehensive set of functions for common stream operations, including reading to completion (`readToEnd`), concatenating streams (`concat`), slicing parts of streams (`slice`), cloning streams (`clone`), and facilitating conversion between Node.js streams and Web Streams (`webToNode`, `nodeToWeb`). The library also features advanced capabilities for transforming stream data, either directly (`transform`) or in chunks with backpressure handling (`transformPair`), and for parsing structured data from streams (`parse`) with functions like `readByte`, `readBytes`, and `readLine`. The current stable version is 0.3.0. Developed by the OpenPGP.js team, it maintains an active development status, with updates generally released as needed. A key differentiator is its explicit focus on the WhatWG Streams API, providing a consistent interface across browser and modern Node.js environments, streamlining complex stream manipulations that often involve manual reader/writer management.
Common errors
-
Error: Cannot find module 'web-stream-tools'
cause The package name has changed from 'web-stream-tools' to '@openpgp/web-stream-tools'.fixChange the dependency in `package.json` and all import paths in your code to `@openpgp/web-stream-tools`. -
TypeError: stream.transform is not a function
cause Incorrect import statement or module not found, leading to 'stream' being undefined or an empty object.fixEnsure you are using `import * as stream from '@openpgp/web-stream-tools';` (for namespace import) or `import { transform } from '@openpgp/web-stream-tools';` (for named import). -
Error: The provided stream is not a WhatWG ReadableStream.
cause You are passing a native Node.js `stream.Readable` object to a function that expects a WhatWG `ReadableStream`.fixIf in Node.js v17+, convert your Node stream using `myNodeStream.toWeb()` before passing it to `web-stream-tools` functions. For older Node.js versions, you may need to manually wrap the Node stream or use a polyfill.
Warnings
- breaking The official package name for Web Stream Tools has been changed from `web-stream-tools` to `@openpgp/web-stream-tools`. Direct imports using the old package name will result in `Module not found` errors or `undefined` imports.
- breaking As of v0.1.0, the library no longer supports native Node.js `Readable` or `Writable` streams directly as input or output for many functions. It now exclusively expects and operates on WhatWG `ReadableStream` and `WritableStream` instances (often referred to as Node's WebStreams in modern Node.js).
- gotcha Attempting to use CommonJS `require()` syntax with `@openpgp/web-stream-tools` might not work as expected or could lead to bundling issues. The library is primarily designed for ES Module (`import`) usage with Web Streams, especially given its browser-compatibility focus.
Install
-
npm install web-stream-tools -
yarn add web-stream-tools -
pnpm add web-stream-tools
Imports
- * as stream
import stream from 'web-stream-tools';
import * as stream from '@openpgp/web-stream-tools';
- transform
import { transform } from 'web-stream-tools';import { transform } from '@openpgp/web-stream-tools'; - readToEnd
const { readToEnd } = require('@openpgp/web-stream-tools');import { readToEnd } from '@openpgp/web-stream-tools';
Quickstart
import * as stream from '@openpgp/web-stream-tools';
// Imagine an encryptor API with process and finish methods
class Encryptor {
process(chunk) {
console.log('Processing chunk:', chunk.byteLength, 'bytes');
// Simulate encryption, return a new Uint8Array
return new Uint8Array(chunk.map(b => b ^ 0xFF));
}
finish() {
console.log('Finishing encryption.');
// Return any final encrypted bytes, or an empty Uint8Array
return new Uint8Array([0xDE, 0xAD, 0xBE, 0xEF]);
}
}
async function encryptDataStream() {
const encryptor = new new Encryptor();
const inputData = new TextEncoder().encode("This is a test string to be encrypted using web-stream-tools.");
// Create a ReadableStream from the input data
const input = new ReadableStream({
start(controller) {
controller.enqueue(inputData);
controller.close();
}
});
const encryptedStream = stream.transform(input, function process(chunk) {
return encryptor.process(chunk);
}, function finish() {
return encryptor.finish();
});
// Read the encrypted stream to the end and collect all chunks
const reader = encryptedStream.getReader();
let allChunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) {
break;
}
allChunks.push(value);
}
const finalEncryptedData = new Uint8Array(allChunks.reduce((acc, chunk) => acc.concat(Array.from(chunk)), []));
console.log('Total encrypted data length:', finalEncryptedData.byteLength, 'bytes');
console.log('Encryption complete.');
}
encryptDataStream().catch(console.error);