This library creates a new Response, given a source Response and a Range header value.
npx @tessl/cli install tessl/npm-workbox-range-requests@7.3.0Workbox Range Requests is a specialized library that provides functionality for handling HTTP range requests in Progressive Web Apps and Service Workers. It enables developers to create partial responses from cached resources when clients request specific byte ranges, which is essential for streaming media content, large file downloads, and bandwidth optimization.
npm install workbox-range-requestsimport { createPartialResponse, RangeRequestsPlugin } from "workbox-range-requests";For individual imports:
import { createPartialResponse } from "workbox-range-requests/createPartialResponse.js";
import { RangeRequestsPlugin } from "workbox-range-requests/RangeRequestsPlugin.js";import { createPartialResponse, RangeRequestsPlugin } from "workbox-range-requests";
// Using createPartialResponse directly
self.addEventListener('fetch', (event) => {
if (event.request.headers.has('range')) {
event.respondWith(
caches.match(event.request).then(async (cachedResponse) => {
if (cachedResponse) {
return createPartialResponse(event.request, cachedResponse);
}
return fetch(event.request);
})
);
}
});
// Using RangeRequestsPlugin with Workbox strategies
import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
registerRoute(
({ url }) => url.pathname.endsWith('.mp4'),
new CacheFirst({
cacheName: 'videos',
plugins: [new RangeRequestsPlugin()],
})
);Workbox Range Requests is built around two key components:
Creates partial HTTP responses from full cached responses when handling range requests.
/**
* Given a Request and Response objects as input, this will return a
* promise for a new Response.
*
* If the original Response already contains partial content (i.e. it has
* a status of 206), then this assumes it already fulfills the Range:
* requirements, and will return it as-is.
*
* @param request A request, which should contain a Range header
* @param originalResponse A response
* @returns Either a 206 Partial Content response with the response body
* set to the slice of content specified by the request's Range
* header, or a 416 Range Not Satisfiable response if the
* conditions of the Range header can't be met
*/
async function createPartialResponse(
request: Request,
originalResponse: Response
): Promise<Response>;Usage Examples:
// Handle range request manually
const request = new Request('/video.mp4', {
headers: { range: 'bytes=1000-2000' }
});
const cachedResponse = await caches.match('/video.mp4');
if (cachedResponse) {
const partialResponse = await createPartialResponse(request, cachedResponse);
// Returns 206 Partial Content response with bytes 1000-2000
console.log(partialResponse.status); // 206
console.log(partialResponse.headers.get('content-range')); // bytes 1000-2000/totalSize
}
// Error handling - returns 416 for invalid ranges
const invalidRequest = new Request('/video.mp4', {
headers: { range: 'bytes=9999999-' }
});
const errorResponse = await createPartialResponse(invalidRequest, cachedResponse);
console.log(errorResponse.status); // 416 Range Not SatisfiablePlugin that automatically handles range requests within Workbox caching strategies.
/**
* The range request plugin makes it easy for a request with a Range header to
* be fulfilled by a cached response.
*
* It does this by intercepting the cachedResponseWillBeUsed plugin callback
* and returning the appropriate subset of the cached response body.
*/
class RangeRequestsPlugin implements WorkboxPlugin {
/**
* Workbox plugin callback that processes cached responses for range requests
* @param param Plugin callback parameters
* @param param.request The original request, which may or may not contain a Range header
* @param param.cachedResponse The complete cached response
* @param param.cacheName The name of the cache
* @param param.event The fetch event
* @param param.matchOptions Cache query options
* @param param.state Plugin state object
* @returns If request contains a Range header, then a new response with status 206
* whose body is a subset of cachedResponse is returned. Otherwise,
* cachedResponse is returned as-is
*/
cachedResponseWillBeUsed(param: {
cacheName: string;
request: Request;
cachedResponse?: Response;
event: ExtendableEvent;
matchOptions?: CacheQueryOptions;
state?: PluginState;
}): Promise<Response | void | null | undefined>;
}
// WorkboxPlugin interface from workbox-core
interface WorkboxPlugin {
cachedResponseWillBeUsed?: (param: {
cacheName: string;
request: Request;
cachedResponse?: Response;
event: ExtendableEvent;
matchOptions?: CacheQueryOptions;
state?: PluginState;
}) => Promise<Response | void | null | undefined>;
}
// Additional types from workbox-core
interface PluginState {
[key: string]: any;
}Usage Examples:
import { registerRoute } from 'workbox-routing';
import { CacheFirst } from 'workbox-strategies';
import { RangeRequestsPlugin } from 'workbox-range-requests';
// Use with any Workbox strategy for media files
registerRoute(
({ url }) => url.pathname.match(/\.(mp4|webm|mp3)$/),
new CacheFirst({
cacheName: 'media-cache',
plugins: [new RangeRequestsPlugin()],
})
);
// Works with other strategies too
import { StaleWhileRevalidate } from 'workbox-strategies';
registerRoute(
({ url }) => url.pathname.includes('/large-files/'),
new StaleWhileRevalidate({
cacheName: 'large-files',
plugins: [new RangeRequestsPlugin()],
})
);The library provides graceful error handling for various edge cases:
// The library handles these scenarios automatically:
// No range header - returns original response
const noRangeRequest = new Request('/file.pdf');
const response1 = await createPartialResponse(noRangeRequest, cachedResponse);
// Returns original cachedResponse unchanged
// Invalid range - returns 416 error
const invalidRequest = new Request('/file.pdf', {
headers: { range: 'invalid-range-header' }
});
const response2 = await createPartialResponse(invalidRequest, cachedResponse);
// Returns 416 Range Not Satisfiable
// Already partial content - passes through
const partialResponse = new Response(blob, { status: 206 });
const response3 = await createPartialResponse(rangeRequest, partialResponse);
// Returns the existing 206 response as-is