CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-workbox-streams

A library that makes it easier to work with Streams in the browser.

Overview
Eval results
Files

Workbox Streams

Workbox Streams provides utilities for working with ReadableStreams in service workers and web applications. It offers functions to concatenate multiple stream sources into a single ReadableStream, create HTTP responses from concatenated streams, detect browser support for streaming APIs, and implement streaming strategies for Workbox routing.

Package Information

  • Package Name: workbox-streams
  • Package Type: npm
  • Language: TypeScript
  • Installation: npm install workbox-streams

Core Imports

import {
  concatenate,
  concatenateToResponse,
  isSupported,
  strategy,
  type StreamSource,
  type StreamsHandlerCallback
} from "workbox-streams";

For CommonJS:

const {
  concatenate,
  concatenateToResponse,
  isSupported,
  strategy
} = require("workbox-streams");

Basic Usage

import {
  concatenate,
  concatenateToResponse,
  isSupported,
  strategy
} from "workbox-streams";

// Check browser support for streaming
if (isSupported()) {
  // Concatenate multiple stream sources
  const sourcePromises = [
    fetch('/api/header'),
    fetch('/api/content'),
    fetch('/api/footer')
  ];

  const {done, stream} = concatenate(sourcePromises);
  
  // Or create a Response directly
  const {done: responseDone, response} = concatenateToResponse(
    sourcePromises,
    {'content-type': 'text/html'}
  );
  
  // Use in service worker
  self.addEventListener('fetch', (event) => {
    event.waitUntil(responseDone);
    event.respondWith(response);
  });
}

Architecture

Workbox Streams is built around streaming and fallback patterns:

  • Stream Concatenation: Core functionality for combining multiple stream sources sequentially
  • Browser Compatibility: Automatic detection and fallback for browsers without streaming support
  • Workbox Integration: Strategy functions designed for use with Workbox routing
  • Response Creation: Utilities to convert streams into standard Response objects
  • Promise Tracking: Support for service worker waitUntil() patterns

Capabilities

Stream Concatenation

Combines multiple stream sources into a single ReadableStream with sequential data flow.

/**
 * Takes multiple source Promises and returns a ReadableStream with sequential data
 * @param sourcePromises - Array of Promises resolving to StreamSource objects
 * @returns Object with completion Promise and ReadableStream
 */
function concatenate(sourcePromises: Promise<StreamSource>[]): {
  done: Promise<void>;
  stream: ReadableStream;
};

Usage Example:

import { concatenate } from "workbox-streams";

const sourcePromises = [
  Promise.resolve(new Response('Hello ')),
  Promise.resolve(new Response('World!')),
  fetch('/api/data')
];

const {done, stream} = concatenate(sourcePromises);

// Use the stream
const response = new Response(stream, {
  headers: {'content-type': 'text/plain'}
});

// Wait for completion
await done;

Response Stream Concatenation

Creates an HTTP Response with body consisting of concatenated stream data.

/**
 * Takes multiple source Promises and returns a Response with concatenated data
 * @param sourcePromises - Array of Promises resolving to StreamSource objects
 * @param headersInit - Optional headers for the response (if no 'Content-Type', defaults to 'text/html')
 * @returns Object with completion Promise and Response
 */
function concatenateToResponse(
  sourcePromises: Promise<StreamSource>[],
  headersInit?: HeadersInit
): {
  done: Promise<void>;
  response: Response;
};

Usage Example:

import { concatenateToResponse } from "workbox-streams";

const sourcePromises = [
  fetch('/template/header'),
  fetch('/content/body'),
  fetch('/template/footer')
];

// With headers
const {done, response} = concatenateToResponse(
  sourcePromises,
  {
    'content-type': 'text/html',
    'cache-control': 'max-age=3600'
  }
);

// Without headers (defaults to 'text/html')
const {done: done2, response: response2} = concatenateToResponse(sourcePromises);

// Use in service worker
self.addEventListener('fetch', (event) => {
  if (event.request.url.endsWith('/composite-page')) {
    event.waitUntil(done);
    event.respondWith(response);
  }
});

Browser Support Detection

Determines whether the current browser supports streaming features.

/**
 * Checks if browser supports ReadableStream construction for streaming responses
 * @returns true if streaming is supported, false otherwise
 */
function isSupported(): boolean;

Usage Example:

import { isSupported, concatenateToResponse } from "workbox-streams";

function createCompositeResponse(sources: Promise<StreamSource>[], headers: HeadersInit) {
  if (isSupported()) {
    // Use streaming concatenation
    return concatenateToResponse(sources, headers);
  } else {
    // Fallback to waiting for all sources
    return Promise.all(sources).then(resolvedSources => {
      // Manual concatenation fallback
      return new Response(/* combined content */, {headers});
    });
  }
}

Workbox Strategy Integration

Creates a strategy function compatible with Workbox routing that handles streaming with automatic fallback.

/**
 * Creates a Workbox-compatible strategy for streaming responses with fallback
 * @param sourceFunctions - Array of handler functions returning StreamSource objects
 * @param headersInit - Optional headers for responses (if no 'Content-Type', defaults to 'text/html')
 * @returns RouteHandlerCallback compatible with Workbox routing
 */
function strategy(
  sourceFunctions: StreamsHandlerCallback[],
  headersInit?: HeadersInit
): RouteHandlerCallback;

Usage Example:

import { strategy } from "workbox-streams";
import { registerRoute } from "workbox-routing";

// Define source functions
const sourceFunctions = [
  // Header source
  ({url, request}) => fetch('/api/header'),
  // Dynamic content based on URL
  ({url, request}) => fetch(`/api/content${url.pathname}`),
  // Footer source
  ({url, request}) => fetch('/api/footer')
];

// Create streaming strategy with headers
const streamingStrategy = strategy(sourceFunctions, {
  'content-type': 'text/html',
  'cache-control': 'no-cache'
});

// Or without headers (defaults to 'text/html')
const basicStrategy = strategy(sourceFunctions);

// Register with Workbox routing
registerRoute(
  ({url}) => url.pathname.startsWith('/dynamic/'),
  streamingStrategy
);

Types

StreamSource

Union type representing valid stream sources.

/**
 * Valid source types for streaming operations
 */
type StreamSource = Response | ReadableStream | BodyInit;

Where BodyInit includes: Blob | BufferSource | FormData | URLSearchParams | string

StreamsHandlerCallback

Interface for handler functions used in streaming strategies.

/**
 * Handler function interface for streaming strategies
 */
interface StreamsHandlerCallback {
  (options: RouteHandlerCallbackOptions): Promise<StreamSource> | StreamSource;
}

/**
 * Generic object interface for key-value mapping (from workbox-core)
 */
interface MapLikeObject {
  [key: string]: any;
}

/**
 * Options passed to handler callbacks (from workbox-core)
 */
interface RouteHandlerCallbackOptions {
  event: ExtendableEvent;
  request: Request;
  url: URL;
  params?: string[] | MapLikeObject;
}

/**
 * Route handler callback interface (from workbox-core)
 */
interface RouteHandlerCallback {
  (options: RouteHandlerCallbackOptions): Promise<Response>;
}

Error Handling

Stream Processing Errors

When stream processing fails, errors are propagated through the Promise chain:

import { concatenate } from "workbox-streams";

const {done, stream} = concatenate([
  fetch('/api/source1'),
  fetch('/api/source2')
]);

// Handle errors in the completion promise
done.catch(error => {
  console.error('Stream concatenation failed:', error);
});

// Errors also propagate through the stream
const response = new Response(stream);
response.text().catch(error => {
  console.error('Stream reading failed:', error);
});

Opaque Response Handling

Workbox Streams throws a WorkboxError when attempting to stream opaque responses:

// This will throw WorkboxError: 'opaque-streams-source'
const opaqueResponse = await fetch('https://external-api.com/data', {
  mode: 'no-cors' // Creates opaque response
});

const {stream} = concatenate([Promise.resolve(opaqueResponse)]);
// Error: Cannot read from opaque response

Browser Compatibility

  • Full Support: Browsers with ReadableStream constructor (Chrome 43+, Firefox 65+, Safari 10.1+)
  • Fallback Mode: Automatic fallback to blob-based concatenation for older browsers
  • Service Worker Context: Designed for service worker environments but works in main thread
  • Streaming Benefits: Only available in supporting browsers; fallback mode waits for all sources to complete

Install with Tessl CLI

npx tessl i tessl/npm-workbox-streams
Workspace
tessl
Visibility
Public
Created
Last updated
Describes
npmpkg:npm/workbox-streams@7.3.x