CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-remix-run--node

Node.js platform abstractions and utilities for Remix applications

Pending
Overview
Eval results
Files

stream-utilities.mddocs/

Stream Utilities

Utilities for converting between Node.js streams and Web Streams API, enabling seamless data flow between different stream types in Node.js environments.

Capabilities

Write ReadableStream to Writable

Writes a Web ReadableStream to a Node.js Writable stream with proper error handling and backpressure management.

/**
 * Writes a Web ReadableStream to a Node.js Writable stream
 * @param stream - The ReadableStream to read from
 * @param writable - The Node.js Writable stream to write to
 * @returns Promise that resolves when the operation completes
 */
function writeReadableStreamToWritable(
  stream: ReadableStream,
  writable: Writable
): Promise<void>;

Usage Examples:

import { writeReadableStreamToWritable } from "@remix-run/node";
import { createWriteStream } from "node:fs";

// Save a fetch response to file
export async function action({ request }: ActionFunctionArgs) {
  const response = await fetch("https://example.com/large-file.zip");
  
  if (response.body) {
    const fileStream = createWriteStream("/tmp/downloaded-file.zip");
    
    await writeReadableStreamToWritable(response.body, fileStream);
    
    return json({ message: "File downloaded successfully" });
  }
  
  return json({ error: "No response body" }, { status: 400 });
}

// Stream request body to file
export async function action({ request }: ActionFunctionArgs) {
  if (request.body) {
    const outputStream = createWriteStream("/tmp/request-body.bin");
    
    try {
      await writeReadableStreamToWritable(request.body, outputStream);
      return json({ success: true });
    } catch (error) {
      return json({ error: "Failed to save request body" }, { status: 500 });
    }
  }
  
  return json({ error: "No request body" }, { status: 400 });
}

Write AsyncIterable to Writable

Writes an AsyncIterable of Uint8Array chunks to a Node.js Writable stream.

/**
 * Writes an AsyncIterable to a Node.js Writable stream
 * @param iterable - The AsyncIterable of Uint8Array chunks to write
 * @param writable - The Node.js Writable stream to write to
 * @returns Promise that resolves when the operation completes
 */
function writeAsyncIterableToWritable(
  iterable: AsyncIterable<Uint8Array>,
  writable: Writable
): Promise<void>;

Usage Examples:

import { writeAsyncIterableToWritable } from "@remix-run/node";
import { createWriteStream } from "node:fs";

// Process and save async data
async function* generateData() {
  for (let i = 0; i < 1000; i++) {
    yield new TextEncoder().encode(`Data chunk ${i}\n`);
  }
}

export async function action() {
  const dataStream = generateData();
  const fileStream = createWriteStream("/tmp/generated-data.txt");
  
  await writeAsyncIterableToWritable(dataStream, fileStream);
  
  return json({ message: "Data generated and saved" });
}

ReadableStream to String

Converts a Web ReadableStream to a string with optional encoding specification.

/**
 * Converts a ReadableStream to a string
 * @param stream - The ReadableStream to convert
 * @param encoding - Optional buffer encoding (defaults to 'utf8')
 * @returns Promise resolving to the stream contents as a string
 */
function readableStreamToString(
  stream: ReadableStream<Uint8Array>,
  encoding?: BufferEncoding
): Promise<string>;

Usage Examples:

import { readableStreamToString } from "@remix-run/node";

// Read request body as string
export async function action({ request }: ActionFunctionArgs) {
  if (request.body) {
    const bodyText = await readableStreamToString(request.body);
    
    try {
      const data = JSON.parse(bodyText);
      return json({ received: data });
    } catch (error) {
      return json({ error: "Invalid JSON" }, { status: 400 });
    }
  }
  
  return json({ error: "No request body" }, { status: 400 });
}

// Read fetch response as string
export async function loader() {
  const response = await fetch("https://api.example.com/data");
  
  if (response.body) {
    const text = await readableStreamToString(response.body);
    return json({ data: text });
  }
  
  return json({ error: "No response body" }, { status: 500 });
}

// Read with specific encoding
export async function action({ request }: ActionFunctionArgs) {
  if (request.body) {
    const bodyText = await readableStreamToString(request.body, "base64");
    return json({ encodedData: bodyText });
  }
  
  return json({ error: "No request body" }, { status: 400 });
}

Create ReadableStream from Readable

Converts a Node.js Readable stream to a Web ReadableStream with proper backpressure handling.

/**
 * Creates a Web ReadableStream from a Node.js Readable stream
 * @param source - The Node.js Readable stream to convert
 * @returns Web ReadableStream
 */
function createReadableStreamFromReadable(
  source: Readable & { readableHighWaterMark?: number }
): ReadableStream;

Usage Examples:

import { createReadableStreamFromReadable } from "@remix-run/node";
import { createReadStream } from "node:fs";

// Stream file contents as Web ReadableStream
export async function loader({ params }: LoaderFunctionArgs) {
  const filePath = `/files/${params.filename}`;
  const nodeStream = createReadStream(filePath);
  const webStream = createReadableStreamFromReadable(nodeStream);
  
  return new Response(webStream, {
    headers: {
      "Content-Type": "application/octet-stream",
      "Content-Disposition": `attachment; filename="${params.filename}"`
    }
  });
}

// Convert process.stdin to Web ReadableStream
export async function action() {
  const stdinStream = createReadableStreamFromReadable(process.stdin);
  const reader = stdinStream.getReader();
  
  const chunks: string[] = [];
  while (true) {
    const { done, value } = await reader.read();
    if (done) break;
    
    chunks.push(new TextDecoder().decode(value));
  }
  
  return json({ input: chunks.join("") });
}

Stream Conversion Patterns

Node.js to Web Streams:

import { createReadableStreamFromReadable } from "@remix-run/node";
import { createReadStream } from "node:fs";

// File → Web ReadableStream
const fileStream = createReadStream("data.txt");
const webStream = createReadableStreamFromReadable(fileStream);

Web Streams to Node.js:

import { writeReadableStreamToWritable, readableStreamToString } from "@remix-run/node";
import { createWriteStream } from "node:fs";

// Web ReadableStream → File
const response = await fetch("https://example.com/data");
if (response.body) {
  await writeReadableStreamToWritable(response.body, createWriteStream("output.txt"));
}

// Web ReadableStream → String
const text = await readableStreamToString(response.body);

Performance Considerations:

  • Backpressure handling: All utilities properly handle backpressure to prevent memory issues
  • Streaming processing: Data is processed in chunks to handle large streams efficiently
  • Error propagation: Errors are properly propagated between stream types
  • Resource cleanup: Streams are properly closed and cleaned up on completion or error

Install with Tessl CLI

npx tessl i tessl/npm-remix-run--node

docs

cookie-session.md

global-polyfills.md

index.md

server-runtime.md

session-storage.md

stream-utilities.md

upload-handling.md

tile.json