Streamer API reference
The exports of @prose-reader/streamer and their signatures: manifest and resource generation, the Streamer and ServiceWorkerStreamer classes, and configure().
The public surface of @prose-reader/streamer. Everything below is exported from the package root (@prose-reader/streamer) unless a subpath is called out explicitly.
import { generateManifestFromArchive, generateResourceFromArchive,} from "@prose-reader/streamer"The package splits into a few areas. Archives and hooks have dedicated guides — this page is the canonical index of what is exported and documents the pieces that don't have a page of their own (manifest/resource generation and the streaming classes).
| Area | Exports | Where |
|---|---|---|
| Manifest & resources | generateManifestFromArchive, generateResourceFromArchive, createManifestResourceHref |
below |
| Streaming | Streamer, ServiceWorkerStreamer |
below · Node · Service Worker · Web (dom) |
| Hooks | StreamerHooks, StreamerManifestHook*, StreamerResourceHook*, HookResource types |
Hooks |
| Setup | configure |
below |
Archives
Section titled “Archives”The streamer consumes archives, it does not create or understand them: the Archive type, the createArchiveFrom* creators, the file-accessor factories, the lookup helpers, and the whole resolve layer (resolveArchive, resolveArchiveReadingOrder, resolveArchiveToc, the ResolvedMetadata vocabulary) all live in @prose-reader/archive-reader and are covered in full on the Archive Reader page. Building an Archive is always the first step, whatever the source.
Generic string helpers previously exported here (createXmlSafeId, createUniqueXmlSafeId, createXmlSafeIdFactory, sortByTitleComparator, removeTrailingSlash, getUriBasename, getUriBasePath) moved to @prose-reader/shared.
Manifest & resource generation
Section titled “Manifest & resource generation”generateManifestFromArchive(archive, options?)
Section titled “generateManifestFromArchive(archive, options?)”generateManifestFromArchive( archive: Archive, options?: { baseUrl?: string; hooks?: StreamerManifestHookFactory[] },): Promise<Manifest>Produces a reader Manifest: the archive is resolved by @prose-reader/archive-reader's resolveArchive (with the viewport layoutScan enabled), mapped into serving space (baseUrl rebasing, XML-safe ids, guide passthrough, audiobook/folder toc), then handed to any user hooks before the final defaults. The manifest keeps two facts from the container: its mediaType, the media type of the book file as resolveArchive resolves it, and each spine item's containerUri, the path of the archive record it shows, with no serving base.
import { createArchiveFromJszip } from "@prose-reader/archive-reader/archives/createArchiveFromJszip"import { generateManifestFromArchive } from "@prose-reader/streamer"
const archive = await createArchiveFromJszip(zip)const manifest = await generateManifestFromArchive(archive)generateResourceFromArchive(archive, resourcePath, options?)
Section titled “generateResourceFromArchive(archive, resourcePath, options?)”generateResourceFromArchive( archive: Archive, resourcePath: string, options?: { hooks?: StreamerResourceHookFactory[] },): Promise<HookResource> // resolved resource with a guaranteed `body` (string | Blob)Resolves a single archive resource by URI, running it through the resource hook pipeline (default / self-closing-tags / css / calibre fixes, plus any user hooks) and falling back to the raw file blob when no hook sets a body. A resource the package document lists is served with the media type its manifest item declares, the item whose href names that record.
createManifestResourceHref({ baseUrl?, resourcePath })
Section titled “createManifestResourceHref({ baseUrl?, resourcePath })”createManifestResourceHref(params: { baseUrl?: string // default "" resourcePath: string}): stringBuilds the href used for a manifest resource. An absolute http(s):// resourcePath is returned untouched (when no baseUrl is given); otherwise the path is prefixed with baseUrl (a trailing / is added when missing) or with file:// when no baseUrl is provided. The result is always passed through encodeURI, and #/? characters appearing in the resource path itself are additionally escaped (%23/%3F) — in an archive filename they are data, not fragment/query delimiters.
import { createManifestResourceHref } from "@prose-reader/streamer"
createManifestResourceHref({ resourcePath: "OEBPS/chapter-1.xhtml" })// "file://OEBPS/chapter-1.xhtml"
createManifestResourceHref({ baseUrl: "https://cdn.example.com/book", resourcePath: "OEBPS/chapter-1.xhtml" })// "https://cdn.example.com/book/OEBPS/chapter-1.xhtml"Streaming
Section titled “Streaming”The streaming classes turn manifest and resource requests into HTTP Responses. See Node, Web (dom) and Service Worker for end-to-end setups.
Streamer
Section titled “Streamer”new Streamer(options: { getArchive: (key: string) => Promise<Archive> cleanArchiveAfter?: number // ms an unused archive stays loaded, default 5 minutes hooks?: StreamerHooks onError?: (error: unknown) => Response onManifestSuccess?: (params: { manifest: Manifest; archive: Archive }) => Observable<Manifest> | Promise<Manifest>})
streamer.fetchManifest(params?: { key?: string; baseUrl?: string }): Promise<Response>streamer.fetchResource(params: { key?: string; resourcePath: string; request?: Request }): Promise<Response>streamer.accessArchive(key?: string): Observable<{ archive: Archive; release: () => void }>streamer.accessArchiveWithoutLock(key?: string): Observable<Archive>streamer.prune(): voidCore archive-serving engine: it loads archives by key through getArchive, keeps them loaded while they are in use, and generates the matching Responses. fetchManifest / fetchResource produce the manifest and resource responses (resource responses support HTTP range requests); accessArchive acquires a locked archive handle you release with release(); accessArchiveWithoutLock yields just the archive and releases immediately; prune purges the archive cache.
The streamer keeps the archive of the last key it was asked for: asking for another key closes the previous archive once nothing uses it. A call without a key uses a default one, so a streamer serving a single book can leave keys out:
const streamer = new Streamer({ getArchive: async () => archive })
const manifest = await streamer.fetchManifest().then((response) => response.json())const resourceResponse = await streamer.fetchResource({ resourcePath: manifest.spineItems[0].href })To serve another book from the same streamer, give each book its own key, or call prune() before asking for the next one.
ServiceWorkerStreamer
Section titled “ServiceWorkerStreamer”new ServiceWorkerStreamer(options: /* Streamer options */ & { getUriInfo: (event: FetchEvent) => { baseUrl: string } | undefined})
serviceWorkerStreamer.fetchEventListener(event: FetchEvent): voidA Streamer subclass for service-worker fetch events. getUriInfo maps a request to its baseUrl (or undefined to ignore the request); the bound fetchEventListener parses the request URL into { key, resourcePath } and calls event.respondWith using fetchManifest (for /manifest URLs) or fetchResource otherwise.
Debug logging
Section titled “Debug logging”Internal Report logging auto-configures from the globalThis.__PROSE_READER_DEBUG flag (set it to true or "true"). The flag is read from globalThis, so it also works where window does not exist — service workers, web workers, node — as long as it is set before the prose-reader modules evaluate (e.g. an early side-effect import in a worker bundle). This is how every package's logging is enabled, @prose-reader/archive-reader included.
configure(options?)
Section titled “configure(options?)”configure(options?: { enableReport?: boolean }): voidExplicit toggle for the streamer's own Report logging, for setups where the global flag cannot be set early enough (it force-enables or force-disables regardless of the flag).