remix-the-web

repository·main·Indexed 21 days ago

https://github.com/mjackson/remix-the-web

A collection of web-standard-focused utility packages designed for portability across JavaScript runtimes including Node.js, Bun, Deno, and Cloudflare Workers. The library includes @mjackson/fetch-proxy for creating fetch proxies, @mjackson/file-storage for key/value File object storage, @mjackson/form-data-parser for streaming multipart/form-data parsing, @mjackson/headers for type-safe HTTP header manipulation, and @mjackson/lazy-file for lazy, streaming Blob and File implementations.

Tokens
34.6K
Snippets
120
Records
144
Agent score
75%

What's inside remix-the-web

  1. Overview of remix-the-web packages

    main

    remix-the-web is a collection of single-responsibility packages designed to build modern web applications using web standards. These tools are built to be portable across multiple JavaScript runtimes, including Node.js, Bun, Deno, and Cloudflare Workers.

    By prioritizing web standards like the Web Streams API, Uint8Array, Web Crypto API, Blob, and File, these packages ensure your code is interoperable and future-proof. While designed with Remix in mind, they can be used with any framework or custom web implementation.

  2. What is lazy-file?

    main

    lazy-file is a lazy, streaming Blob/File implementation for JavaScript. It allows you to create Blob and File objects that defer reading their contents until they are actually needed. This is ideal for server-side environments where file contents might be too large to fit in memory, as it uses streaming to avoid buffering the entire content at once.

    Key features:

    • Deferred loading: Minimizes memory usage by only reading data when requested.
    • Compatibility: LazyBlob extends Blob and LazyFile extends File, so they can be used anywhere a standard Blob or File is expected.
    • Standard API: Accepts the same constructor arguments as the original Blob() and File() constructors.
    • Streaming Slices: Supports Blob.slice() even on streaming content.
  3. Key features of @mjackson/tar-parser

    main

    Core Capabilities

    • Environment Agnostic: Runs in any JavaScript environment (Node.js, Bun, Deno, Browsers, etc.).
    • Web Streams API: Built on standard Web Streams, making it highly composable with fetch() and other stream transformations.
    • Format Support: Supports POSIX, GNU, and PAX tar formats.
    • Memory Efficient: Does not buffer the entire archive in memory during normal usage.
    • Zero Dependencies: Lightweight and secure with no external dependencies.
  4. What is FileStorage and when to use it?

    main

    Concept

    file-storage provides a key/value interface for storing JavaScript File objects on a server. While localStorage is designed for string-based key/value pairs in the browser, file-storage is designed for handling actual file data on the server side.

    Key Features

    • Metadata Preservation: Automatically preserves file.name, file.type, and file.lastModified.
    • Streaming Support: Supports streaming file content to and from storage.
    • Generic Interface: The FileStorage interface allows you to swap local storage for large object storage backends (e.g., AWS S3, Cloudflare R2) without changing your application logic.
  5. Related packages for multipart handling

    main

    If you are using multipart-parser, you may also be interested in these related packages in the remix-the-web ecosystem:

    • form-data-parser: A higher-level package that uses multipart-parser internally to parse multipart requests and automatically generate FileUpload objects for storage.
    • headers: A utility used internally to parse HTTP headers and extract metadata (such as filename and content-type) for each MultipartPart.
  6. How @mjackson/headers works

    main

    @mjackson/headers is a subclass of the standard Web Headers API. It provides type-safe accessors and automatic parsing/stringification for complex headers like Accept, Content-Type, Cookie, and Set-Cookie. Because it is a subclass, it can be used anywhere a standard Headers object is expected (e.g., in fetch calls or as a response header).

    Key features:

    • Type-Safe Accessors: Access media types, quality factors, and cookie attributes via structured properties.
    • Automatic Parsing: Raw header strings are converted into structured objects.
    • Fluent Interface: Expressive API for reading and writing headers.
    • Drop-in Enhancement: Compatible with existing code expecting standard Headers.
    • Individual Utilities: Standalone classes are available for specific headers if a full Headers object isn't needed.
    import Headers from '@mjackson/headers';
    
    // Use in a fetch()
    let response = await fetch('https://example.com', {
      headers: new Headers(),
    });
    
    // Convert from DOM Headers
    let headers = new Headers(response.headers);
  7. How RoutePattern URL components work

    main

    Route patterns are conceptually split into four parts:

    '<protocol>://<hostname>/<pathname>?<search>'

    • Pathname-only (Default): If you don't specify a protocol or hostname, the pattern matches the pathname. Everything after the first ? is treated as the search component.
    • Full URL: To specify a protocol or hostname, you must use :// before any / or ?.
    • Search: The search part is treated as URLSearchParams and is not part of the pattern matching logic for params/globs/etc.

    Example of pathname-only matching:

    let pattern = new RoutePattern('blog/:id');
    pattern.match('https://remix.run/blog/hello-world');
    // { params: { id: 'hello-world' } }

    Example of full URL matching (protocol/hostname):

    let pattern2 = new RoutePattern('://:tenant.remix.run/admin');
    pattern2.match('https://acme.remix.run/admin');
    // { params: { tenant: 'acme' } }
  8. Match multi-segment paths with Globs

    main

    Globs match dynamic parts of a URL that can span multiple segments. They are prefixed with an asterisk (*).

    • Named Glob: *name captures the matched string into the params object.
    • Unnamed Glob: * matches the segment but does not capture the value.

    Example:

    let pattern = new RoutePattern('://app.unpkg.com/*path/dist/:file.mjs');
    pattern.match('https://app.unpkg.com/preact@10.26.9/files/dist/preact.mjs');
    // { params: { path: 'preact@10.26.9/files', file: 'preact' }}
    let pattern = new RoutePattern('://app.unpkg.com/*path/dist/:file.mjs');
    
    pattern.match('https://app.unpkg.com/preact@10.26.9/files/dist/preact.mjs');
    // { params: { path: 'preact@10.26.9/files', file: 'preact' }}
  9. Define optional segments with Optionals

    main

    You can make parts of a pattern optional by wrapping them in parentheses (). This allows the pattern to match both the versioned and non-versioned versions of a URL.

    let pattern = new RoutePattern('api(/v:version)/users');
    
    pattern.match('https://remix.run/api/users');
    // { params: {} }
    
    pattern.match('https://remix.run/api/v2/users');
    // { params: { version: '2' } }
  10. Define dynamic parameters with Params

    main

    Params match dynamic parts of a URL within a single segment. They are prefixed with a colon (:).

    • Standard Param: :name captures the value.
    • Multiple Params: You can include multiple params in one segment (e.g., v:major.:minor).
    • Unnamed Params: If you omit the name (e.g., :-shoes), the value is matched but not captured in the params object.
    • Prefixes: You can include static characters before the param (e.g., @:id).
    // Standard param
    let pattern = new RoutePattern('users/@:id');
    pattern.match('https://remix.run/users/@sarah');
    // { params: { id: 'sarah' } }
    
    // Multiple params in one segment
    let pattern2 = new RoutePattern('api/v:major.:minor');
    pattern2.match('https://remix.run/api/v2.1');
    // { params: { major: '2', minor: '1' } }
    
    // Unnamed param (matched but not captured)
    let pattern3 = new RoutePattern('products/:-shoes');
    pattern3.match('https://remix.run/products/tennis-shoes');
    // { params: {} }
  11. Match specific values with Enums

    main

    Enums allow you to match against a specific set of static values using curly braces {}. This is useful for constraining segments to specific file extensions or keywords.

    let pattern = new RoutePattern('files/:filename.{jpg,png,gif}');
    
    pattern.match('https://remix.run/files/logo.png');
    // { params: { filename: 'logo' } }