blob
js/globals/blob.ts
Blob and File globals (WHATWG File API).
Blob is an immutable byte sequence with an associated MIME type. It is
the standard way to carry binary data in web APIs: fetch Request/Response
bodies, FormData values, FileReader, and FileReaderSync all work in terms of
Blobs. File extends Blob with a name and lastModified timestamp.
Alongside the two globals this module implements the reader interfaces from
the same spec: FileReader (asynchronous, event-based), FileReaderSync
(the worker-only synchronous variant), and the array-like FileList. The
WebIDL surface details WPT checks for — enumerable prototype members,
constructor length values, readonly FileReader state constants, and
string tags — are patched onto the classes at the bottom of the file.
WHATWG File API: https://w3c.github.io/FileAPI/
Internal byte storage
All Blob content is eagerly concatenated into a single Uint8Array on
construction. String parts honor endings: 'native' by normalizing CRLF,
CR, and LF sequences to the runtime's native newline before UTF-8 encoding;
non-string parts remain byte-preserving. The WHATWG spec allows lazy
concatenation (Blob objects can reference other Blobs by ref), but eager
flattening is simpler and correct for our use case where Blobs are typically
small-to-medium in-memory values rather than multi-GB file handles.
The BYTES_INIT sentinel
slice() needs to construct a Blob directly from raw bytes, bypassing the
normal parts iteration and normalization logic. Rather than adding a
public constructor overload or a static factory, we use a module-private
Symbol('bytes-init') as a sentinel first argument. When the constructor
sees parts === BYTES_INIT, it treats the second argument as
{ bytes: Uint8Array, type: string } and uses those values directly.
This keeps the fast path clean and invisible to external callers.
The _blobBytes WeakMap
_normalizePart() needs to read the internal bytes of a Blob (or File)
that appears as a part in another Blob's constructor. Since #bytes is a
private field only accessible within the class body, module-level helpers
can't reach it directly. The _blobBytes WeakMap is populated in the
constructor (_blobBytes.set(this, this.#bytes)) to give module-level
code read access to internal bytes without exposing a public getter.
arrayBuffer() returns a copy
blob.arrayBuffer() returns a copy of the internal buffer (via
buffer.slice()), not a view into it. This prevents callers from mutating
the Blob's internal state through the returned ArrayBuffer. bytes()
similarly returns a fresh new Uint8Array(this.#bytes) copy. The stream()
method returns a byte-oriented ReadableStream that enqueues one copied
chunk and closes, and textStream() does the same with the decoded UTF-8
text — sufficient for piping Blob bodies without a chunking layer.
slice() semantics
slice(start, end, contentType) follows the WHATWG spec: negative indices
are resolved relative to the blob size, values are clamped to [0, size,
and end < start produces an empty Blob. The content type of the resulting
Blob is the provided contentType (lowercased), or empty string if omitted.
// Blob and File are available via globalThis
const blob = new Blob(['hello, ', 'world'], { type: 'text/plain' });
blob.size; // 12
await blob.text(); // 'hello, world'
await blob.bytes(); // Uint8Array
await blob.arrayBuffer(); // ArrayBuffer
blob.slice(0, 5); // new Blob containing 'hello'
const file = new File([blob], 'hello.txt', { type: 'text/plain' });
file.name; // 'hello.txt'
file.lastModified; // number (ms since epoch)
const syncReader = new FileReaderSync();
syncReader.readAsText(blob); // 'hello, world'
Types
type BlobPart = string | ArrayBuffer | ArrayBufferView | Blob
Value accepted by the Blob and File constructors as one input part.
Strings are UTF-8 encoded after optional line-ending normalization. ArrayBuffer and ArrayBufferView values are copied as bytes, and Blob/File parts contribute their immutable byte contents.
type FileReaderHandler = ((event: Event) => void) | null
Event handler attribute type used by FileReader.
Assigning a function installs the handler for the corresponding event.
Assigning null or a non-function value clears it, matching WebIDL event
handler attribute behavior.
Interfaces
interface BlobOptions {
Options accepted by the Blob constructor.
type is lowercased and must contain only printable ASCII characters or it
is normalized to the empty string. endings controls only string parts:
'transparent' preserves line endings and 'native' normalizes CRLF, CR,
and LF sequences to the runtime's native newline before UTF-8 encoding.
Properties
type?: string
endings?: EndingType
interface FileOptions extends BlobOptions {
Options accepted by the File constructor.
In addition to Blob options, lastModified supplies the millisecond Unix
timestamp exposed by the File instance. When omitted, construction captures
Date.now().
Properties
lastModified?: number
interface FileList {
Array-like list of File objects.
FileList objects are produced by web APIs such as file inputs and drag/drop. Fino exposes the interface for File API compatibility; there is no public constructor, matching browsers.
try { new FileList(); } catch (err) { console.log(err instanceof TypeError); }
Readonly Properties
readonly length: number
Number of File objects in the list.
Methods
item(index: number): File | null
Return the File at the given index, or null when out of range.
The index is coerced to an unsigned 32-bit integer, matching WebIDL. Throws a TypeError when called with no arguments.
Classes
class Blob {
Web Blob facade backed by an immutable in-memory byte store.
Blob parts are eagerly normalized and concatenated during construction. MIME types are lowercased and rejected to the empty string when they contain non-ASCII printable characters. Methods that expose bytes return copies.
const blob = new Blob(['hello'], { type: 'TEXT/PLAIN' });
blob.type; // "text/plain"
await blob.text(); // "hello"
Constructors
constructor(parts?: Iterable<BlobPart> | null, options?: BlobOptions | null)
Create a Blob from strings, buffers, views, or other Blobs.
Omitted parts create an empty Blob. Non-iterable parts, including null,
throw a TypeError. endings: 'native' normalizes string-part line endings
before encoding; the default transparent preserves them. The internal
BYTES_INIT path is private to this module and is used by slice() to avoid
re-normalizing already-owned bytes.
const bytes = new Uint8Array([104, 105]);
const blob = new Blob(['prefix-', bytes], { type: 'text/plain' });
blob.size; // 9
constructor(
parts?: Iterable<BlobPart> | typeof BYTES_INIT | null,
options?: BlobOptions | InternalBlobOptions | null
)
Getters
get size()
Number of bytes in the Blob.
The value is computed from the immutable internal byte store.
new Blob(['abc']).size; // 3
get type()
Normalized MIME type for the Blob.
The value is lowercased. Invalid type strings containing characters outside U+0020 through U+007E become the empty string.
new Blob([], { type: 'Application/JSON' }).type; // "application/json"
Methods
slice(start?: number, end?: number, contentType?: string): Blob
Return a new Blob containing a byte range from this Blob.
Negative indexes are resolved from the end, bounds are clamped to the Blob size, and end values before start produce an empty Blob. The contentType argument becomes the returned Blob type after the same normalization rules.
const blob = new Blob(['abcdef']);
await blob.slice(1, 4).text(); // "bcd"
async text(): Promise<string>
Decode the Blob bytes as UTF-8 text.
Invalid UTF-8 sequences follow the internal decoder's replacement behavior. The method resolves to a string and does not mutate the Blob.
const text = await new Blob(['hello']).text();
textStream(): ReadableStream<string>
Create a ReadableStream that emits the Blob contents as UTF-8 text.
The MIME type charset parameter is ignored, matching the File API text decoding rules used by text(). Empty blobs close without emitting chunks.
const chunks = [];
for await (const chunk of new Blob(['hi']).textStream()) chunks.push(chunk);
async arrayBuffer(): Promise<ArrayBuffer>
Copy the Blob bytes into a new ArrayBuffer.
The returned buffer is detached from Blob storage, so modifications to a view over it cannot change the Blob.
const buffer = await new Blob(['hi']).arrayBuffer();
new Uint8Array(buffer).byteLength; // 2
async bytes(): Promise<Uint8Array>
Copy the Blob bytes into a new Uint8Array.
This is a convenience over arrayBuffer() when callers want a typed array. The returned array is safe to mutate.
const bytes = await new Blob(['hi']).bytes();
bytes[0]; // 104
stream(): ReadableStream
Create a ReadableStream for the Blob contents.
The current implementation emits one copied Uint8Array chunk and then closes. It is suitable for piping Blob bodies without exposing storage.
const stream = new Blob(['hi']).stream();
const reader = stream.getReader();
await reader.read();
class File extends Blob {
File extends Blob with a filename and lastModified timestamp.
File contents use the same eager in-memory byte store as Blob. The name is string-coerced and lastModified defaults to Date.now() when omitted.
const file = new File(['hello'], 'hello.txt', { type: 'text/plain' });
file.name; // "hello.txt"
Constructors
constructor(parts: Iterable<BlobPart> | null, name: string, options?: FileOptions | null)
Create a File from Blob parts and metadata.
The options type and endings are passed through Blob normalization. lastModified is truncated to an integer millisecond timestamp; when absent, Date.now() is captured at construction time.
const file = new File(['data'], 'data.bin', { lastModified: 0 });
file.lastModified; // 0
Getters
get name()
File name supplied at construction.
The name is not path-normalized or sanitized; callers should validate it before using it on a filesystem.
new File([], 'avatar.png').name; // "avatar.png"
get lastModified()
Last modified time in Unix epoch milliseconds.
The value is an integer and defaults to the construction time.
new File([], 'x', { lastModified: 123 }).lastModified; // 123
class FileReader extends EventTarget {
Asynchronous Blob reader from the File API.
FileReader reads an in-memory Blob or File as text, an ArrayBuffer,
a binary string, or a data URL. Reads transition through EMPTY, LOADING,
and DONE, dispatching the standard loadstart, progress, load,
abort, error, and loadend events. Fino supports Blob-backed reads and
does not install filesystem-backed browser file handles.
const reader = new FileReader();
reader.onload = () => console.log(reader.result);
reader.readAsText(new Blob(['hello']));
Static Properties
static EMPTY
No read has started.
new FileReader().readyState === FileReader.EMPTY; // true
static LOADING
A read is currently in progress.
FileReader.LOADING; // 1
static DONE
The read completed, failed, or was aborted.
FileReader.DONE; // 2
Getters
get onloadstart()
Event handler invoked when a read begins (the loadstart event).
Assigning a non-function clears the handler to null, matching WebIDL event handler attributes. Handlers run in addition to any listeners added via addEventListener.
get onprogress()
Event handler for progress events.
Fino dispatches a single progress event per read, and only for non-empty Blobs — reads are in-memory so there is no incremental progress to report.
get onload()
Event handler invoked when a read completes successfully (load).
By the time this fires, result holds the decoded value and readyState
is DONE.
const reader = new FileReader();
reader.onload = () => console.log(reader.result);
reader.readAsText(new Blob(['hi']));
get onabort()
Event handler invoked when an in-progress read is cancelled via abort().
get onerror()
Event handler invoked when a read fails (error).
The failure is available on the error property as a DOMException;
result is null.
get onloadend()
Event handler for loadend, fired after load, error, or abort.
Use this for cleanup that must run regardless of how the read finished.
get EMPTY()
Instance mirror of FileReader.EMPTY, required by WebIDL.
get LOADING()
Instance mirror of FileReader.LOADING, required by WebIDL.
get DONE()
Instance mirror of FileReader.DONE, required by WebIDL.
get readyState()
Current read state: EMPTY (0), LOADING (1), or DONE (2).
A new reader starts at EMPTY, moves to LOADING when a readAs* method is called, and settles at DONE after load, error, or abort.
get result()
Value produced by the last completed read, or null.
The type depends on the read method: an ArrayBuffer for readAsArrayBuffer, otherwise a string. The value stays null while a read is in flight and is reset to null when a new read starts, on failure, and on abort.
get error()
DOMException describing the last read failure, or null.
Non-DOMException failures are wrapped in a NotReadableError DOMException.
Setters
set onloadstart(value: FileReaderHandler)
set onprogress(value: FileReaderHandler)
set onload(value: FileReaderHandler)
set onabort(value: FileReaderHandler)
set onerror(value: FileReaderHandler)
set onloadend(value: FileReaderHandler)
Methods
dispatchEvent(event: Event): boolean
Dispatch an event to listeners and the matching on* handler.
After regular EventTarget dispatch, the corresponding event handler
property (onload for a load event, and so on) is invoked with the same
event. Exceptions thrown by that handler are swallowed so a faulty
handler cannot break the read state machine.
readAsArrayBuffer(blob: Blob): void
Start reading the Blob; result becomes a copied ArrayBuffer on load.
Throws a TypeError if the argument is not a Blob, and an InvalidStateError DOMException if a read is already in progress. Events fire asynchronously in the order loadstart, progress (non-empty Blobs only), load or error, loadend.
const reader = new FileReader();
reader.onload = () => new Uint8Array(reader.result as ArrayBuffer);
reader.readAsArrayBuffer(new Blob([new Uint8Array([1, 2, 3])]));
readAsBinaryString(blob: Blob): void
Start reading the Blob; result becomes a binary string on load.
Each byte maps to one code unit with the same numeric value. Error conditions and event sequencing match readAsArrayBuffer.
readAsDataURL(blob: Blob): void
Start reading the Blob; result becomes a base64 data URL on load.
A Blob with an empty type is labeled application/octet-stream in the
URL. Error conditions and event sequencing match readAsArrayBuffer.
const reader = new FileReader();
reader.onload = () => reader.result; // "data:text/plain;base64,aGk="
reader.readAsDataURL(new Blob(['hi'], { type: 'text/plain' }));
readAsText(blob: Blob, encoding?: string): void
Start reading the Blob; result becomes decoded text on load.
The encoding is chosen in order of precedence: the explicit encoding
label, a charset parameter in the Blob type, a UTF-16 byte order mark,
and finally UTF-8. Error conditions and event sequencing match
readAsArrayBuffer.
const reader = new FileReader();
reader.onload = () => reader.result; // "héllo"
reader.readAsText(new Blob(['héllo']), 'utf-8');
abort(): void
Cancel an in-progress read.
When a read is LOADING, the pending result is discarded, readyState
becomes DONE, and abort then loadend events fire; the read's
remaining load/error events are suppressed. When no read is in progress
the call only clears result, without dispatching events.
class FileReaderSync {
Synchronous Blob reader from the File API.
FileReaderSync exposes the worker-only synchronous read methods for
Blob-backed data. Fino does not install it on the normal global object; the
WPT worker harness installs it only for worker tests. Like FileReader,
reads are limited to in-memory Blob and File objects.
const reader = new FileReaderSync();
reader.readAsText(new Blob(['hello'])); // "hello"
Methods
readAsArrayBuffer(blob: Blob): ArrayBuffer
Read Blob bytes into a new ArrayBuffer.
The returned buffer is detached from Blob storage.
readAsBinaryString(blob: Blob): string
Read Blob bytes as a binary string.
Each byte becomes one code unit with the same numeric value.
readAsDataURL(blob: Blob): string
Read Blob bytes as a data URL.
Empty Blob types use application/octet-stream, matching FileReader.
new FileReaderSync().readAsDataURL(new Blob(['hi'], { type: 'text/plain' }));
// "data:text/plain;base64,aGk="
readAsText(blob: Blob, encoding?: string): string
Read Blob bytes as text.
The optional encoding label follows the same decoding rules as
FileReader.readAsText: explicit label, then Blob type charset, then a
UTF-16 byte order mark, then UTF-8.
new FileReaderSync().readAsText(new Blob(['hello'])); // "hello"
Constants
const FileList
FileList constructor global — always throws, matching browsers.
FileList has no public constructor; new FileList() throws a TypeError with
the standard "Illegal constructor" message. Instances are created only by
the runtime (via _createFileList) for web API integrations. The global is
still installed so instanceof checks and prototype inspection work.
try {
new FileList();
} catch (err) {
err instanceof TypeError; // true
}