Skip to content
LogoLogo

Getting started

Portable is a set of small npm packages built only on web-standard APIs: Web Streams, Uint8Array, TextEncoder, fetch, and crypto.subtle. The same code runs in browsers, Cloudflare Workers, Deno, Bun, and Node 24+. You don't need polyfills or the nodejs_compat flag.

Install a package

Install only the packages you use. They are ESM-only, and their only runtime dependencies are other @portable/* packages.

npm install @portable/streams

Import a package

Import a package as a namespace. Export names are short, such as toJson, because the namespace shows the package. This Cloudflare Worker reads a JSON request body, and if the body passes 1 MB, it stops reading and answers 413:

import * as Streams from '@portable/streams';
 
export default {
  async fetch(request: Request) {
    try {
      const body = await Streams.toJson(request.body!, {
        maxBytes: 1_000_000,
      });
      return Response.json({ received: body });
    } catch (error) {
      if (error instanceof Streams.MaxBytesError) {
        return new Response('Body too large', { status: 413 });
      }
      throw error;
    }
  },
};

All packages follow the same conventions for names, options, errors, and cancellation.

Use in a browser

With a bundler, import the package as in the example above. Without one, import it from esm.sh in a module script:

import * as Streams from 'https://esm.sh/@portable/streams';

esm.sh rewrites the imports between @portable/* packages to esm.sh URLs. You don't need an import map.

Runtimes

The tests run in Chromium, Firefox, WebKit, Deno, Bun, and Node, and a check of every package runs in workerd, the Cloudflare Workers runtime.

@portable/smtp needs a TCP socket API, so it doesn't run in browsers.