### Install form-data-parser Source: https://github.com/mjackson/remix-the-web/blob/main/packages/form-data-parser/examples/node/README.md Installs the form-data-parser package using npm, a package manager for Node.js. ```bash npm install form-data-parser ``` -------------------------------- ### Install multipart-parser Source: https://github.com/mjackson/remix-the-web/blob/main/packages/multipart-parser/README.md Instructions for installing the multipart-parser library using npm or deno. ```sh npm install @mjackson/multipart-parser ``` ```sh deno add @mjackson/multipart-parser ``` -------------------------------- ### Basic Server Example Source: https://github.com/mjackson/remix-the-web/blob/main/packages/node-fetch-server/README.md A complete working example demonstrating how to create a Node.js server using node-fetch-server with a simple in-memory user store. It handles GET requests for listing users and retrieving specific users by ID. ```typescript import * as http from 'node:http'; import { createRequestListener } from '@mjackson/node-fetch-server'; // Example: Simple in-memory user storage let users = new Map([ ['1', { id: '1', name: 'Alice', email: 'alice@example.com' }], ['2', { id: '2', name: 'Bob', email: 'bob@example.com' }], ]); async function handler(request: Request) { let url = new URL(request.url); // GET / - Home page if (url.pathname === '/' && request.method === 'GET') { return new Response('Welcome to the User API! Try GET /api/users'); } // GET /api/users - List all users if (url.pathname === '/api/users' && request.method === 'GET') { return Response.json(Array.from(users.values())); } // GET /api/users/:id - Get specific user let userMatch = url.pathname.match(/^\/api\/users\/(\w+)$/); if (userMatch && request.method === 'GET') { let user = users.get(userMatch[1]); if (user) { return Response.json(user); } return new Response('User not found', { status: 404 }); } return new Response('Not Found', { status: 404 }); } // Create a standard Node.js server let server = http.createServer(createRequestListener(handler)); server.listen(3000, () => { console.log('Server running at http://localhost:3000'); }); ``` -------------------------------- ### Install node-fetch-server Source: https://github.com/mjackson/remix-the-web/blob/main/packages/node-fetch-server/README.md Installs the node-fetch-server package using npm. ```sh npm install @mjackson/node-fetch-server ``` -------------------------------- ### Install Project Dependencies Source: https://github.com/mjackson/remix-the-web/blob/main/CONTRIBUTING.md Installs all necessary project dependencies using pnpm. This is a prerequisite for building and testing the project. ```sh pnpm install ``` -------------------------------- ### Install @mjackson/headers Source: https://github.com/mjackson/remix-the-web/blob/main/packages/headers/README.md Installs the @mjackson/headers package using npm. ```sh npm install @mjackson/headers ``` -------------------------------- ### Install @mjackson/file-storage Source: https://github.com/mjackson/remix-the-web/blob/main/packages/file-storage/README.md Installs the file-storage package using npm. ```sh npm install @mjackson/file-storage ``` -------------------------------- ### Install fetch-proxy Source: https://github.com/mjackson/remix-the-web/blob/main/packages/fetch-proxy/README.md Installs the fetch-proxy package using npm. This is the first step to using the library in your project. ```sh npm i @mjackson/fetch-proxy ``` -------------------------------- ### Install Package Source: https://github.com/mjackson/remix-the-web/blob/main/README.md Installs a package from npm. This is the primary method for integrating project packages into your workflow. ```bash npm install ``` -------------------------------- ### Import and Use Package Source: https://github.com/mjackson/remix-the-web/blob/main/README.md Demonstrates how to import and use a package after installation. Specific usage details should be found in individual package README files. ```javascript import { someFunction } from ''; // Use someFunction in your project ``` -------------------------------- ### Build the Project Source: https://github.com/mjackson/remix-the-web/blob/main/CONTRIBUTING.md Compiles and builds the entire project. This command should be run after installing dependencies or making significant code changes. ```sh pnpm build ``` -------------------------------- ### Install form-data-parser Source: https://github.com/mjackson/remix-the-web/blob/main/packages/form-data-parser/README.md Installs the form-data-parser package using npm. This command is used to add the library to your project's dependencies. ```sh npm install @mjackson/form-data-parser ``` -------------------------------- ### FileStorage Interface Implementation Source: https://github.com/mjackson/remix-the-web/blob/main/packages/file-storage/README.md Shows how to implement the generic FileStorage interface for custom storage backends. Includes methods for checking existence, setting, getting, and removing files. ```ts import { type FileStorage } from '@mjackson/file-storage'; class CustomFileStorage implements FileStorage { /** * Returns `true` if a file with the given key exists, `false` otherwise. */ has(key: string): boolean | Promise { // ... } /** * Puts a file in storage at the given key. */ set(key: string, file: File): void | Promise { // ... } /** * Returns the file with the given key, or `null` if no such key exists. */ get(key: string): File | null | Promise { // ... } /** * Removes the file with the given key from storage. */ remove(key: string): void | Promise { // ... } } ``` -------------------------------- ### Install tar-parser Source: https://github.com/mjackson/remix-the-web/blob/main/packages/tar-parser/README.md Installs the tar-parser package using npm. This is the standard way to add the library to your JavaScript project. ```sh npm install @mjackson/tar-parser ``` -------------------------------- ### Node.js Server for File Uploads Source: https://github.com/mjackson/remix-the-web/blob/main/packages/form-data-parser/examples/node/README.md A basic Node.js server setup using the 'http' module to handle incoming requests. It utilizes the 'form-data-parser' to process multipart/form-data requests, specifically focusing on file uploads and streaming them to a temporary file. ```javascript const http = require('http'); const FormDataParser = require('form-data-parser'); const fs = require('fs'); const path = require('path'); const server = http.createServer((req, res) => { if (req.method === 'POST') { const parser = new FormDataParser(req); parser.on('file', (fieldName, fileStream, fileName, fileType, transferEncoding, fileSize) => { const tempFilePath = path.join(__dirname, 'uploads', fileName); const writeStream = fs.createWriteStream(tempFilePath); fileStream.pipe(writeStream); writeStream.on('finish', () => { console.log(`File saved to: ${tempFilePath}`); res.writeHead(200, { 'Content-Type': 'text/plain' }); res.end('File uploaded successfully!'); }); writeStream.on('error', (err) => { console.error('Error writing file:', err); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error saving file.'); }); }); parser.on('field', (fieldName, value) => { console.log(`Field: ${fieldName}, Value: ${value}`); }); parser.on('error', (err) => { console.error('Error parsing form data:', err); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error processing form data.'); }); parser.on('end', () => { console.log('Finished parsing form data.'); }); } else { res.writeHead(405, { 'Content-Type': 'text/plain' }); res.end('Method Not Allowed'); } }); const PORT = 3000; server.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); // Ensure uploads directory exists if (!fs.existsSync('uploads')) { fs.mkdirSync('uploads'); } }); ``` -------------------------------- ### Handle Multipart Form Data in Deno Source: https://github.com/mjackson/remix-the-web/blob/main/packages/multipart-parser/examples/deno/README.md This snippet shows how to parse multipart/form-data requests on a Deno server, extract form fields, and stream file uploads to a specified directory. It utilizes the `multipart-parser` library. ```typescript import { multipart } from "https://deno.land/std@0.192.0/multipart/mod.ts"; import { join } from "https://deno.land/std@0.192.0/path/mod.ts"; const TMP_DIR = "./tmp"; Deno.serve(async (req) => { if (req.method === "POST") { const body = await req.formData(); const files = body.getAll("file"); for (const file of files) { if (file instanceof File) { const filePath = join(TMP_DIR, file.name); const fileBytes = await file.arrayBuffer(); await Deno.writeFile(filePath, new Uint8Array(fileBytes)); console.log(`Saved file to: ${filePath}`); } } return new Response("Files uploaded successfully!"); } return new Response("Send a POST request with multipart/form-data"); }); // Ensure the temporary directory exists await Deno.mkdir(TMP_DIR, { recursive: true }); console.log(`Listening on http://localhost:8000, saving files to ${TMP_DIR}`); ``` -------------------------------- ### Node.js Multipart File Upload Server Source: https://github.com/mjackson/remix-the-web/blob/main/packages/multipart-parser/examples/node/README.md This Node.js server example utilizes the 'multipart-parser' library to handle incoming HTTP requests with 'multipart/form-data' content. It demonstrates how to parse these requests, extract uploaded files, and stream their content to temporary files on the server's disk. This is useful for web applications that need to accept file uploads. ```javascript const http = require('http'); const multipart = require('multipart-parser'); const fs = require('fs'); const path = require('path'); const server = http.createServer((req, res) => { if (req.method === 'POST') { let body = []; req.on('data', (chunk) => { body.push(chunk); }); req.on('end', () => { const boundary = multipart.getBoundary(req.headers['content-type']); const parts = multipart.parser(Buffer.concat(body), boundary); parts.forEach(part => { if (part.filename) { const filePath = path.join(__dirname, 'uploads', part.filename); // Ensure uploads directory exists if (!fs.existsSync(path.dirname(filePath))) { fs.mkdirSync(path.dirname(filePath), { recursive: true }); } fs.writeFile(filePath, part.data, (err) => { if (err) { console.error('Error writing file:', err); res.statusCode = 500; res.end('Error saving file'); } else { console.log(`File saved: ${filePath}`); } }); } }); res.statusCode = 200; res.end('File uploaded successfully'); }); } else { res.statusCode = 405; res.end('Method Not Allowed'); } }); const PORT = 3000; server.listen(PORT, () => { console.log(`Server listening on port ${PORT}`); }); ``` -------------------------------- ### Install lazy-file via npm Source: https://github.com/mjackson/remix-the-web/blob/main/packages/lazy-file/README.md Provides the command to install the `@mjackson/lazy-file` package using npm, the Node Package Manager. This is the standard way to add the library to your JavaScript or TypeScript project. ```sh npm install @mjackson/lazy-file ``` -------------------------------- ### Cookie Management Source: https://github.com/mjackson/remix-the-web/blob/main/packages/headers/README.md Demonstrates how to create, get, set, delete, and iterate over cookie key-value pairs using the Cookie class. Supports initialization with string, object, or array formats. ```ts import { Cookie } from '@mjackson/headers'; let header = new Cookie('theme=dark; session_id=123'); header.get('theme'); // "dark" header.set('theme', 'light'); header.delete('theme'); header.has('session_id'); // true // Iterate over cookie name/value pairs for (let [name, value] of header) { // ... } // Alternative init styles let header = new Cookie({ theme: 'dark', session_id: '123' }); let header = new Cookie([ ['theme', 'dark'], ['session_id', '123'], ]); ``` -------------------------------- ### Development Commands Source: https://github.com/mjackson/remix-the-web/blob/main/CLAUDE.md Provides commands for building the project and running tests. It includes instructions for building the entire project or specific packages, as well as running all tests or tests for a particular package. ```sh # Run the build $ pnpm run build # Build a specific package $ cd packages/headers && pnpm run build # Run the tests $ pnpm test # Run the tests for a specific package $ cd packages/headers && pnpm test ``` -------------------------------- ### Migration from Express: Basic Routing Source: https://github.com/mjackson/remix-the-web/blob/main/packages/node-fetch-server/README.md Compares basic routing between Express and `@mjackson/node-fetch-server`. It shows how to define routes and handle parameters using URL matching in the latter. ```javascript // Express let app = express(); app.get('/users/:id', async (req, res) => { let user = await db.getUser(req.params.id); if (!user) { return res.status(404).json({ error: 'User not found' }); } res.json(user); }); app.listen(3000); ``` ```ts // node-fetch-server import { createRequestListener } from '@mjackson/node-fetch-server'; async function handler(request: Request) { let url = new URL(request.url); let match = url.pathname.match(/^\/users\/(\w+)$/); if (match && request.method === 'GET') { let user = await db.getUser(match[1]); if (!user) { return Response.json({ error: 'User not found' }, { status: 404 }); } return Response.json(user); } return new Response('Not Found', { status: 404 }); } http.createServer(createRequestListener(handler)).listen(3000); ``` -------------------------------- ### Headers Initialization with Config Source: https://github.com/mjackson/remix-the-web/blob/main/packages/headers/README.md Shows how to initialize the Headers class with a configuration object, specifying properties like Content-Type and Set-Cookie details, and how the Headers object stringifies to a standard HTTP header format. ```ts let headers = new Headers({ contentType: { mediaType: 'text/html', charset: 'utf-8', }, setCookie: [ { name: 'session', value: 'abc', path: '/' }, { name: 'theme', value: 'dark', expires: new Date('2021-12-31T23:59:59Z') }, ], }); console.log(`${headers}`); // Content-Type: text/html; charset=utf-8 // Set-Cookie: session=abc; Path=/ // Set-Cookie: theme=dark; Expires=Fri, 31 Dec 2021 23:59:59 GMT ``` -------------------------------- ### fetch-proxy Package Source: https://github.com/mjackson/remix-the-web/blob/main/README.md Seamlessly construct HTTP proxies with the familiar `fetch()` API. Ideal for API gateways or abstracting microservices. ```javascript import { fetchProxy } from "@remix-run/fetch-proxy"; // Example usage: const proxy = fetchProxy('https://api.example.com'); proxy.fetch('/users').then(response => console.log(response.json())); ``` -------------------------------- ### Basic Route Pattern Usage Source: https://github.com/mjackson/remix-the-web/blob/main/packages/route-pattern/README.md Demonstrates creating a RoutePattern for a blog URL with an optional HTML extension and matching against different URLs. ```tsx import { RoutePattern } from 'route-pattern'; // Blog with optional HTML extension let pattern = new RoutePattern('blog/:year-:month-:day/:slug(.html)'); pattern.match('https://remix.run/blog/2024-01-15/web-architecture'); // { params: { year: '2024', month: '01', day: '15', slug: 'web-architecture' } } pattern.match('https://remix.run/blog/2024-01-15/web-architecture.html'); // { params: { year: '2024', month: '01', day: '15', slug: 'web-architecture' } } ``` -------------------------------- ### Deno Benchmark Results Source: https://github.com/mjackson/remix-the-web/blob/main/packages/multipart-parser/README.md Performance results for @mjackson/multipart-parser and other multipart parsers when run with Deno. It includes metrics for different file sizes and quantities. ```shell > @mjackson/multipart-parser@0.10.1 bench:deno /Users/michael/Projects/remix-the-web/packages/multipart-parser > deno run --allow-sys ./bench/runner.ts Platform: Darwin (24.5.0) CPU: Apple M1 Pro Date: 6/13/2025, 12:28:12 PM Deno 2.3.6 ┌──────────────────┬──────────────────┬────────────────────┬──────────────────┬─────────────────────┐ │ (idx) │ 1 small file │ 1 large file │ 100 small files │ 5 large files │ ├──────────────────┼──────────────────┼────────────────────┼──────────────────┼─────────────────────┤ │ multipart-parser │ "0.01 ms ± 0.03" │ "1.03 ms ± 0.04" │ "0.05 ms ± 0.01" │ "10.05 ms ± 0.20" │ │ multipasta │ "0.02 ms ± 0.07" │ "1.04 ms ± 0.03" │ "0.16 ms ± 0.02" │ "10.10 ms ± 0.08" │ │ busboy │ "0.05 ms ± 0.19" │ "3.06 ms ± 0.15" │ "0.32 ms ± 0.05" │ "29.92 ms ± 0.24" │ │ @fastify/busboy │ "0.06 ms ± 0.14" │ "14.72 ms ± 11.42" │ "0.81 ms ± 0.20" │ "127.63 ms ± 35.77" │ └──────────────────┴──────────────────┴────────────────────┴──────────────────┴─────────────────────┘ ``` -------------------------------- ### Low-level Multipart Parsing from Buffer Source: https://github.com/mjackson/remix-the-web/blob/main/packages/multipart-parser/README.md This example shows how to use the low-level `parseMultipart` function to parse multipart data directly from a Uint8Array (buffer). It requires the message buffer and the boundary string as input. ```ts import { parseMultipart } from '@mjackson/multipart-parser'; // Assume 'message' is a Uint8Array containing multipart data let message = new Uint8Array(/* ... your multipart data ... */); let boundary = '----WebKitFormBoundary56eac3x'; // Replace with the actual boundary for (let part of parseMultipart(message, { boundary })) { // Process each part (e.g., part.name, part.contentType, part.data) console.log('Part name:', part.name); console.log('Part content type:', part.contentType); // For file parts, part.file will be a File object // For text parts, part.data will be the string content } ``` -------------------------------- ### Bun Benchmark Results Source: https://github.com/mjackson/remix-the-web/blob/main/packages/multipart-parser/README.md Performance results for @mjackson/multipart-parser and other multipart parsers when run with Bun. It includes metrics for different file sizes and quantities. ```shell > @mjackson/multipart-parser@0.10.1 bench:bun /Users/michael/Projects/remix-the-web/packages/multipart-parser > bun run ./bench/runner.ts Platform: Darwin (24.5.0) CPU: Apple M1 Pro Date: 6/13/2025, 12:27:31 PM Bun 1.2.13 ┌──────────────────┬────────────────┬────────────────┬─────────────────┬─────────────────┐ │ │ 1 small file │ 1 large file │ 100 small files │ 5 large files │ ├──────────────────┼────────────────┼────────────────┼─────────────────┼─────────────────┤ │ multipart-parser │ 0.01 ms ± 0.04 │ 0.86 ms ± 0.09 │ 0.04 ms ± 0.01 │ 8.32 ms ± 0.26 │ │ multipasta │ 0.02 ms ± 0.07 │ 0.87 ms ± 0.03 │ 0.25 ms ± 0.21 │ 8.27 ms ± 0.09 │ │ busboy │ 0.05 ms ± 0.17 │ 3.54 ms ± 0.10 │ 0.30 ms ± 0.03 │ 34.79 ms ± 0.38 │ │ @fastify/busboy │ 0.06 ms ± 0.18 │ 4.04 ms ± 0.08 │ 0.48 ms ± 0.06 │ 39.91 ms ± 0.37 │ └──────────────────┴────────────────┴────────────────┴─────────────────┴─────────────────┘ ``` -------------------------------- ### Create and Use Fetch Proxy Source: https://github.com/mjackson/remix-the-web/blob/main/packages/fetch-proxy/README.md Demonstrates how to create a fetch proxy using `createFetchProxy` and then use it to handle incoming requests. It shows how to proxy requests to a target server (remix.run) and process the response. ```typescript import { createFetchProxy } from '@mjackson/fetch-proxy'; // Create a proxy that sends all requests through to remix.run let proxy = createFetchProxy('https://remix.run'); // This fetch handler is probably running as part of your server somewhere... function handleFetch(request: Request): Promise { return proxy(request); } // Test it out by manually throwing a Request at it let response = await handleFetch(new Request('https://shopify.com')); let text = await response.text(); let title = text.match(/([^<]+)<\/title>/)[1]; assert(title.includes('Remix')); ``` -------------------------------- ### Handle File Uploads with multipart-parser Source: https://github.com/mjackson/remix-the-web/blob/main/packages/multipart-parser/README.md Example of parsing a multipart request and handling file uploads and form fields using the `parseMultipartRequest` function. It demonstrates accessing file data as an ArrayBuffer and processing text fields. ```typescript import { MultipartParseError, parseMultipartRequest } from '@mjackson/multipart-parser'; async function handleRequest(request: Request): void { try { for await (let part of parseMultipartRequest(request)) { if (part.isFile) { // Access file data in multiple formats let buffer = part.arrayBuffer; // ArrayBuffer console.log(`File received: ${part.filename} (${buffer.byteLength} bytes)`); console.log(`Content type: ${part.mediaType}`); console.log(`Field name: ${part.name}`); // Save to disk, upload to cloud storage, etc. await saveFile(part.filename, part.bytes); } else { let text = part.text; // string console.log(`Field received: ${part.name} = ${JSON.stringify(text)}`); } } } catch (error) { if (error instanceof MultipartParseError) { console.error('Failed to parse multipart request:', error.message); } else { console.error('An unexpected error occurred:', error); } } } ``` -------------------------------- ### HTTPS Support Source: https://github.com/mjackson/remix-the-web/blob/main/packages/node-fetch-server/README.md Demonstrates how to configure and run a server using HTTPS by integrating with Node.js's `https` module. It requires SSL certificate and private key files for secure connections. ```ts import * as https from 'node:https'; import * as fs from 'node:fs'; import { createRequestListener } from '@mjackson/node-fetch-server'; let options = { key: fs.readFileSync('private-key.pem'), cert: fs.readFileSync('certificate.pem'), }; let server = https.createServer(options, createRequestListener(handler)); server.listen(443, () => { console.log('HTTPS Server running on port 443'); }); ``` -------------------------------- ### Accept-Language Header Parsing and Matching Source: https://github.com/mjackson/remix-the-web/blob/main/packages/headers/README.md Details the `AcceptLanguage` header class for parsing language tags and checking language preferences. It includes methods to verify if a language is accepted and to get the most preferred language from a given array. ```ts import { AcceptLanguage } from '@mjackson/headers'; let header = new AcceptLanguage('en-US,en;q=0.9'); header.has('en-US'); // true header.has('en-GB'); // false header.accepts('en-US'); // true header.accepts('en-GB'); // true header.accepts('en'); // true header.accepts('fr'); // true header.getPreferred(['en-US', 'en-GB']); // 'en-US' header.getPreferred(['en', 'fr']); // 'en' for (let [language, quality] of header) { // ... } // Alternative init styles let header = new AcceptLanguage({ 'en-US': 1, en: 0.9 }); let header = new AcceptLanguage(['en-US', ['en', 0.9]]); ``` -------------------------------- ### Parse Tar Archive with Streams Source: https://github.com/mjackson/remix-the-web/blob/main/packages/tar-parser/README.md Demonstrates how to parse a tar archive streamed from a fetch request. It pipes the response body through a decompression stream (gzip) and then uses parseTar to process each entry, logging its name and size. This example highlights the composability with the Web Streams API. ```typescript import { parseTar } from '@mjackson/tar-parser'; let response = await fetch( 'https://github.com/mjackson/remix-the-web/archive/refs/heads/main.tar.gz', ); await parseTar(response.body.pipeThrough(new DecompressionStream('gzip')), (entry) => { console.log(entry.name, entry.size); }); ``` -------------------------------- ### node-fetch-server Package Source: https://github.com/mjackson/remix-the-web/blob/main/README.md Build Node.js HTTP servers using the web-standard `fetch()` API, promoting code consistency. ```javascript import { createServer } from "@remix-run/node-fetch-server"; const server = createServer({ fetch(request) { return new Response(`Hello from ${request.url}`); } }); server.listen(3000, () => { console.log("Server listening on port 3000"); }); ``` -------------------------------- ### Accept Header Parsing and Matching Source: https://github.com/mjackson/remix-the-web/blob/main/packages/headers/README.md Demonstrates the usage of the `Accept` header class for parsing media types and checking for accepted formats. It includes methods for checking if a specific media type is accepted and retrieving the preferred media type from a given list. ```ts import { Accept } from '@mjackson/headers'; let header = new Accept('text/html;text/*;q=0.9'); header.has('text/html'); // true header.has('text/plain'); // false header.accepts('text/html'); // true header.accepts('text/plain'); // true header.accepts('text/*'); // true header.accepts('image/jpeg'); // false header.getPreferred(['text/html', 'text/plain']); // 'text/html' for (let [mediaType, quality] of header) { // ... } // Alternative init styles let header = new Accept({ 'text/html': 1, 'text/*': 0.9 }); let header = new Accept(['text/html', ['text/*', 0.9]]); ``` -------------------------------- ### Asset Serving with Type Constraints Source: https://github.com/mjackson/remix-the-web/blob/main/packages/route-pattern/README.md Demonstrates RoutePattern for serving assets with constraints on file types, using a glob to match the path and an enum for extensions. ```tsx // Asset serving with type constraints let pattern = new RoutePattern('assets/*path.{jpg,png,gif,svg}'); pattern.match('https://remix.run/assets/images/logos/remix.svg'); // { params: { path: 'images/logos/remix' } } pattern.match('https://remix.run/assets/styles/main.css'); // null (wrong file type) ``` -------------------------------- ### Remix v2 Single Fetch Configuration Source: https://github.com/mjackson/remix-the-web/blob/main/README.md Instructions on how to enable 'Single Fetch' in Remix v2 to use Node's built-in fetch primitives, which is necessary for compatibility with these libraries. ```APIDOC Remix v2 Single Fetch: To use the packages from this repository with Remix v2, you need to enable the 'Single Fetch' feature. This ensures that Remix utilizes Node.js's built-in `fetch` primitives (`Request`, `Response`, `Headers`, etc.) instead of a potentially incompatible polyfill. How to enable: 1. Navigate to your Remix project's documentation or configuration files. 2. Locate the setting related to fetch behavior or polyfills. 3. Enable the 'Single Fetch' option. Refer to the official Remix documentation for the most up-to-date instructions: [https://remix.run/docs/en/main/guides/single-fetch#enabling-single-fetch](https://remix.run/docs/en/main/guides/single-fetch#enabling-single-fetch) This configuration is crucial for ensuring that the standard web APIs used by these packages are correctly implemented and accessible within your Remix application. ``` -------------------------------- ### Node.js Benchmark Results Source: https://github.com/mjackson/remix-the-web/blob/main/packages/multipart-parser/README.md Performance results for @mjackson/multipart-parser and other multipart parsers when run with Node.js. It includes metrics for different file sizes and quantities. ```shell > @mjackson/multipart-parser@0.10.1 bench:node /Users/michael/Projects/remix-the-web/packages/multipart-parser > node --disable-warning=ExperimentalWarning ./bench/runner.ts Platform: Darwin (24.5.0) CPU: Apple M1 Pro Date: 6/13/2025, 12:27:09 PM Node.js v24.0.2 ┌──────────────────┬──────────────────┬──────────────────┬──────────────────┬───────────────────┐ │ (index) │ 1 small file │ 1 large file │ 100 small files │ 5 large files │ ├──────────────────┼──────────────────┼──────────────────┼──────────────────┼───────────────────┤ │ multipart-parser │ '0.01 ms ± 0.03' │ '1.08 ms ± 0.08' │ '0.04 ms ± 0.01' │ '10.50 ms ± 0.38' │ │ multipasta │ '0.02 ms ± 0.06' │ '1.07 ms ± 0.02' │ '0.15 ms ± 0.02' │ '10.46 ms ± 0.11' │ │ busboy │ '0.06 ms ± 0.17' │ '3.07 ms ± 0.24' │ '0.24 ms ± 0.05' │ '29.85 ms ± 0.18' │ │ @fastify/busboy │ '0.05 ms ± 0.13' │ '1.23 ms ± 0.09' │ '0.45 ms ± 0.22' │ '11.81 ms ± 0.11' │ └──────────────────┴──────────────────┴──────────────────┴──────────────────┴───────────────────┘ ``` -------------------------------- ### Publish a Release Source: https://github.com/mjackson/remix-the-web/blob/main/CONTRIBUTING.md Publishes a previously tagged release to npm/JSR. This is the second step in the release process. ```sh pnpm run publish-release <tag> ``` ```sh pnpm run publish-release headers@1.0.0 ``` -------------------------------- ### Route Pattern with Protocol and Hostname Source: https://github.com/mjackson/remix-the-web/blob/main/packages/route-pattern/README.md Demonstrates specifying a protocol and hostname in a RoutePattern using '://' to define more specific URL structures. ```tsx let pattern2 = new RoutePattern('://:tenant.remix.run/admin'); pattern2.match('https://acme.remix.run/admin'); // { params: { tenant: 'acme' } } ``` -------------------------------- ### Basic Usage of LocalFileStorage Source: https://github.com/mjackson/remix-the-web/blob/main/packages/file-storage/README.md Demonstrates how to use LocalFileStorage to store, retrieve, and remove a File object. It shows how file metadata is preserved. ```ts import { LocalFileStorage } from '@mjackson/file-storage/local'; let storage = new LocalFileStorage('./user/files'); let file = new File(['hello world'], 'hello.txt', { type: 'text/plain' }); let key = 'hello-key'; // Put the file in storage. await storage.set(key, file); // Then, sometime later... let fileFromStorage = await storage.get(key); // All of the original file's metadata is intact fileFromStorage.name; // 'hello.txt' fileFromStorage.type; // 'text/plain' // To remove from storage await storage.remove(key); ``` -------------------------------- ### Working with Request Data Source: https://github.com/mjackson/remix-the-web/blob/main/packages/node-fetch-server/README.md Demonstrates how to handle different types of request data, including JSON payloads and URL search parameters, using standard web APIs within the node-fetch-server handler. ```typescript async function handler(request: Request) { let url = new URL(request.url); // Handle JSON data if (request.method === 'POST' && url.pathname === '/api/users') { try { let userData = await request.json(); // Validate required fields if (!userData.name || !userData.email) { return Response.json({ error: 'Name and email are required' }, { status: 400 }); } // Create user (your implementation) let newUser = { id: Date.now().toString(), ...userData, }; return Response.json(newUser, { status: 201 }); } catch (error) { return Response.json({ error: 'Invalid JSON' }, { status: 400 }); } } // Handle URL search params if (url.pathname === '/api/search') { let query = url.searchParams.get('q'); let limit = parseInt(url.searchParams.get('limit') || '10'); return Response.json({ query, limit, results: [], // Your search results here }); } return new Response('Not Found', { status: 404 }); } ``` -------------------------------- ### Streaming Responses Source: https://github.com/mjackson/remix-the-web/blob/main/packages/node-fetch-server/README.md Illustrates how to create streaming responses using the `ReadableStream` API, allowing for efficient handling of large or continuously generated data. ```typescript async function handler(request: Request) { if (request.url.endsWith('/stream')) { // Create a streaming response let stream = new ReadableStream({ async start(controller) { for (let i = 0; i < 5; i++) { controller.enqueue(new TextEncoder().encode(`Chunk ${i}\n`)); await new Promise((resolve) => setTimeout(resolve, 1000)); } controller.close(); }, }); return new Response(stream, { headers: { 'Content-Type': 'text/plain' }, }); } return new Response('Not Found', { status: 404 }); } ``` -------------------------------- ### Run Project Tests Source: https://github.com/mjackson/remix-the-web/blob/main/CONTRIBUTING.md Executes all automated tests for the project to ensure code integrity. It's recommended to run this after making changes. ```sh pnpm test ``` -------------------------------- ### RoutePattern API Documentation Source: https://github.com/mjackson/remix-the-web/blob/main/packages/route-pattern/README.md Provides the TypeScript definition for the RoutePattern class, including its constructor and the match method, along with the Match type definition. ```ts class RoutePattern { readonly source: string; constructor(source: string); match(url: string | URL): Match | null; } type Match = { params: Record<string, string | undefined>; }; ``` -------------------------------- ### Integrating with File Storage Source: https://github.com/mjackson/remix-the-web/blob/main/packages/form-data-parser/README.md Illustrates how to use the `file-storage` library with `parseFormData` for more flexible file storage. The `uploadHandler` saves files using `LocalFileStorage` and returns a reference to the stored file. ```ts import { LocalFileStorage } from '@mjackson/file-storage/local'; import type { FileUpload } from '@mjackson/form-data-parser'; import { parseFormData } from '@mjackson/form-data-parser'; // Set up storage for uploaded files const fileStorage = new LocalFileStorage('/uploads/user-avatars'); // Define how to handle incoming file uploads async function uploadHandler(fileUpload: FileUpload) { // Is this file upload from the <input type="file" name="user-avatar"> field? if (fileUpload.fieldName === 'user-avatar') { let storageKey = `user-${user.id}-avatar`; // Put the file in storage await fileStorage.set(storageKey, fileUpload); // Return a lazy File object that can access the stored file when needed return fileStorage.get(storageKey); } // Ignore unrecognized fields } ``` -------------------------------- ### Accept-Encoding Header Parsing and Matching Source: https://github.com/mjackson/remix-the-web/blob/main/packages/headers/README.md Illustrates the functionality of the `AcceptEncoding` header class, enabling parsing of content encodings and checking for supported encodings. It provides methods to determine if an encoding is accepted and to find the preferred encoding from a list. ```ts import { AcceptEncoding } from '@mjackson/headers'; let header = new AcceptEncoding('gzip,deflate;q=0.9'); header.has('gzip'); // true header.has('br'); // false header.accepts('gzip'); // true header.accepts('deflate'); // true header.accepts('identity'); // true header.accepts('br'); // true header.getPreferred(['gzip', 'deflate']); // 'gzip' for (let [encoding, weight] of header) { // ... } // Alternative init styles let header = new AcceptEncoding({ gzip: 1, deflate: 0.9 }); let header = new AcceptEncoding(['gzip', ['deflate', 0.9]]); ``` -------------------------------- ### Tag a Release Source: https://github.com/mjackson/remix-the-web/blob/main/CONTRIBUTING.md Tags a new release for a specific package with a given release type (e.g., 'minor', 'major', 'patch'). This is the first step in the release process. ```sh pnpm run tag-release <packageName> <releaseType> ``` ```sh pnpm run tag-release headers minor ``` -------------------------------- ### API Route Pattern with Versioning Source: https://github.com/mjackson/remix-the-web/blob/main/packages/route-pattern/README.md Shows how to define a RoutePattern for an API with versioning and an optional JSON format, and matching against different API URLs. ```tsx // API with versioning and optional format let pattern = new RoutePattern('api(/v:major.:minor)/users/:id(.json)'); pattern.match('https://remix.run/api/users/sarah'); // { params: { id: 'sarah' } } pattern.match('https://remix.run/api/v2.1/users/sarah.json'); // { params: { major: '2', minor: '1', id: 'sarah' } } ``` -------------------------------- ### Multi-tenant Application Route Pattern Source: https://github.com/mjackson/remix-the-web/blob/main/packages/route-pattern/README.md Illustrates using RoutePattern for multi-tenant applications, matching URLs with different tenant subdomains and an optional admin path. ```tsx // Multi-tenant applications let pattern = new RoutePattern('://:tenant.remix.run(/admin)/users/:id'); pattern.match('https://acme.remix.run/users/123'); // { params: { tenant: 'acme', id: '123' } } pattern.match('https://acme.remix.run/admin/users/123'); // { params: { tenant: 'acme', id: '123' } } ``` -------------------------------- ### headers Package Source: https://github.com/mjackson/remix-the-web/blob/main/README.md A comprehensive toolkit for effortlessly managing HTTP headers in your JavaScript applications. ```javascript import { Headers } from "@remix-run/headers"; // Example usage: const headers = new Headers({ "Content-Type": "application/json", "X-Custom-Header": "value" }); console.log(headers.get("Content-Type")); headers.append("Cache-Control", "no-cache"); ``` -------------------------------- ### lazy-file Package Source: https://github.com/mjackson/remix-the-web/blob/main/README.md Optimize performance with lazy-loaded, streaming `Blob`s and `File`s for JavaScript. ```javascript import { lazyFile } from "@remix-run/lazy-file"; // Example usage: const lazyBlob = lazyFile("/path/to/large.file"); // The file content is only read when needed const stream = await lazyBlob.stream(); console.log(stream); ``` -------------------------------- ### file-storage Package Source: https://github.com/mjackson/remix-the-web/blob/main/README.md Robust key/value storage tailored for JavaScript `File` objects, simplifying file management. ```javascript import { FileStorage } from "@remix-run/file-storage"; // Example usage: const storage = new FileStorage("/path/to/storage"); const file = new File(["content"], "example.txt"); await storage.set("myFile", file); const retrievedFile = await storage.get("myFile"); console.log(retrievedFile.name); ``` -------------------------------- ### Route Pattern Params Source: https://github.com/mjackson/remix-the-web/blob/main/packages/route-pattern/README.md Explains and demonstrates the use of parameters (e.g., ':id') within RoutePattern segments to capture dynamic parts of a URL. ```tsx let pattern = new RoutePattern('users/@:id'); pattern.match('https://remix.run/users/@sarah'); // { params: { id: 'sarah' } } let pattern = new RoutePattern('api/v:major.:minor'); pattern.match('https://remix.run/api/v2.1'); // { params: { major: '2', minor: '1' } } let pattern = new RoutePattern('products/:-shoes'); pattern.match('https://remix.run/products/tennis-shoes'); // { params: {} } ``` -------------------------------- ### Low-level API: Request and Response Handling Source: https://github.com/mjackson/remix-the-web/blob/main/packages/node-fetch-server/README.md Utilizes the low-level API of `@mjackson/node-fetch-server` to manually convert Node.js `IncomingMessage` to a Fetch API `Request` and send a Fetch API `Response` using `ServerResponse`. This offers more control for custom middleware and error handling. ```ts import * as http from 'node:http'; import { createRequest, sendResponse } from '@mjackson/node-fetch-server'; let server = http.createServer(async (req, res) => { // Convert Node.js request to Fetch API Request let request = createRequest(req, res, { host: process.env.HOST }); try { // Add custom headers or middleware logic let startTime = Date.now(); // Process the request with your handler let response = await handler(request); // Add response timing header let duration = Date.now() - startTime; response.headers.set('X-Response-Time', `${duration}ms`); // Send the response await sendResponse(res, response); } catch (error) { console.error('Server error:', error); res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Internal Server Error'); } }); server.listen(3000); ``` -------------------------------- ### form-data-parser Package Source: https://github.com/mjackson/remix-the-web/blob/main/README.md An enhanced `request.formData()` wrapper enabling efficient, streaming file uploads. ```javascript import { formDataParser } from "@remix-run/form-data-parser"; // Example usage within a fetch handler: async function handleRequest(request) { const formData = await formDataParser(request); const file = await formData.get("file"); console.log(file.name); } ```