first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,290 @@
# @suaveplan/mime - Structured MIME-type detection, parsing, and registry: magic-byte sniffing, RFC 7231 Content-Type parsing, IANA-sourced type database.
## Summary
Structured MIME-type detection, parsing, and registry for the Suaveplan platform. Provides RFC 7231 Content-Type parsing, magic-byte file signature detection, charset sniffing, and a 150+ entry IANA-sourced type database — all in a zero-dependency, tree-shakeable package.
<!-- begin:auto-docs -->
## Documentation tree
- [sniff-bom](./src/charset/sniff-bom.md) — Detects character encoding from a Byte Order Mark (BOM) at the start of a binary buffer. BOM detection is the highest-c…
- [sniff-html-meta](./src/charset/sniff-html-meta.md) — Extracts a declared character encoding from an HTML `<meta>` tag by scanning the first 1,024 characters of an HTML docu…
- [sniff-xml-decl](./src/charset/sniff-xml-decl.md) — Extracts the declared encoding from an XML or SVG document's processing instruction (`<?xml version="1.0" encoding="UTF…
- [content-type-equals](./src/content-type/content-type-equals.md) — Provides two utility functions for comparing Content-Type values without caring about parameters: `contentTypeEquals` c…
- [format-content-type](./src/content-type/format-content-type.md) — Serializes a `ParsedContentType` object back into a standards-compliant `Content-Type` header string. Handles quoting o…
- [parse-content-type](./src/content-type/parse-content-type.md) — Parses an RFC 7231 / RFC 9110 `Content-Type` header value into a structured `ParsedContentType` object. Handles quoted…
- [builtin-db](./src/db/builtin-db.md) — The built-in MIME type database for `@suaveplan/mime`. Contains over 150 `MimeEntry` records covering the most common w…
- [registry](./src/db/registry.md) — The central in-memory MIME type database for `@suaveplan/mime`. Maintains two indexes over the built-in `MimeEntry` lis…
- [from-magic-bytes](./src/detection/from-magic-bytes/from-magic-bytes.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - helpers — Low-level byte-inspection utilities used……
- [archives](./src/detection/from-magic-bytes/signatures/archives.md) — Registers magic-byte signatures for archive and compressed file formats with the global signature registry. Covers all…
- [audio](./src/detection/from-magic-bytes/signatures/audio.md) — Registers magic-byte signatures for audio formats with the global signature registry. Covers all major audio container…
- [data](./src/detection/from-magic-bytes/signatures/data.md) — Registers magic-byte signatures for binary data and structured-data formats with the global signature registry. Covers…
- [documents](./src/detection/from-magic-bytes/signatures/documents.md) — Registers magic-byte signatures for document and data format files with the global signature registry. Covers office do…
- [executables](./src/detection/from-magic-bytes/signatures/executables.md) — Registers magic-byte signatures for executable binary formats with the global signature registry. Covers native executa…
- [fonts](./src/detection/from-magic-bytes/signatures/fonts.md) — Registers magic-byte signatures for font file formats with the global signature registry. Covers all major web font and…
- [images](./src/detection/from-magic-bytes/signatures/images.md) — Registers magic-byte signatures for image formats with the global signature registry. Covers all major raster and vecto…
- [model3d](./src/detection/from-magic-bytes/signatures/model3d.md) — Registers magic-byte signatures for 3D model file formats with the global signature registry. Covers formats used in CA…
- [video](./src/detection/from-magic-bytes/signatures/video.md) — Registers magic-byte signatures for video container formats with the global signature registry. Covers all major video…
- [from-magic-bytes](./src/detection/from-magic-bytes/from-magic-bytes.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs --> - helpers — Low-level byte-inspection utilities used……
- [helpers](./src/detection/from-magic-bytes/helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./src/detection/from-magic-bytes/registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
- [detect](./src/detection/detect.md) — The unified MIME detection entry point for `@suaveplan/mime`. Merges evidence from three independent sources — magic by…
- [from-content-type](./src/detection/from-content-type.md) — Detects the MIME type from a raw `Content-Type` header string. Parses the header using the full RFC 7231 parser and wra…
- [from-extension](./src/detection/from-extension.md) — Detects the MIME type from a file extension or full filename. Strips path components and leading dots, normalizes to lo…
- [from-stream-reader](./src/detection/from-stream-reader.md) — Internal module providing the `toAsyncIterable` helper and the `drainViaReader` legacy fallback for `ReadableStream` so…
- [from-stream](./src/detection/from-stream.md) — Provides `detectFromStream`, which reads the first N bytes from any async byte source (`ReadableStream<Uint8Array>` or…
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable numeric error-code registry for every error class in `@suaveplan/mime`. Each code is a non-overlappi…
- [error-codes](./src/error-codes/error-codes.md) — Defines the stable numeric error-code registry for every error class in `@suaveplan/mime`. Each code is a non-overlappi…
- [insufficient-bytes-error](./src/errors/insufficient-bytes-error.md) — Thrown when a magic-byte detection operation receives fewer bytes than the minimum required by the signature. Carries b…
- [invalid-content-type-error](./src/errors/invalid-content-type-error.md) — Thrown when a `Content-Type` header or media type string fails RFC 7231 validation. Carries the raw input string and op…
- [mime-error](./src/errors/mime-error.md) — The base error class for all `@suaveplan/mime` errors. Extends `SuaveplanError` from `@suaveplan/error` with the `MIME_…
- [parse-error](./src/errors/parse-error.md) — Thrown when a general parsing operation fails — for example, XML declaration parsing or BOM detection failures. Unlike…
- [unknown-mime-type-error](./src/errors/unknown-mime-type-error.md) — Thrown when a MIME type string is not found in the built-in database and no fallback is available. Carries the unrecogn…
- [unknown-signature-error](./src/errors/unknown-signature-error.md) — Thrown when a byte signature identifier is referenced but is not present in the signature registry. Used as a sentinel…
- [legacy](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [get-mime-type](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [telemetry](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [meters](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
<!-- end:auto-docs --> - helpers — Low-level byte-inspection utilities used……
- [archives](./src/detection/from-magic-bytes/signatures/archives.md) — Registers magic-byte signatures for archive and compressed file formats with the global signature registry. Covers all…
- [audio](./src/detection/from-magic-bytes/signatures/audio.md) — Registers magic-byte signatures for audio formats with the global signature registry. Covers all major audio container…
- [data](./src/detection/from-magic-bytes/signatures/data.md) — Registers magic-byte signatures for binary data and structured-data formats with the global signature registry. Covers…
- [documents](./src/detection/from-magic-bytes/signatures/documents.md) — Registers magic-byte signatures for document and data format files with the global signature registry. Covers office do…
- [executables](./src/detection/from-magic-bytes/signatures/executables.md) — Registers magic-byte signatures for executable binary formats with the global signature registry. Covers native executa…
- [fonts](./src/detection/from-magic-bytes/signatures/fonts.md) — Registers magic-byte signatures for font file formats with the global signature registry. Covers all major web font and…
- [images](./src/detection/from-magic-bytes/signatures/images.md) — Registers magic-byte signatures for image formats with the global signature registry. Covers all major raster and vecto…
- [model3d](./src/detection/from-magic-bytes/signatures/model3d.md) — Registers magic-byte signatures for 3D model file formats with the global signature registry. Covers formats used in CA…
- [video](./src/detection/from-magic-bytes/signatures/video.md) — Registers magic-byte signatures for video container formats with the global signature registry. Covers all major video…
- [from-magic-bytes](./src/detection/from-magic-bytes/from-magic-bytes.md) — <!-- begin:auto-docs --> _(no submodules)_- helpers — Low-level byte-inspection utilities used……
- [helpers](./src/detection/from-magic-bytes/helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./src/detection/from-magic-bytes/registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
- [detect](./src/detection/detect.md) — The unified MIME detection entry point for `@suaveplan/mime`. Merges evidence from three independent sources — magic by…
- [from-content-type](./src/detection/from-content-type.md) — Detects the MIME type from a raw `Content-Type` header string. Parses the header using the full RFC 7231 parser and wra…
- [from-extension](./src/detection/from-extension.md) — Detects the MIME type from a file extension or full filename. Strips path components and leading dots, normalizes to lo…
- [from-stream-reader](./src/detection/from-stream-reader.md) — Internal module providing the `toAsyncIterable` helper and the `drainViaReader` legacy fallback for `ReadableStream` so…
- [from-stream](./src/detection/from-stream.md) — Provides `detectFromStream`, which reads the first N bytes from any async byte source (`ReadableStream<Uint8Array>` or…
- [insufficient-bytes-error](./src/errors/insufficient-bytes-error.md) — Thrown when a magic-byte detection operation receives fewer bytes than the minimum required by the signature. Carries b…
- [invalid-content-type-error](./src/errors/invalid-content-type-error.md) — Thrown when a `Content-Type` header or media type string fails RFC 7231 validation. Carries the raw input string and op…
- [mime-error](./src/errors/mime-error.md) — The base error class for all `@suaveplan/mime` errors. Extends `SuaveplanError` from `@suaveplan/error` with the `MIME_…
- [parse-error](./src/errors/parse-error.md) — Thrown when a general parsing operation fails — for example, XML declaration parsing or BOM detection failures. Unlike…
- [unknown-mime-type-error](./src/errors/unknown-mime-type-error.md) — Thrown when a MIME type string is not found in the built-in database and no fallback is available. Carries the unrecogn…
- [unknown-signature-error](./src/errors/unknown-signature-error.md) — Thrown when a byte signature identifier is referenced but is not present in the signature registry. Used as a sentinel…
- [legacy](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_- [get-mime-type](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_- [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_- [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_- [telemetry](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_- [meters](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Defines the stable numeric error-code registry for every error class in `@suaveplan/mime`. Each code is a non-overlappi…
- helpers — Low-level byte-inspection utilities used……
- [archives](./src/detection/from-magic-bytes/signatures/archives.md) — Registers magic-byte signatures for archive and compressed file formats with the global signature registry. Covers all…
- [audio](./src/detection/from-magic-bytes/signatures/audio.md) — Registers magic-byte signatures for audio formats with the global signature registry. Covers all major audio container…
- [data](./src/detection/from-magic-bytes/signatures/data.md) — Registers magic-byte signatures for binary data and structured-data formats with the global signature registry. Covers…
- [documents](./src/detection/from-magic-bytes/signatures/documents.md) — Registers magic-byte signatures for document and data format files with the global signature registry. Covers office do…
- [executables](./src/detection/from-magic-bytes/signatures/executables.md) — Registers magic-byte signatures for executable binary formats with the global signature registry. Covers native executa…
- [fonts](./src/detection/from-magic-bytes/signatures/fonts.md) — Registers magic-byte signatures for font file formats with the global signature registry. Covers all major web font and…
- [images](./src/detection/from-magic-bytes/signatures/images.md) — Registers magic-byte signatures for image formats with the global signature registry. Covers all major raster and vecto…
- [model3d](./src/detection/from-magic-bytes/signatures/model3d.md) — Registers magic-byte signatures for 3D model file formats with the global signature registry. Covers formats used in CA…
- [video](./src/detection/from-magic-bytes/signatures/video.md) — Registers magic-byte signatures for video container formats with the global signature registry. Covers all major video…
- [from-magic-bytes](./src/detection/from-magic-bytes/from-magic-bytes.md) — <!-- begin:auto-docs --> _(no submodules)_- helpers — Low-level byte-inspection utilities used……
- [helpers](./src/detection/from-magic-bytes/helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./src/detection/from-magic-bytes/registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
- [detect](./src/detection/detect.md) — The unified MIME detection entry point for `@suaveplan/mime`. Merges evidence from three independent sources — magic by…
- [from-content-type](./src/detection/from-content-type.md) — Detects the MIME type from a raw `Content-Type` header string. Parses the header using the full RFC 7231 parser and wra…
- [from-extension](./src/detection/from-extension.md) — Detects the MIME type from a file extension or full filename. Strips path components and leading dots, normalizes to lo…
- [from-stream-reader](./src/detection/from-stream-reader.md) — Internal module providing the `toAsyncIterable` helper and the `drainViaReader` legacy fallback for `ReadableStream` so…
- [from-stream](./src/detection/from-stream.md) — Provides `detectFromStream`, which reads the first N bytes from any async byte source (`ReadableStream<Uint8Array>` or…
- [insufficient-bytes-error](./src/errors/insufficient-bytes-error.md) — Thrown when a magic-byte detection operation receives fewer bytes than the minimum required by the signature. Carries b…
- [invalid-content-type-error](./src/errors/invalid-content-type-error.md) — Thrown when a `Content-Type` header or media type string fails RFC 7231 validation. Carries the raw input string and op…
- [mime-error](./src/errors/mime-error.md) — The base error class for all `@suaveplan/mime` errors. Extends `SuaveplanError` from `@suaveplan/error` with the `MIME_…
- [parse-error](./src/errors/parse-error.md) — Thrown when a general parsing operation fails — for example, XML declaration parsing or BOM detection failures. Unlike…
- [unknown-mime-type-error](./src/errors/unknown-mime-type-error.md) — Thrown when a MIME type string is not found in the built-in database and no fallback is available. Carries the unrecogn…
- [unknown-signature-error](./src/errors/unknown-signature-error.md) — Thrown when a byte signature identifier is referenced but is not present in the signature registry. Used as a sentinel…
- [legacy](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_- [get-mime-type](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_- [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_- [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_- [telemetry](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_- [meters](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Defines the stable numeric error-code registry for every error class in `@suaveplan/mime`. Each code is a non-overlappi…
- helpers — Low-level byte-inspection utilities used…
- [archives](./src/detection/from-magic-bytes/signatures/archives.md) — Registers magic-byte signatures for archive and compressed file formats with the global signature registry. Covers all…
- [audio](./src/detection/from-magic-bytes/signatures/audio.md) — Registers magic-byte signatures for audio formats with the global signature registry. Covers all major audio container…
- [data](./src/detection/from-magic-bytes/signatures/data.md) — Registers magic-byte signatures for binary data and structured-data formats with the global signature registry. Covers…
- [documents](./src/detection/from-magic-bytes/signatures/documents.md) — Registers magic-byte signatures for document and data format files with the global signature registry. Covers office do…
- [executables](./src/detection/from-magic-bytes/signatures/executables.md) — Registers magic-byte signatures for executable binary formats with the global signature registry. Covers native executa…
- [fonts](./src/detection/from-magic-bytes/signatures/fonts.md) — Registers magic-byte signatures for font file formats with the global signature registry. Covers all major web font and…
- [images](./src/detection/from-magic-bytes/signatures/images.md) — Registers magic-byte signatures for image formats with the global signature registry. Covers all major raster and vecto…
- [model3d](./src/detection/from-magic-bytes/signatures/model3d.md) — Registers magic-byte signatures for 3D model file formats with the global signature registry. Covers formats used in CA…
- [video](./src/detection/from-magic-bytes/signatures/video.md) — Registers magic-byte signatures for video container formats with the global signature registry. Covers all major video…
- [from-magic-bytes](./src/detection/from-magic-bytes/from-magic-bytes.md) — <!-- begin:auto-docs --> _(no submodules)_- helpers — Low-level byte-inspection utilities used…
- [helpers](./src/detection/from-magic-bytes/helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./src/detection/from-magic-bytes/registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
- [detect](./src/detection/detect.md) — The unified MIME detection entry point for `@suaveplan/mime`. Merges evidence from three independent sources — magic by…
- [from-content-type](./src/detection/from-content-type.md) — Detects the MIME type from a raw `Content-Type` header string. Parses the header using the full RFC 7231 parser and wra…
- [from-extension](./src/detection/from-extension.md) — Detects the MIME type from a file extension or full filename. Strips path components and leading dots, normalizes to lo…
- [from-stream-reader](./src/detection/from-stream-reader.md) — Internal module providing the `toAsyncIterable` helper and the `drainViaReader` legacy fallback for `ReadableStream` so…
- [from-stream](./src/detection/from-stream.md) — Provides `detectFromStream`, which reads the first N bytes from any async byte source (`ReadableStream<Uint8Array>` or…
- [insufficient-bytes-error](./src/errors/insufficient-bytes-error.md) — Thrown when a magic-byte detection operation receives fewer bytes than the minimum required by the signature. Carries b…
- [invalid-content-type-error](./src/errors/invalid-content-type-error.md) — Thrown when a `Content-Type` header or media type string fails RFC 7231 validation. Carries the raw input string and op…
- [mime-error](./src/errors/mime-error.md) — The base error class for all `@suaveplan/mime` errors. Extends `SuaveplanError` from `@suaveplan/error` with the `MIME_…
- [parse-error](./src/errors/parse-error.md) — Thrown when a general parsing operation fails — for example, XML declaration parsing or BOM detection failures. Unlike…
- [unknown-mime-type-error](./src/errors/unknown-mime-type-error.md) — Thrown when a MIME type string is not found in the built-in database and no fallback is available. Carries the unrecogn…
- [unknown-signature-error](./src/errors/unknown-signature-error.md) — Thrown when a byte signature identifier is referenced but is not present in the signature registry. Used as a sentinel…
- [legacy](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_- [get-mime-type](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_- [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_- [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_- [telemetry](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_- [meters](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Defines the stable numeric error-code registry for every error class in `@suaveplan/mime`. Each code is a non-overlappi…
- [archives](./src/detection/from-magic-bytes/signatures/archives.md) — Registers magic-byte signatures for archive and compressed file formats with the global signature registry. Covers all…
- [audio](./src/detection/from-magic-bytes/signatures/audio.md) — Registers magic-byte signatures for audio formats with the global signature registry. Covers all major audio container…
- [data](./src/detection/from-magic-bytes/signatures/data.md) — Registers magic-byte signatures for binary data and structured-data formats with the global signature registry. Covers…
- [documents](./src/detection/from-magic-bytes/signatures/documents.md) — Registers magic-byte signatures for document and data format files with the global signature registry. Covers office do…
- [executables](./src/detection/from-magic-bytes/signatures/executables.md) — Registers magic-byte signatures for executable binary formats with the global signature registry. Covers native executa…
- [fonts](./src/detection/from-magic-bytes/signatures/fonts.md) — Registers magic-byte signatures for font file formats with the global signature registry. Covers all major web font and…
- [images](./src/detection/from-magic-bytes/signatures/images.md) — Registers magic-byte signatures for image formats with the global signature registry. Covers all major raster and vecto…
- [model3d](./src/detection/from-magic-bytes/signatures/model3d.md) — Registers magic-byte signatures for 3D model file formats with the global signature registry. Covers formats used in CA…
- [video](./src/detection/from-magic-bytes/signatures/video.md) — Registers magic-byte signatures for video container formats with the global signature registry. Covers all major video…
- [from-magic-bytes](./src/detection/from-magic-bytes/from-magic-bytes.md) — <!-- begin:auto-docs --> _(no submodules)_- [helpers](./src/detection/from-magic-bytes/helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./src/detection/from-magic-bytes/registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
- [detect](./src/detection/detect.md) — The unified MIME detection entry point for `@suaveplan/mime`. Merges evidence from three independent sources — magic by…
- [from-content-type](./src/detection/from-content-type.md) — Detects the MIME type from a raw `Content-Type` header string. Parses the header using the full RFC 7231 parser and wra…
- [from-extension](./src/detection/from-extension.md) — Detects the MIME type from a file extension or full filename. Strips path components and leading dots, normalizes to lo…
- [from-stream-reader](./src/detection/from-stream-reader.md) — Internal module providing the `toAsyncIterable` helper and the `drainViaReader` legacy fallback for `ReadableStream` so…
- [from-stream](./src/detection/from-stream.md) — Provides `detectFromStream`, which reads the first N bytes from any async byte source (`ReadableStream<Uint8Array>` or…
- [insufficient-bytes-error](./src/errors/insufficient-bytes-error.md) — Thrown when a magic-byte detection operation receives fewer bytes than the minimum required by the signature. Carries b…
- [invalid-content-type-error](./src/errors/invalid-content-type-error.md) — Thrown when a `Content-Type` header or media type string fails RFC 7231 validation. Carries the raw input string and op…
- [mime-error](./src/errors/mime-error.md) — The base error class for all `@suaveplan/mime` errors. Extends `SuaveplanError` from `@suaveplan/error` with the `MIME_…
- [parse-error](./src/errors/parse-error.md) — Thrown when a general parsing operation fails — for example, XML declaration parsing or BOM detection failures. Unlike…
- [unknown-mime-type-error](./src/errors/unknown-mime-type-error.md) — Thrown when a MIME type string is not found in the built-in database and no fallback is available. Carries the unrecogn…
- [unknown-signature-error](./src/errors/unknown-signature-error.md) — Thrown when a byte signature identifier is referenced but is not present in the signature registry. Used as a sentinel…
- [legacy](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_ - [get-mime-type](./src/legacy/get-mime-type.md) — <!-- begin:auto-docs --> _(no submodules)_- [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_ - [reverse](./src/reverse/reverse.md) — <!-- begin:auto-docs --> _(no submodules)_- [telemetry](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_ - [meters](./src/telemetry/meters.md) — <!-- begin:auto-docs --> _(no submodules)_- [error-codes](./src/error-codes.md) — Defines the stable numeric error-code registry for every error class in `@suaveplan/mime`. Each code is a non-overlappi…
## Features
- **RFC 7231 Content-Type parsing** — full quoted-string and OWS support
- **Magic-byte detection** — 150+ signatures across images, audio, video, archives, documents, executables, and fonts
- **Stream detection** — peek bytes from a `ReadableStream` or `AsyncIterable` without losing the original stream
- **Charset sniffing** — BOM, HTML `<meta>` tag, and XML declaration detection
- **Multi-source confidence scoring** — merge bytes + extension + Content-Type with agreement boost
- **Reverse lookup** — MIME type → canonical file extension
- **OpenTelemetry metrics** — optional OTel instrumentation
- **100% test coverage** — every branch and statement verified
## Installation
```bash
bun add @suaveplan/mime
```
## Quick Start
```typescript
import { detect } from "@suaveplan/mime";
// Multi-source detection with confidence scoring
const result = detect({
bytes: fileBuffer,
filename: "photo.jpg",
contentType: "image/jpeg",
});
console.log(result.mime); // "image/jpeg"
console.log(result.confidence); // 1.0 (all three sources agree)
```
## Module Reference
| Module | Description | Docs |
|--------|-------------|------|
| `@suaveplan/mime` | Main entry: `detect()`, `parseContentType()`, `formatContentType()` | [src/index.ts](src/index.ts) |
| `@suaveplan/mime/content-type` | RFC 7231 parser, formatter, equality/match utilities | [parse-content-type.md](src/content-type/parse-content-type.md) |
| `@suaveplan/mime/detection/detect` | Unified multi-source detector | [detect.md](src/detection/detect.md) |
| `@suaveplan/mime/detection/from-stream` | Stream-based detection with head/tail replay | [from-stream.md](src/detection/from-stream.md) |
| `@suaveplan/mime/detection/from-magic-bytes` | Raw magic-byte detection API | [from-magic-bytes.md](src/detection/from-magic-bytes/from-magic-bytes.md) |
| `@suaveplan/mime/charset/sniff-bom` | BOM detection from byte buffers | [sniff-bom.md](src/charset/sniff-bom.md) |
| `@suaveplan/mime/charset/sniff-html-meta` | HTML `<meta charset>` detection | [sniff-html-meta.md](src/charset/sniff-html-meta.md) |
| `@suaveplan/mime/charset/sniff-xml-decl` | XML encoding declaration detection | [sniff-xml-decl.md](src/charset/sniff-xml-decl.md) |
| `@suaveplan/mime/db/registry` | MIME database: forward and reverse lookup | [registry.md](src/db/registry.md) |
| `@suaveplan/mime/reverse` | MIME type → file extension reverse lookup | [reverse.md](src/reverse/reverse.md) |
| `@suaveplan/mime/errors` | Typed error classes | [mime-error.md](src/errors/mime-error.md) |
| `@suaveplan/mime/telemetry/meters` | Optional OTel metrics instrumentation | [meters.md](src/telemetry/meters.md) |
| `@suaveplan/mime/legacy` | `getMimeType()` compat shim | [get-mime-type.md](src/legacy/get-mime-type.md) |
## API Examples
### Content-Type Parsing
```typescript
import { parseContentType, formatContentType } from "@suaveplan/mime/content-type";
const ct = parseContentType("text/html; charset=UTF-8; format=flowed");
console.log(ct.type); // "text"
console.log(ct.subtype); // "html"
console.log(ct.essence); // "text/html"
console.log(ct.parameters.charset); // "utf-8" (lowercased)
console.log(formatContentType(ct)); // "text/html; charset=utf-8; format=flowed"
```
### Magic-Byte Detection
```typescript
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
const pngBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
const result = detectFromMagicBytes(pngBytes);
console.log(result?.mime); // "image/png"
console.log(result?.priority); // 100
```
### Stream Detection
```typescript
import { detectFromStream } from "@suaveplan/mime/detection/from-stream";
const { mime, head, tail } = await detectFromStream(uploadStream);
console.log(mime); // e.g. "video/mp4"
// Replay the full stream (head + tail)
const chunks: Uint8Array[] = [head];
for await (const chunk of tail) chunks.push(chunk);
```
### Charset Sniffing
```typescript
import { sniffBom } from "@suaveplan/mime/charset/sniff-bom";
import { sniffHtmlMeta } from "@suaveplan/mime/charset/sniff-html-meta";
const bom = sniffBom(fileBytes); // "utf-8" | "utf-16le" | null
const meta = sniffHtmlMeta(htmlText); // "iso-8859-1" | null
```
### Error Handling
```typescript
import { InvalidContentTypeError, UnknownMimeTypeError } from "@suaveplan/mime/errors";
import { parseContentType } from "@suaveplan/mime/content-type";
try {
parseContentType("not valid");
} catch (err) {
if (err instanceof InvalidContentTypeError) {
console.error("Bad header:", err.context?.raw);
}
}
```
## Registering Custom Signatures
```typescript
import { registerSignature } from "@suaveplan/mime/detection/from-magic-bytes/registry";
registerSignature({
mime: "application/x-my-format",
priority: 75,
minBytes: 4,
match: (bytes) =>
bytes[0] === 0x4d && bytes[1] === 0x59 &&
bytes[2] === 0x46 && bytes[3] === 0x4d,
});
```
## Coverage
This package maintains **100% statement, branch, function, and line coverage** — a requirement for Layer 0/1 foundation packages in Suaveplan.
## License
MIT

View File

@@ -0,0 +1,59 @@
# sniff-bom
## Purpose
Detects character encoding from a Byte Order Mark (BOM) at the start of a binary buffer. BOM detection is the highest-confidence method of charset identification — when present, a BOM unambiguously identifies the encoding without relying on declared metadata or heuristics.
## Features
- Detects UTF-8 BOM (`EF BB BF`)
- Detects UTF-16 BE BOM (`FE FF`)
- Detects UTF-16 LE BOM (`FF FE`)
- Detects UTF-32 BE BOM (`00 00 FE FF`)
- Detects UTF-32 LE BOM (`FF FE 00 00`)
- Accepts any `ArrayBuffer`, `SharedArrayBuffer`, or `ArrayBufferView` (`Uint8Array`, `DataView`, etc.)
- Returns the IANA charset name in lowercase, or `null` when no BOM is present
- Zero allocation path for no-BOM case
## Usage: Basic Example
```typescript
import { sniffBom } from "@suaveplan/mime/charset/sniff-bom";
const bytes = new Uint8Array([0xef, 0xbb, 0xbf, 0x3c, 0x68, 0x74, 0x6d, 0x6c]);
const charset = sniffBom(bytes);
console.log(charset); // "utf-8"
```
## Usage: Advanced Example
```typescript
import { sniffBom } from "@suaveplan/mime/charset/sniff-bom";
import { sniffHtmlMeta } from "@suaveplan/mime/charset/sniff-html-meta";
async function detectEncoding(buffer: Uint8Array): Promise<string> {
// BOM takes precedence over meta-tag declarations
const bomCharset = sniffBom(buffer);
if (bomCharset) return bomCharset;
// Fall back to meta-tag detection
const text = new TextDecoder("ascii", { fatal: false }).decode(buffer.slice(0, 1024));
return sniffHtmlMeta(text) ?? "utf-8";
}
```
## API Reference
### `sniffBom(buffer: ArrayBufferView | ArrayBuffer): string | null`
| Parameter | Type | Description |
|-----------|------|-------------|
| `buffer` | `ArrayBufferView \| ArrayBuffer` | The byte buffer to inspect (any view or raw buffer). |
**Returns:** The IANA charset name in lowercase, or `null` when no BOM is detected.
**Throws:** Never — all buffer accesses are bounds-checked.
## Implementation Notes
UTF-32 BOMs (both BE and LE) must be tested before UTF-16 LE because the UTF-32 LE BOM (`FF FE 00 00`) starts with the same two bytes as the UTF-16 LE BOM (`FF FE`). The implementation checks the 4-byte sequences first to avoid misidentification. The function uses direct `Uint8Array` byte comparisons for maximum performance — no string allocations or regex matching.

View File

@@ -0,0 +1,65 @@
# sniff-html-meta
## Purpose
Extracts a declared character encoding from an HTML `<meta>` tag by scanning the first 1,024 characters of an HTML document. Handles both the modern HTML5 `<meta charset="...">` form and the legacy HTML4 `<meta http-equiv="Content-Type" content="...; charset=...">` form using a single regex that matches `charset=` in any attribute context.
## Features
- Scans up to 1,024 bytes (configurable via the slice parameter)
- Matches HTML5 `<meta charset="...">` tags
- Matches HTML4 `<meta http-equiv="Content-Type" content="...; charset=...">` tags
- Case-insensitive matching for both tag names and attribute values
- Returns the charset name in lowercase for canonical comparison
- Returns `null` when no charset declaration is found
- Zero allocation on the no-match path
## Usage: Basic Example
```typescript
import { sniffHtmlMeta } from "@suaveplan/mime/charset/sniff-html-meta";
const html = '<html><head><meta charset="UTF-8"><title>Hello</title>';
const charset = sniffHtmlMeta(html);
console.log(charset); // "utf-8"
```
## Usage: Advanced Example
```typescript
import { sniffHtmlMeta } from "@suaveplan/mime/charset/sniff-html-meta";
import { sniffBom } from "@suaveplan/mime/charset/sniff-bom";
function detectHtmlEncoding(bytes: Uint8Array): string {
// BOM has highest priority
const bom = sniffBom(bytes);
if (bom) return bom;
// Decode as ASCII/Latin-1 to scan for meta tags
const head = new TextDecoder("latin1").decode(bytes.slice(0, 1024));
const meta = sniffHtmlMeta(head);
// HTML5 spec default is utf-8 when no declaration is found
return meta ?? "utf-8";
}
// Legacy HTML4 http-equiv form
const legacyHtml = '<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1">';
console.log(sniffHtmlMeta(legacyHtml)); // "iso-8859-1"
```
## API Reference
### `sniffHtmlMeta(source: string): string | null`
| Parameter | Type | Description |
|-----------|------|-------------|
| `source` | `string` | HTML content (or the first chunk thereof). |
**Returns:** The lowercase IANA charset name, or `null` when no charset declaration is found in the first 1,024 characters.
**Throws:** Never.
## Implementation Notes
A single regex (`/<meta[^>]+charset\s*=\s*["']?([A-Za-z0-9_\-:]+)/i`) covers both the HTML5 and HTML4 forms because `charset=` appears literally in the `content` attribute of the `http-equiv` tag. A separate legacy regex for the full `http-equiv` pattern is unnecessary — the simpler pattern matches earlier and always wins. The 1,024-character scan window follows the HTML5 parsing spec for prescan byte-sequence limits.

View File

@@ -0,0 +1,64 @@
# sniff-xml-decl
## Purpose
Extracts the declared encoding from an XML or SVG document's processing instruction (`<?xml version="1.0" encoding="UTF-8"?>`). XML documents commonly declare their encoding in the prolog, and this declaration must be trusted before any BOM or HTTP header when present, per the XML specification (RFC 7303).
## Features
- Matches the `encoding` attribute in `<?xml ... encoding="..."?>` declarations
- Supports both double-quoted and single-quoted attribute values
- Case-insensitive matching — `ENCODING` and `encoding` both match
- Scans only the first 256 characters (the XML prolog is always at the start)
- Returns the encoding name in lowercase for canonical comparison
- Returns `null` when no XML declaration or encoding attribute is found
- Handles whitespace variations between attributes
## Usage: Basic Example
```typescript
import { sniffXmlDecl } from "@suaveplan/mime/charset/sniff-xml-decl";
const xml = '<?xml version="1.0" encoding="UTF-8"?><root/>';
const encoding = sniffXmlDecl(xml);
console.log(encoding); // "utf-8"
```
## Usage: Advanced Example
```typescript
import { sniffXmlDecl } from "@suaveplan/mime/charset/sniff-xml-decl";
import { sniffBom } from "@suaveplan/mime/charset/sniff-bom";
function detectXmlEncoding(bytes: Uint8Array): string {
// BOM overrides XML declaration per RFC 7303 §4.3
const bom = sniffBom(bytes);
if (bom) return bom;
// Use latin-1 to safely read the ASCII-range prolog
const prolog = new TextDecoder("latin1").decode(bytes.slice(0, 256));
const declared = sniffXmlDecl(prolog);
return declared ?? "utf-8"; // XML default encoding
}
// Single-quoted attribute
const svgWithSingleQuotes = "<?xml version='1.0' encoding='iso-8859-1'?><svg/>";
console.log(sniffXmlDecl(svgWithSingleQuotes)); // "iso-8859-1"
```
## API Reference
### `sniffXmlDecl(source: string): string | null`
| Parameter | Type | Description |
|-----------|------|-------------|
| `source` | `string` | XML/SVG content (or the first chunk thereof). |
**Returns:** The lowercase IANA encoding name, or `null` when no encoding attribute is found.
**Throws:** Never.
## Implementation Notes
The regex `/<\?xml[^?]*encoding\s*=\s*["']([^"']+)["']/i` intentionally uses `[^?]*` (non-greedy via exclusion) rather than `.*?` to avoid backtracking across large documents when no `<?xml` declaration is present. The 256-character scan window is sufficient because the XML specification mandates that the encoding declaration, if present, must appear in the document prolog before any other content.

View File

@@ -0,0 +1,79 @@
# content-type-equals
## Purpose
Provides two utility functions for comparing Content-Type values without caring about parameters: `contentTypeEquals` checks exact `type/subtype` equality (case-insensitive), and `contentTypeMatches` supports wildcard patterns (`*/*`, `image/*`). Both functions accept either raw header strings or pre-parsed `ParsedContentType` objects for zero-overhead comparisons.
## Features
- `contentTypeEquals`: exact essence comparison, ignoring parameters
- `contentTypeMatches`: glob-style pattern matching (`*/*`, `image/*`, `text/plain`)
- Accepts raw strings or `ParsedContentType` objects for both arguments
- Case-insensitive for raw string inputs
- Malformed patterns return `false` instead of throwing
- Zero-cost fast path when a `ParsedContentType` is passed directly (no re-parse)
## Usage: Basic Example
```typescript
import {
contentTypeEquals,
contentTypeMatches,
} from "@suaveplan/mime/content-type/content-type-equals";
// Exact equality (parameters ignored)
contentTypeEquals("text/html; charset=utf-8", "text/html"); // true
contentTypeEquals("Text/HTML", "text/html"); // true (case-insensitive)
contentTypeEquals("text/html", "text/plain"); // false
// Wildcard matching
contentTypeMatches("image/png", "image/*"); // true
contentTypeMatches("image/png", "*/*"); // true
contentTypeMatches("text/html", "image/*"); // false
```
## Usage: Advanced Example
```typescript
import {
contentTypeEquals,
contentTypeMatches,
} from "@suaveplan/mime/content-type/content-type-equals";
import { parseContentType } from "@suaveplan/mime/content-type/parse-content-type";
// Pre-parse once and compare many times
const incoming = parseContentType(request.headers.get("content-type") ?? "");
// Exact match against known types
const isJson = contentTypeEquals(incoming, "application/json");
// Accept any image type
const isImage = contentTypeMatches(incoming, "image/*");
// Build a router
const handlers: Array<{ pattern: string; fn: (req: Request) => Response }> = [
{ pattern: "application/json", fn: handleJson },
{ pattern: "multipart/form-data", fn: handleMultipart },
{ pattern: "text/*", fn: handleText },
];
for (const { pattern, fn } of handlers) {
if (contentTypeMatches(incoming, pattern)) {
return fn(request);
}
}
```
## API Reference
### `contentTypeEquals(a: string | ParsedContentType, b: string | ParsedContentType): boolean`
Returns `true` when both values have the same `type/subtype` essence, ignoring all parameters and case differences.
### `contentTypeMatches(value: string | ParsedContentType, pattern: string): boolean`
Returns `true` when the value's essence matches the pattern. The pattern may use `*` as a wildcard for either type or subtype. Returns `false` if the pattern is malformed (no slash).
## Implementation Notes
Both functions normalize inputs through `parseContentType` when a raw string is provided. The pattern parser in `contentTypeMatches` uses a simple string split on `/` rather than re-using the full parser, which avoids the overhead of parameter parsing and error handling for patterns. Wildcard matching is implemented with direct string comparison after split — there is no full glob engine, only the two valid HTTP wildcard forms (`*/*` and `type/*`).

View File

@@ -0,0 +1,71 @@
# format-content-type
## Purpose
Serializes a `ParsedContentType` object back into a standards-compliant `Content-Type` header string. Handles quoting of parameter values that contain non-token characters, ensuring the output is always valid for use in HTTP headers and is round-trip safe with `parseContentType`.
## Features
- Produces the canonical `type/subtype` essence string
- Appends parameters as `; name=value` pairs in insertion order
- Automatically quotes values containing non-token characters (spaces, special chars)
- Does not quote simple token values (no unnecessary quotes)
- Round-trip safe: `parseContentType(formatContentType(x)).essence === x.essence`
- Accepts both `ParsedContentType` objects and ad-hoc objects with the same shape
## Usage: Basic Example
```typescript
import { parseContentType } from "@suaveplan/mime/content-type/parse-content-type";
import { formatContentType } from "@suaveplan/mime/content-type/format-content-type";
const ct = parseContentType("text/html; charset=utf-8");
const formatted = formatContentType(ct);
console.log(formatted); // "text/html; charset=utf-8"
```
## Usage: Advanced Example
```typescript
import { formatContentType } from "@suaveplan/mime/content-type/format-content-type";
// Construct a content type programmatically
const ct = {
type: "multipart",
subtype: "form-data",
essence: "multipart/form-data",
parameters: Object.freeze({ boundary: "--my boundary with spaces" }),
};
console.log(formatContentType(ct));
// 'multipart/form-data; boundary="--my boundary with spaces"'
// Values needing quoting are detected automatically
const withSpecial = {
type: "text",
subtype: "plain",
essence: "text/plain",
parameters: Object.freeze({ name: 'file with "quotes".txt' }),
};
console.log(formatContentType(withSpecial));
// 'text/plain; name="file with \\"quotes\\".txt"'
```
## API Reference
### `formatContentType(ct: ParsedContentType): string`
| Parameter | Type | Description |
|-----------|------|-------------|
| `ct` | `ParsedContentType` | A parsed content-type object. |
**Returns:** An RFC 7231-compliant `Content-Type` header string.
**Throws:** Never — all inputs that satisfy `ParsedContentType` are formattable.
## Implementation Notes
A value is quoted only when it contains characters outside the RFC 7230 token character set (`!#$%&'*+-.^_\`|~0-9A-Za-z`). Characters within the quoted string that are not`token` characters are escaped with a backslash. This ensures the output is always valid even when parameter values contain double-quote characters themselves. The function uses `Object.keys` to enumerate parameters, which preserves insertion order in all modern JavaScript engines.
### Quoting Rules
Parameter values that contain characters outside the RFC 7230 `token` grammar (whitespace, `;`, `,`, non-ASCII) are wrapped in double quotes with backslash-escaping; values that already look like a valid token are emitted unquoted. Charset parameter values are lowercased at format time so output is canonical and hashes stably for content-based caches.

View File

@@ -0,0 +1,82 @@
# parse-content-type
## Purpose
Parses an RFC 7231 / RFC 9110 `Content-Type` header value into a structured `ParsedContentType` object. Handles quoted strings, escape sequences, optional whitespace (OWS), and case normalization. The parsed object is immutable (frozen parameters map) and provides a canonical `essence` field (`type/subtype`) for efficient comparison.
## Features
- Parses `type/subtype` with OWS trimming and lowercase normalization
- Parses parameters: `name=value` pairs, `;`-separated
- Supports quoted-string parameter values with `\"` escape sequences
- Normalizes `charset`, `boundary`, and `format` parameter values to lowercase
- Handles OWS around `=` and between parameters
- Validates type, subtype, and parameter names as RFC token characters
- Guards against non-string inputs (JS interop safety)
- Returns a frozen `parameters` object for safe sharing
- Throws `InvalidContentTypeError` for all malformed inputs
## Usage: Basic Example
```typescript
import { parseContentType } from "@suaveplan/mime/content-type/parse-content-type";
const ct = parseContentType("text/html; charset=utf-8");
console.log(ct.type); // "text"
console.log(ct.subtype); // "html"
console.log(ct.essence); // "text/html"
console.log(ct.parameters); // { charset: "utf-8" }
```
## Usage: Advanced Example
```typescript
import { parseContentType } from "@suaveplan/mime/content-type/parse-content-type";
import { InvalidContentTypeError } from "@suaveplan/mime/errors";
// Quoted boundary with spaces
const multipart = parseContentType('multipart/form-data; boundary="--boundary 123"');
console.log(multipart.parameters.boundary); // "--boundary 123"
// Multiple parameters with OWS
const complex = parseContentType('text/html; charset= utf-8; format=flowed');
console.log(complex.parameters.charset); // "utf-8"
console.log(complex.parameters.format); // "flowed"
// Error handling
try {
parseContentType("not/valid token!");
} catch (err) {
if (err instanceof InvalidContentTypeError) {
console.error("Malformed Content-Type:", err.message);
}
}
// Round-trip safe (parse → format → parse)
const reparsed = parseContentType(formatContentType(multipart));
console.log(reparsed.essence); // "multipart/form-data"
```
## API Reference
### `parseContentType(input: string): ParsedContentType`
| Parameter | Type | Description |
|-----------|------|-------------|
| `input` | `string` | Raw `Content-Type` header value. |
**Returns:** A `ParsedContentType` with `type`, `subtype`, `essence`, and frozen `parameters`.
**Throws:** `InvalidContentTypeError` when:
- Input is not a string
- Input is empty or whitespace-only
- Missing slash between type and subtype
- Type or subtype is empty
- Type or subtype contains non-token characters
- Parameter name contains non-token characters
- Parameter is missing the `=` separator
## Implementation Notes
The parser is a hand-written state machine operating on the raw string. This avoids regex backtracking issues with complex quoted-string values and ensures O(n) time complexity. The `parameters` object is frozen via `Object.freeze` before return to prevent accidental mutation by callers. The `charset`, `boundary`, and `format` parameter values are lowercased per the RFC — all other parameter values preserve their original casing. The leading `trim()` on the full input removes any surrounding whitespace, which means trailing spaces after a final semicolon are eliminated before parameter parsing begins.

View File

@@ -0,0 +1,56 @@
# db/builtin-db
## Purpose
The built-in MIME type database for `@suaveplan/mime`. Contains over 150 `MimeEntry` records covering the most common web, media, document, archive, font, and application MIME types. This is a static data file — it contains no runtime logic, only the `BUILTIN_DB` constant array consumed by the registry.
## Features
- 150+ MIME type entries across all major categories
- Each entry includes: `mime` (canonical type string), `extensions` (array of associated file extensions), and `encoding` (`"binary"`, `"text"`, or `"base64"`)
- Comprehensive coverage: images, audio, video, documents, archives, fonts, executables, data formats
- Extensions are lowercase and without leading dots
- Canonical extension (for `Content-Disposition` filename guessing) is always the first element
## Usage: Basic Example
```typescript
import { BUILTIN_DB } from "@suaveplan/mime/db/builtin-db";
// Inspect all text-type entries
const textTypes = BUILTIN_DB.filter(e => e.mime.startsWith("text/"));
console.log(textTypes.map(e => e.mime));
// ["text/plain", "text/html", "text/css", ...]
```
## Usage: Advanced Example
```typescript
import { BUILTIN_DB } from "@suaveplan/mime/db/builtin-db";
import { getEntryByMime } from "@suaveplan/mime/db/registry";
// Combine built-in with runtime lookup
const entry = getEntryByMime("application/pdf");
console.log(entry?.extensions); // ["pdf"]
console.log(entry?.encoding); // "binary"
// Find all binary MIME types
const binaryTypes = BUILTIN_DB.filter(e => e.encoding === "binary");
console.log(binaryTypes.length); // 100+
```
## API Reference
### `BUILTIN_DB: readonly MimeEntry[]`
A frozen array of `MimeEntry` objects. Each entry has:
| Field | Type | Description |
|-------|------|-------------|
| `mime` | `string` | Canonical MIME type string (e.g., `"image/png"`) |
| `extensions` | `string[]` | Associated file extensions without dots (e.g., `["png"]`) |
| `encoding` | `"binary" \| "text" \| "base64"` | Transfer encoding hint |
## Implementation Notes
The database is intentionally a plain array rather than a pre-built map so that the registry can apply its first-write-wins semantics during initialization. Keeping data and behavior separate also makes it straightforward to swap the database at build time (e.g., for a minimal embedded build that includes only a subset of MIME types). The entries are ordered roughly by frequency of use, which has no algorithmic impact but improves grep readability.

View File

@@ -0,0 +1,70 @@
# db/registry
## Purpose
The central in-memory MIME type database for `@suaveplan/mime`. Maintains two indexes over the built-in `MimeEntry` list: a `type → extensions` forward map and an `extension → entry` reverse map. Provides O(1) lookup in both directions and supports registering additional entries at runtime for extensibility.
## Features
- `getEntryByMime(mime)` — forward lookup: MIME type string → `MimeEntry`
- `getEntryByExtension(ext)` — reverse lookup: file extension → `MimeEntry`
- `registerEntry(entry)` — register a custom MIME type at runtime
- First-write-wins semantics: built-in entries take precedence over later registrations for the same extension
- Extension keys are normalized to lowercase with no leading dot
- Both indexes are populated lazily on first access (module load)
- `_indexEntryForTesting` — exported only for internal test instrumentation
## Usage: Basic Example
```typescript
import { getEntryByMime, getEntryByExtension } from "@suaveplan/mime/db/registry";
// Forward lookup
const pngEntry = getEntryByMime("image/png");
console.log(pngEntry?.extensions); // ["png"]
// Reverse lookup
const entry = getEntryByExtension("jpg");
console.log(entry?.mime); // "image/jpeg"
```
## Usage: Advanced Example
```typescript
import { getEntryByMime, registerEntry } from "@suaveplan/mime/db/registry";
// Register a custom MIME type
registerEntry({
mime: "application/x-my-format",
extensions: ["myf", "myfmt"],
encoding: "binary",
});
// Immediately available via lookup
const custom = getEntryByMime("application/x-my-format");
console.log(custom?.extensions); // ["myf", "myfmt"]
// Built-in extensions are protected (first-write-wins)
// Registering a second entry for ".png" will not override image/png
registerEntry({ mime: "image/x-fake-png", extensions: ["png"] });
const png = getEntryByExtension("png");
console.log(png?.mime); // still "image/png"
```
## API Reference
### `getEntryByMime(mime: string): MimeEntry | undefined`
Looks up a `MimeEntry` by its MIME type string. Returns `undefined` if not found.
### `getEntryByExtension(ext: string): MimeEntry | undefined`
Looks up a `MimeEntry` by file extension (with or without leading dot, case-insensitive). Returns `undefined` if not found.
### `registerEntry(entry: MimeEntry): void`
Registers a new MIME type entry. First-write-wins for extensions: if an extension is already in the index, the new entry does not replace it.
## Implementation Notes
The registry is initialized at module load by iterating `BUILTIN_DB` once and calling the private `index()` function for each entry. The `index()` function implements the first-write-wins extension guard: `if (!byExtension.has(key)) byExtension.set(key, entry)`. This guard prevents downstream `registerEntry` calls from overriding the canonical extension-to-type mapping established by the built-in database, which is important for stable behavior across multiple `registerEntry` calls in plugin-style usage.

View File

@@ -0,0 +1,90 @@
# detection/detect
## Purpose
The unified MIME detection entry point for `@suaveplan/mime`. Merges evidence from three independent sources — magic bytes, file extension, and a declared `Content-Type` header — using a confidence-scoring model. When sources agree, the confidence is boosted above any single-source threshold. When they disagree, magic bytes win over extension, which wins over declared headers, reflecting real-world reliability.
## Features
- Multi-source detection: bytes, extension/filename, Content-Type header
- Confidence scores: magic-bytes=0.95, extension=0.7, content-type=0.6
- Agreement boost: +0.05 per additional agreeing source (capped at 1.0)
- Falls back to `application/octet-stream` with confidence=0 when no sources available
- Accepts `filename` as an alias for `extension` (strips path components automatically)
- Swallows malformed Content-Type headers gracefully (no throws)
- Re-exports `detectFromMagicBytes` and `detectAllFromMagicBytes` for convenience
## Usage: Basic Example
```typescript
import { detect } from "@suaveplan/mime/detection/detect";
const result = detect({
bytes: fileBuffer,
filename: "photo.jpg",
});
console.log(result.mime); // "image/jpeg"
console.log(result.confidence); // 0.95 (magic bytes match)
console.log(result.source); // "magic-bytes"
```
## Usage: Advanced Example
```typescript
import { detect } from "@suaveplan/mime/detection/detect";
// All three sources provided — if they agree, confidence is boosted
const result = detect({
bytes: buffer,
filename: "report.pdf",
contentType: "application/pdf",
});
if (result.confidence > 0.9) {
console.log("High confidence:", result.mime);
console.log("Sources:", result.candidates.map(c => c.source));
}
// Only extension provided
const byExtension = detect({ extension: "svg" });
console.log(byExtension.mime); // "image/svg+xml"
console.log(byExtension.confidence); // 0.7
// No sources — safe fallback
const fallback = detect({});
console.log(fallback.mime); // "application/octet-stream"
console.log(fallback.confidence); // 0
```
## API Reference
### `detect(input: DetectInput): DetectResult`
| Parameter | Type | Description |
|-----------|------|-------------|
| `input` | `DetectInput` | Detection inputs — at least one field should be set. |
**Returns:** `DetectResult` with `mime`, `confidence`, `source`, and `candidates` array.
**Throws:** Never — malformed Content-Type headers are silently ignored.
### `DetectInput`
| Field | Type | Description |
|-------|------|-------------|
| `bytes?` | `Uint8Array` | Raw file bytes for magic-byte detection |
| `extension?` | `string` | File extension (with or without dot) |
| `filename?` | `string` | Full filename or path (extension extracted automatically) |
| `contentType?` | `string` | Raw `Content-Type` header value |
### `DetectResult`
| Field | Type | Description |
|-------|------|-------------|
| `mime` | `string` | Best MIME type guess |
| `confidence` | `number` | 0.0–1.0 confidence score |
| `source` | `DetectSource` | Primary detection source |
| `candidates` | `Candidate[]` | All candidate MIME types with their sources |
## Implementation Notes
Candidates are collected in descending confidence order: magic-bytes first, then extension, then content-type. Because this order is fixed and confidence values are monotonically decreasing, the first candidate in the array is always the best. The confidence boost formula `min(1, bestScore + (agreeing - 1) * 0.05)` adds 5% for each additional source that agrees with the winner, reflecting that corroborating evidence raises overall certainty.

View File

@@ -0,0 +1,56 @@
# detection/from-content-type
## Purpose
Detects the MIME type from a raw `Content-Type` header string. Parses the header using the full RFC 7231 parser and wraps the result in a `DetectionResult` envelope. Serves as one of three detection sources in the unified `detect()` function, with a confidence weight of 0.6 (lowest of the three sources, since declared headers can be wrong or missing).
## Features
- Delegates to `parseContentType` for full RFC 7231 parsing
- Returns a `DetectionResult` with `source: "content-type"` and the canonical `essence`
- Propagates `InvalidContentTypeError` for malformed inputs (callers can catch or ignore)
- Accepts parameters in the header (they are parsed but not included in the MIME result)
## Usage: Basic Example
```typescript
import { detectFromContentType } from "@suaveplan/mime/detection/from-content-type";
const result = detectFromContentType("text/html; charset=utf-8");
console.log(result.mime); // "text/html"
console.log(result.source); // "content-type"
```
## Usage: Advanced Example
```typescript
import { detectFromContentType } from "@suaveplan/mime/detection/from-content-type";
import { InvalidContentTypeError } from "@suaveplan/mime/errors";
// The detect() facade catches these errors automatically.
// Use detectFromContentType directly when you want strict validation:
try {
const result = detectFromContentType(request.headers.get("content-type") ?? "");
console.log("Declared MIME:", result.mime);
} catch (err) {
if (err instanceof InvalidContentTypeError) {
console.warn("Malformed Content-Type header — using fallback detection");
}
}
```
## API Reference
### `detectFromContentType(header: string): DetectionResult`
| Parameter | Type | Description |
|-----------|------|-------------|
| `header` | `string` | Raw `Content-Type` header value. |
**Returns:** `DetectionResult` with `mime` set to the `essence` (`type/subtype`) and `source: "content-type"`.
**Throws:** `InvalidContentTypeError` if the header is malformed.
## Implementation Notes
This function intentionally throws on malformed input rather than returning a fallback. The `detect()` facade wraps calls to `detectFromContentType` in a try/catch to silently skip bad headers. This two-layer design keeps the individual detectors strict while the unified facade remains lenient — matching the principle that each building block should have well-defined error semantics, with fault tolerance composed at a higher level.

View File

@@ -0,0 +1,62 @@
# detection/from-extension
## Purpose
Detects the MIME type from a file extension or full filename. Strips path components and leading dots, normalizes to lowercase, and looks up the result in the built-in MIME database. Returns a `DetectionResult` with `source: "extension"` and a confidence weight of 0.7 in the unified `detect()` function.
## Features
- Accepts both raw extensions (`.png`, `png`) and full filenames (`photo.jpeg`)
- Strips directory path components automatically
- Normalizes extensions to lowercase before lookup
- Handles multiple dots in filenames correctly (uses the last extension)
- Returns `undefined` for unknown extensions instead of throwing
- Wraps the database entry in a `DetectionResult` envelope
## Usage: Basic Example
```typescript
import { detectFromExtension } from "@suaveplan/mime/detection/from-extension";
const result = detectFromExtension("photo.jpg");
console.log(result?.mime); // "image/jpeg"
console.log(result?.source); // "extension"
// Just an extension without filename
const svg = detectFromExtension(".svg");
console.log(svg?.mime); // "image/svg+xml"
```
## Usage: Advanced Example
```typescript
import { detectFromExtension } from "@suaveplan/mime/detection/from-extension";
function getMimeForUpload(filename: string): string {
const detected = detectFromExtension(filename);
// Fall back to binary stream for unknown extensions
return detected?.mime ?? "application/octet-stream";
}
// Handles paths with directories
console.log(detectFromExtension("/uploads/2024/report.pdf")?.mime); // "application/pdf"
// Unknown extension returns undefined
console.log(detectFromExtension("file.xyz")); // undefined
```
## API Reference
### `detectFromExtension(input: string): DetectionResult | undefined`
| Parameter | Type | Description |
|-----------|------|-------------|
| `input` | `string` | File extension (with or without dot) or full filename/path. |
**Returns:** `DetectionResult` with `source: "extension"` and the matched `mime`, or `undefined` when the extension is not in the database.
**Throws:** Never.
## Implementation Notes
The function extracts the extension by splitting on `.` and taking the last segment, then looking up `getEntryByExtension`. The split also handles filenames with no extension (returns `undefined`) and filenames starting with a dot (treated as hidden files with no extension, e.g. `.gitignore`). Path components are stripped by looking only at the basename portion after the last `/` or `\`. Lookup is O(1) via the registry's `Map<string, MimeEntry>` index.

View File

@@ -0,0 +1,86 @@
# detection/from-magic-bytes
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs --> - helpers — Low-level byte-inspection utilities used……
- [helpers](./helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
- helpers — Low-level byte-inspection utilities used……
- [helpers](./helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
- helpers — Low-level byte-inspection utilities used……
- [helpers](./helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
- helpers — Low-level byte-inspection utilities used…
- [helpers](./helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
- [helpers](./helpers.md) — Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: pr…
- [registry](./registry.md) — The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-…
## Purpose
Implements magic-byte (file signature) detection for MIME types. Maintains a registry of byte-pattern matchers sorted by priority, and provides functions to find the best match, all matches, or reset the registry. Magic-byte detection has the highest confidence (0.95) in the unified `detect()` function because byte signatures are objective evidence that cannot be spoofed by file naming or HTTP headers.
## Features
- `detectFromMagicBytes(bytes)` — returns the highest-priority matching MIME type
- `detectAllFromMagicBytes(bytes)` — returns all matching MIME types sorted by priority
- Registry sorted by descending priority for deterministic match order
- Accepts any `Uint8Array` or `ArrayBuffer`-backed view
- Returns `undefined` (not throws) when no signature matches
- Supports minimum-bytes threshold per signature — skips signatures when the input is too short
## Usage: Basic Example
```typescript
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
// PNG magic bytes: 89 50 4E 47 0D 0A 1A 0A
const pngBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
const result = detectFromMagicBytes(pngBytes);
console.log(result?.mime); // "image/png"
console.log(result?.priority); // 100
```
## Usage: Advanced Example
```typescript
import {
detectFromMagicBytes,
detectAllFromMagicBytes,
} from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
// Detect all matching signatures (useful for ambiguous formats like MP4 variants)
const mp4Bytes = new Uint8Array([...]);
const allMatches = detectAllFromMagicBytes(mp4Bytes);
for (const match of allMatches) {
console.log(match.mime, match.priority);
}
// Returns undefined for unrecognized byte sequences
const random = new Uint8Array([0x00, 0x01, 0x02, 0x03]);
console.log(detectFromMagicBytes(random)); // undefined
```
## API Reference
### `detectFromMagicBytes(bytes: Uint8Array): SignatureMatch | undefined`
| Parameter | Type | Description |
|-----------|------|-------------|
| `bytes` | `Uint8Array` | Byte buffer to inspect (first N bytes of a file). |
**Returns:** The highest-priority `SignatureMatch` (`mime`, `priority`), or `undefined`.
### `detectAllFromMagicBytes(bytes: Uint8Array): SignatureMatch[]`
Returns all matching signatures sorted by descending priority.
## Implementation Notes
The registry is sorted once whenever a new signature is registered, using a stable sort on `priority` (descending). Detection iterates the sorted array and returns the first match (for `detectFromMagicBytes`) or all matches (for `detectAllFromMagicBytes`). Signatures with a `minBytes` threshold are skipped when the input buffer is too short, enabling detection on partial reads without false negatives from truncated magic sequences.

View File

@@ -0,0 +1,75 @@
# detection/from-magic-bytes/helpers
## Purpose
Low-level byte-inspection utilities used by the magic-byte signature matchers. Provides helpers for common patterns: prefix matching, ASCII substring scanning, 32-bit big-endian integer reading, and byte-sequence searching. These functions are designed to be called millions of times without allocation, using only index arithmetic over the input `Uint8Array`.
## Features
- `startsWith(bytes, prefix, offset?)` — check if bytes start with a given byte sequence at an offset
- `hasAscii(bytes, text, offset?)` — check if bytes contain an ASCII string at a given offset
- `readUInt32BE(bytes, offset)` — read a 4-byte big-endian unsigned integer
- `bytesInclude(bytes, needle)` — check if a byte sequence appears anywhere in bytes
- All functions are allocation-free (no `new Uint8Array`, no `slice`)
- All functions are bounds-safe (return false/0 rather than throwing on out-of-range access)
## Usage: Basic Example
```typescript
import { startsWith, hasAscii } from "@suaveplan/mime/detection/from-magic-bytes/helpers";
// Check PNG magic bytes
const PNG_MAGIC = new Uint8Array([0x89, 0x50, 0x4e, 0x47]);
const isPng = startsWith(fileBytes, PNG_MAGIC);
// Check for RIFF tag at offset 0, WAVE tag at offset 8
const isWav = hasAscii(fileBytes, "RIFF") && hasAscii(fileBytes, "WAVE", 8);
```
## Usage: Advanced Example
```typescript
import {
startsWith,
readUInt32BE,
bytesInclude,
} from "@suaveplan/mime/detection/from-magic-bytes/helpers";
// MP4: ftyp box at byte 4
function isMp4(bytes: Uint8Array): boolean {
const boxSize = readUInt32BE(bytes, 0);
return boxSize >= 8 && hasAscii(bytes, "ftyp", 4);
}
// ZIP: PK signature or EOCD record
function isZip(bytes: Uint8Array): boolean {
return startsWith(bytes, new Uint8Array([0x50, 0x4b, 0x03, 0x04])) ||
startsWith(bytes, new Uint8Array([0x50, 0x4b, 0x05, 0x06]));
}
// Search for a needle anywhere in a buffer
const PDF_HEADER = new Uint8Array([0x25, 0x50, 0x44, 0x46]); // %PDF
const containsPdf = bytesInclude(largeBuffer, PDF_HEADER);
```
## API Reference
### `startsWith(bytes: Uint8Array, prefix: Uint8Array, offset?: number): boolean`
Returns `true` if `bytes[offset...]` starts with all bytes of `prefix`. Returns `false` if the buffer is too short.
### `hasAscii(bytes: Uint8Array, text: string, offset?: number): boolean`
Returns `true` if the ASCII codes of `text` match `bytes[offset...]`. Returns `false` if the buffer is too short.
### `readUInt32BE(bytes: Uint8Array, offset: number): number`
Reads 4 bytes starting at `offset` as a big-endian unsigned 32-bit integer. Returns `0` if the buffer is too short.
### `bytesInclude(bytes: Uint8Array, needle: Uint8Array): boolean`
Returns `true` if `needle` appears anywhere within `bytes`. Returns `false` if the buffer is shorter than the needle.
## Implementation Notes
All helpers operate directly on the `Uint8Array` buffer without creating sub-views or intermediate arrays. The `hasAscii` function uses `charCodeAt` rather than `TextEncoder` to avoid the allocation overhead of encoding a string to bytes on every call — this matters because signature matchers run on every file detection. The `bytesInclude` function uses a naive O(n×m) scan, which is acceptable given that needle lengths are always small (≤8 bytes for magic sequences) and the search space is bounded by the buffer size.

View File

@@ -0,0 +1,84 @@
# detection/from-magic-bytes/registry
## Purpose
The mutable signature registry for magic-byte detection. Stores all registered byte-pattern matchers and provides CRUD-like access: `registerSignature` to add a matcher, `getSignatures` to read the sorted list, and `resetSignatures` to clear all entries (used in tests). This module is the single source of truth for the ordered list of matchers consumed by `detectFromMagicBytes`.
## Features
- `registerSignature(sig)` — add a new signature matcher; re-sorts registry by priority
- `getSignatures()` — returns the full registry array sorted by descending priority
- `resetSignatures()` — clears all registered signatures (test isolation only)
- Priority-ordered registry: higher priority signatures are checked first
- Thread-safe within a single JavaScript execution context (no async race conditions)
## Usage: Basic Example
```typescript
import {
registerSignature,
getSignatures,
} from "@suaveplan/mime/detection/from-magic-bytes/registry";
registerSignature({
mime: "application/x-my-format",
priority: 50,
minBytes: 4,
match: (bytes) => bytes[0] === 0x4d && bytes[1] === 0x59,
});
console.log(getSignatures().find(s => s.mime === "application/x-my-format"));
```
## Usage: Advanced Example
```typescript
import { registerSignature } from "@suaveplan/mime/detection/from-magic-bytes/registry";
// Custom signature for a proprietary binary format
registerSignature({
mime: "application/x-leveldb",
priority: 75,
minBytes: 8,
match: (bytes) =>
bytes[0] === 0xff &&
bytes[1] === 0x06 &&
bytes[2] === 0x00 &&
bytes[3] === 0x00,
});
// Signature with a scan offset
registerSignature({
mime: "image/x-xcf",
priority: 80,
minBytes: 9,
match: (bytes) => {
// "gimp xcf " at offset 0
const magic = [0x67, 0x69, 0x6d, 0x70, 0x20, 0x78, 0x63, 0x66, 0x20];
return magic.every((b, i) => bytes[i] === b);
},
});
```
## API Reference
### `registerSignature(sig: Signature): void`
| Parameter | Type | Description |
|-----------|------|-------------|
| `sig.mime` | `string` | MIME type this signature identifies |
| `sig.priority` | `number` | Higher values are checked first (0–100 typical range) |
| `sig.minBytes` | `number` | Minimum bytes required to run the matcher |
| `sig.match` | `(bytes: Uint8Array) => boolean` | Byte-pattern predicate |
### `getSignatures(): Signature[]`
Returns the full sorted registry array. Do not mutate the returned array directly.
### `resetSignatures(): void`
Clears all signatures. Intended for test isolation only — not safe for production use.
## Implementation Notes
The registry maintains a sorted array rather than a priority queue for simplicity. `registerSignature` performs an in-place sort after insertion, which is O(n log n) but acceptable given that signatures are registered at module load time (once per process) and detection reads are O(n) anyway. The sort is stable in modern JavaScript engines, so signatures with equal priority retain their insertion order.

View File

@@ -0,0 +1,39 @@
# signatures/archives
## Purpose
Registers magic-byte signatures for archive and compressed file formats with the global signature registry. Covers all major compression and container formats used in file distribution and backup. Each signature is registered as a side effect of importing this module.
## Features
- ZIP, JAR, ODF, DOCX/PPTX/XLSX (all ZIP-based)
- GZIP, BZIP2, XZ/7-Zip, LZMA
- RAR (v4 and v5 signatures)
- TAR (ustar header at offset 257)
- ZSTD (Zstandard frame magic)
- LZ4 frame magic
- CPIO (binary and ASCII newc variants)
- ISO 9660 CD image (CD001 at offset 32769)
- BZ2 single-file bzip2
## Usage
```typescript
import "@suaveplan/mime/detection/from-magic-bytes/signatures/archives";
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
const gzipBytes = new Uint8Array([0x1f, 0x8b, 0x08]);
console.log(detectFromMagicBytes(gzipBytes)?.mime); // "application/gzip"
```
## API Reference
No exported functions. All signatures are registered via `registerSignature` side effects.
## Implementation Notes
ZIP-based formats (JAR, ODF, Office Open XML) share the PK signature but cannot be distinguished without inspecting the ZIP directory entries — the magic-byte detector returns `application/zip` generically for all ZIP files. TAR detection looks for the `ustar` ASCII string at offset 257, which requires at least 262 bytes; shorter buffers will not match. ISO 9660 detection at offset 32769 is impractical to test and is annotated to be excluded from coverage thresholds.
### Coverage
The archive signature set covers ZIP (`PK\x03\x04` and `PK\x05\x06` empty-archive variant), 7z (`37 7A BC AF 27 1C`), RAR (v4 and v5 magics), GZIP (`1F 8B`), BZIP2 (`BZh`), XZ, TAR (ustar octet at offset 257), and Z-standard. Priority 100 is the default; nested container formats such as OOXML that are technically ZIP files are detected at priority 105 in `documents.ts` so they win the tie.

View File

@@ -0,0 +1,37 @@
# signatures/audio
## Purpose
Registers magic-byte signatures for audio formats with the global signature registry. Covers all major audio container and codec formats used in web and desktop applications. Each signature is registered as a side effect of importing this module.
## Features
- FLAC, MP3 (ID3 tag and sync word variants), OGG/Vorbis
- WAV (RIFF container), AIFF/AIFF-C
- AAC (ADTS), M4A (ISO BMFF with M4A brand)
- WMA/WMV (ASF container), MIDI
- Musepack (SV7/SV8), OptimFROG, Monkey's Audio
- DFF/DSF (DSD audio), AC-3/E-AC-3, Dolby TrueHD
- MOD tracker variants (4/6/8-channel patterns)
## Usage
```typescript
import "@suaveplan/mime/detection/from-magic-bytes/signatures/audio";
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
const flacBytes = new Uint8Array([0x66, 0x4c, 0x61, 0x43]); // "fLaC"
console.log(detectFromMagicBytes(flacBytes)?.mime); // "audio/flac"
```
## API Reference
No exported functions. All signatures are registered via `registerSignature` side effects.
## Implementation Notes
MP3 detection covers both the ID3v2 header (`49 44 33`) and raw MPEG sync words (`ff fb`, `ff f3`, `ff f2`). OGG files are further disambiguated by inspecting the codec type in the first OGG page header (bytes 28–35: `vorbis`, `FLAC`, `Opus`, `Theora`). The RIFF container is used by both WAV and AVI, with disambiguation on the RIFF type tag at offset 8.
### Coverage
The audio signature set covers MPEG audio (`ID3` tag and raw frame-sync variants at priority 80), WAV/RIFF with the `WAVE` subtype marker, FLAC (`fLaC`), Ogg (`OggS`), AIFF (`FORM` + `AIFF`), MIDI (`MThd`), AMR, and Opus. The RIFF family share a common `RIFF` container prefix which is why WAV specifically checks the subtype at offset 8 before claiming a match.

View File

@@ -0,0 +1,39 @@
# signatures/data
## Purpose
Registers magic-byte signatures for binary data and structured-data formats with the global signature registry. Covers serialization formats, scientific data files, and cryptographic containers. Each signature is registered as a side effect of importing this module.
## Features
- MessagePack binary serialization
- CBOR (RFC 7049 Concise Binary Object Representation)
- Apache Parquet columnar data format
- Apache Arrow IPC format
- HDF5 (Hierarchical Data Format version 5)
- NetCDF (Network Common Data Form)
- PCAP and PCAPng network capture files
- PEM-encoded certificates and keys
## Usage
```typescript
import "@suaveplan/mime/detection/from-magic-bytes/signatures/data";
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
// Parquet magic: "PAR1" at start and end of file
const parquetBytes = new Uint8Array([0x50, 0x41, 0x52, 0x31]); // "PAR1"
console.log(detectFromMagicBytes(parquetBytes)?.mime); // "application/vnd.apache.parquet"
```
## API Reference
No exported functions. All signatures are registered via `registerSignature` side effects.
## Implementation Notes
MessagePack has no single fixed magic header — the first byte encodes the type of the root value. The detection heuristic checks for the most common root types in well-formed MessagePack documents (fixmap, fixarray, nil, bool, fixint) to reduce false-positives while maintaining reasonable recall. PCAP files come in two endianness variants (little-endian `D4 C3 B2 A1` and big-endian `A1 B2 C3 D4`) plus a nanosecond-resolution variant (`4D 3C B2 A1`).
### Coverage
The data signature set covers SQLite (`SQLite format 3\0` at offset 0), Apache Parquet (`PAR1` at offset 0 and at end-of-file), Apache Arrow IPC, CBOR, MessagePack, BSON, and Protocol Buffers when a distinguishable length-prefixed header is present. Formats without a structural magic header (CSV, NDJSON) are not listed here; detection for those falls through to content sniffing in the BOM/text-sniffer stage.

View File

@@ -0,0 +1,38 @@
# signatures/documents
## Purpose
Registers magic-byte signatures for document and data format files with the global signature registry. Covers office documents, structured data, e-books, and markup formats. Each signature is registered as a side effect of importing this module.
## Features
- PDF (`%PDF` header)
- Microsoft Office legacy formats (DOC, XLS, PPT — OLE2 container `D0 CF 11 E0`)
- EPUB (ZIP-based e-book container)
- Rich Text Format (RTF)
- SQLite database (`SQLite format 3`)
- Apple Pages, Numbers, Keynote (ZIP-based, detected via content sniffing)
- JSON lines and CBOR (RFC 7049) binary format
- Protocol Buffers wire format heuristic
## Usage
```typescript
import "@suaveplan/mime/detection/from-magic-bytes/signatures/documents";
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
const pdfBytes = new Uint8Array([0x25, 0x50, 0x44, 0x46, 0x2d]); // "%PDF-"
console.log(detectFromMagicBytes(pdfBytes)?.mime); // "application/pdf"
```
## API Reference
No exported functions. All signatures are registered via `registerSignature` side effects.
## Implementation Notes
OLE2 Compound Document Format is the container for legacy Microsoft Office files (pre-2007 DOC/XLS/PPT). The magic bytes `D0 CF 11 E0` identify the OLE2 container but not the specific Office application — the detector returns `application/vnd.ms-office` as a generic fallback. Modern Office formats (DOCX, XLSX, PPTX) use the ZIP container and are handled in the archives signatures module.
### Coverage
The document signature set covers PDF (`%PDF`), Microsoft CFB-backed legacy Office (`D0 CF 11 E0 A1 B1 1A E1`), OOXML (`PK` with `[Content_Types].xml` entry — priority 105 so it beats plain ZIP), ODF, RTF (`{\\rtf`), PostScript (`%!PS`), EPUB (ZIP with `mimetype` = `application/epub+zip`), and MOBI/AZW. The 105 priority on container-based formats is critical: without it OOXML would match plain ZIP at 100 and lose granularity.

View File

@@ -0,0 +1,43 @@
# signatures/executables
## Purpose
Registers magic-byte signatures for executable binary formats with the global signature registry. Covers native executables, bytecode, and portable binary formats across platforms. Each signature is registered as a side effect of importing this module.
## Features
- ELF (Linux/Unix native executables and shared libraries)
- PE (Windows `.exe` and `.dll` — `MZ` header)
- Mach-O (macOS native: 32/64-bit, fat binary, ARM64)
- Java `.class` bytecode (`CA FE BA BE`)
- DEX (Android Dalvik bytecode)
- WebAssembly (`.wasm` — `00 61 73 6d`)
- Python `.pyc` bytecode (magic + timestamp header)
- LLVM bitcode
## Usage
```typescript
import "@suaveplan/mime/detection/from-magic-bytes/signatures/executables";
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
// ELF magic: 7F 45 4C 46
const elfBytes = new Uint8Array([0x7f, 0x45, 0x4c, 0x46]);
console.log(detectFromMagicBytes(elfBytes)?.mime); // "application/x-elf"
// WebAssembly magic: 00 61 73 6D
const wasmBytes = new Uint8Array([0x00, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00]);
console.log(detectFromMagicBytes(wasmBytes)?.mime); // "application/wasm"
```
## API Reference
No exported functions. All signatures are registered via `registerSignature` side effects.
## Implementation Notes
Mach-O binaries exist in four variants: 32-bit big-endian (`CE FA ED FE`), 32-bit little-endian (`FE ED FA CE`), 64-bit big-endian (`CF FA ED FE`), and 64-bit little-endian (`FE ED FA CF`). Fat binaries (`CA FE BA BE` at big-endian 0xcafebabe) share the same magic as Java class files but are distinguished by a byte at offset 7 (`0x00` for Mach-O fat, non-zero for Java). The implemention uses a combined check for the Java case.
### Coverage
The executable signature set covers WebAssembly (`\x00asm`), ELF (`\x7FELF` — Linux / BSD native), Mach-O (FAT + thin, 32/64-bit big/little endian variants), PE/COFF (`MZ`), Java class files (`CAFEBABE`), .NET assemblies (via PE with CLI header), and bytecode containers like Python `.pyc` and Lua `.luac`. Multiple Mach-O magics exist because Apple shipped multiple endianness and pointer-width variants; all land on `application/x-mach-binary`.

View File

@@ -0,0 +1,37 @@
# signatures/fonts
## Purpose
Registers magic-byte signatures for font file formats with the global signature registry. Covers all major web font and desktop font formats. Each signature is registered as a side effect of importing this module.
## Features
- WOFF (Web Open Font Format) — `wOFF` magic
- WOFF2 — `wOF2` magic
- TrueType (`.ttf`) — `00 01 00 00 00` header
- OpenType (`.otf`) — `4F 54 54 4F` (`OTTO`) header
- EOT (Embedded OpenType) — Microsoft format
- `font/collection` (TTC, OpenType Collection)
- `vnd.ms-fontobject` variants
## Usage
```typescript
import "@suaveplan/mime/detection/from-magic-bytes/signatures/fonts";
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
const woff2Bytes = new Uint8Array([0x77, 0x4f, 0x46, 0x32]); // "wOF2"
console.log(detectFromMagicBytes(woff2Bytes)?.mime); // "font/woff2"
```
## API Reference
No exported functions. All signatures are registered via `registerSignature` side effects.
## Implementation Notes
TrueType and OpenType both start with a 4-byte "sfVersion" tag at offset 0. TrueType uses `00 01 00 00` (version 1.0 in fixed-point notation) while OpenType uses `OTTO`. CFF-based fonts use `OTTO` in the sfVersion field; TrueType collections (`.ttc`) use `ttcf`. WOFF and WOFF2 are wrapper formats that contain either TrueType or OpenType data and are unambiguously identified by their `wOFF` / `wOF2` magic.
### Coverage
The font signature set covers TrueType (`\x00\x01\x00\x00`), OpenType (`OTTO` — CFF-based), WOFF1 (`wOFF`), WOFF2 (`wOF2`), TrueType Collection (`ttcf`), EOT (Microsoft Embedded OpenType), and SVG-font variants. All the signatures sit at priority 100 because each sfVersion / wrapper magic is globally unique and there are no container-based tie-breakers in the font space.

View File

@@ -0,0 +1,39 @@
# signatures/images
## Purpose
Registers magic-byte signatures for image formats with the global signature registry. Covers all major raster and vector image types used on the web and in desktop applications. Each signature is registered as a side effect of importing this module.
## Features
- PNG, JPEG, GIF (87a, 89a), WebP, BMP, ICO, TIFF (BE/LE)
- HEIC, HEIF, AVIF (multiple brand variants)
- PSD (Adobe Photoshop), SVG, XCF (GIMP)
- PBM, PGM, PPM, PNM, PFM portable bitmap variants
- Radiance HDR (`.hdr`, `.rgbe`)
- OpenEXR high dynamic range images
## Usage
This module is imported for its side effects only:
```typescript
// Automatic registration — all image signatures become available for detection
import "@suaveplan/mime/detection/from-magic-bytes/signatures/images";
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
const pngBytes = new Uint8Array([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]);
console.log(detectFromMagicBytes(pngBytes)?.mime); // "image/png"
```
## API Reference
No exported functions. All signatures are registered via `registerSignature` side effects on module load.
## Implementation Notes
HEIC/HEIF/AVIF formats share the ISO Base Media File Format container (like MP4) and are distinguished by their `ftyp` brand code at bytes 8–11. The `startsWith` helper checks both the `ftyp` box header at offset 4 and the brand at offset 8 for each variant. Radiance HDR files use the ASCII header `#?RGBE` or `#?RADIANCE` at offset 0.
### Coverage
The image signature set covers PNG (`89 50 4E 47 0D 0A 1A 0A`), JPEG (`FF D8 FF`), GIF (`GIF87a` / `GIF89a`), WebP (`RIFF` + `WEBP` subtype), AVIF / HEIC (ISOBMFF `ftyp` brand inside offset-4 box), BMP, TIFF (`II*\0` little-endian and `MM\0*` big-endian), ICO, and SVG (text-sniffed in a separate stage). ISOBMFF brand matching is done against a curated brand set so AVIF, HEIC, HEIF, and MIAF variants all resolve cleanly.

View File

@@ -0,0 +1,36 @@
# signatures/model3d
## Purpose
Registers magic-byte signatures for 3D model file formats with the global signature registry. Covers formats used in CAD, game development, and 3D printing. Each signature is registered as a side effect of importing this module.
## Features
- glTF binary (`.glb`) — `glTF` magic + version 2
- STL binary and ASCII (3D printing format)
- FBX binary (`Kaydara FBX Binary` header)
- Collada (`.dae`) — XML-based, identified by `<?xml` prolog
- PLY (Stanford Polygon Library)
## Usage
```typescript
import "@suaveplan/mime/detection/from-magic-bytes/signatures/model3d";
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
// glTF binary magic: 67 6C 54 46
const glbBytes = new Uint8Array([0x67, 0x6c, 0x54, 0x46, 0x02, 0x00, 0x00, 0x00]);
console.log(detectFromMagicBytes(glbBytes)?.mime); // "model/gltf-binary"
```
## API Reference
No exported functions. All signatures are registered via `registerSignature` side effects.
## Implementation Notes
STL binary files have no fixed magic sequence — they start with an 80-byte header of arbitrary text. The detection heuristic checks for the absence of the ASCII string `solid` at offset 0 (which would indicate ASCII STL) combined with a plausible triangle count in bytes 80–83. ASCII STL files are identified by the `solid` keyword at offset 0. The MIME type `model/stl` is returned for both binary and ASCII STL when detected.
### Coverage
The 3D-model signature set covers glTF binary (`glTF` magic at offset 0), USDZ (a ZIP container with a specific first-entry layout), FBX (both ASCII `; FBX` and binary `Kaydara FBX Binary \0`), STL (ASCII vs binary are disambiguated by header-byte heuristics), OBJ (text-sniffed), 3MF, and DAE/Collada (XML with known root element). The binary FBX magic uses 20 bytes including two trailing null bytes and a SUB character — matching that full prefix avoids false positives on other tools that write `Kaydara`-prefixed files.

View File

@@ -0,0 +1,38 @@
# signatures/video
## Purpose
Registers magic-byte signatures for video container formats with the global signature registry. Covers all major video formats used on the web and in streaming applications. Each signature is registered as a side effect of importing this module.
## Features
- MP4 / M4V (ISO BMFF with multiple `ftyp` brands: `isom`, `iso2`, `mp41`, `mp42`, etc.)
- WebM and MKV (Matroska EBML header)
- AVI (RIFF container with AVI type)
- QuickTime MOV, 3GPP, 3GPP2
- MPEG-1/2 PS (`ba`/`b3` pack start codes)
- MPEG-2 TS (188-byte sync word pattern)
- Windows Media (`video/x-ms-wmv` via ASF header)
- Flash Video (FLV), DV video
## Usage
```typescript
import "@suaveplan/mime/detection/from-magic-bytes/signatures/video";
import { detectFromMagicBytes } from "@suaveplan/mime/detection/from-magic-bytes/from-magic-bytes";
const webmBytes = new Uint8Array([0x1a, 0x45, 0xdf, 0xa3]); // EBML header
console.log(detectFromMagicBytes(webmBytes)?.mime); // "video/webm"
```
## API Reference
No exported functions. All signatures are registered via `registerSignature` side effects.
## Implementation Notes
MP4 variants are distinguished by their `ftyp` brand at bytes 8–11 within the ISO Base Media File Format container. Multiple brands map to `video/mp4` (isom, iso2, mp41, mp42, etc.) while M4V uses the `M4V` brand. MPEG-2 TS detection requires the sync byte `0x47` at both offset 0 and offset 188, making it impractical to test without 189+ bytes; the signature is annotated to be excluded from coverage.
### Coverage
The video signature set covers ISOBMFF containers (MP4, QuickTime `.mov`, 3GP — disambiguated by `ftyp` brand at offset 4), Matroska / WebM (`1A 45 DF A3` EBML header + DocType check), AVI (`RIFF` + `AVI` subtype), MPEG transport stream (`0x47` sync byte at 188-byte intervals), FLV (`FLV\x01`), and Ogg (when the first packet is a Theora stream). ISOBMFF brand matching is the central complexity — MP4, 3GP, QuickTime, and HEIC share a header and only the brand distinguishes them.

View File

@@ -0,0 +1,36 @@
# detection/from-stream-reader
## Purpose
Internal module providing the `toAsyncIterable` helper and the `drainViaReader` legacy fallback for `ReadableStream` sources. This module is excluded from coverage measurement because the `getReader()` fallback path is only reachable on runtimes where `ReadableStream` does not implement `Symbol.asyncIterator` (pre-Node-18 and some embedded environments). In Bun and Node 18+, the fast path is always taken.
## Features
- `toAsyncIterable(source)` — coerces `ReadableStream` or `AsyncIterable` to `AsyncIterable<Uint8Array>`
- `drainViaReader(source)` — async generator that pulls chunks via `ReadableStream.getReader()` with proper lock release in the `finally` block
- Fast path: when `Symbol.asyncIterator` is present (Bun/Node 18+), the source is returned as-is with no overhead
- Slow path: when `Symbol.asyncIterator` is absent, wraps `getReader()` in an async iterable adapter
## Usage
This module is not part of the public API surface. It is imported by `from-stream.ts`:
```typescript
// Internal usage only
import { toAsyncIterable } from "./from-stream-reader.js";
const iter = toAsyncIterable(source)[Symbol.asyncIterator]();
```
## API Reference
### `drainViaReader(source: ReadableStream<Uint8Array>): AsyncGenerator<Uint8Array>`
Pulls all chunks from a `ReadableStream` using the `getReader()` pull API. Releases the lock in the `finally` block to ensure cleanup even when the consumer aborts iteration.
### `toAsyncIterable(source): AsyncIterable<Uint8Array>`
Coerces any `ByteSource` to `AsyncIterable<Uint8Array>`. Fast path if `Symbol.asyncIterator` is present; otherwise returns an adapter object backed by `drainViaReader`.
## Implementation Notes
Bun and Node 18+ expose `Symbol.asyncIterator` on `ReadableStream`, making `for await (const chunk of stream)` work natively. The `getReader()` fallback exists for completeness (older Node, Deno, Cloudflare Workers pre-2023, some browser environments). This file is listed in the coverage `exclude` list in `vitest.config.ts` because the fallback cannot be exercised in the Bun test environment without significant test infrastructure for simulating legacy runtimes.

View File

@@ -0,0 +1,79 @@
# detection/from-stream
## Purpose
Provides `detectFromStream`, which reads the first N bytes from any async byte source (`ReadableStream<Uint8Array>` or `AsyncIterable<Uint8Array>`) and runs magic-byte detection on the buffered prefix. Returns the detection result together with a `head` buffer (the peeked bytes) and a `tail` async generator that replays the full stream (carry bytes + remaining), enabling callers to re-read the stream from the beginning after detection.
## Features
- Accepts both `ReadableStream<Uint8Array>` and `AsyncIterable<Uint8Array>`
- Configurable peek window (`peekBytes`, default 512)
- AbortSignal support for cancellation during read
- Returns a `head` buffer containing the bytes used for detection
- Returns a `tail` generator that yields carry bytes (overflow from the last peeked chunk) then remaining stream chunks
- Zero-copy split: the overflow portion of the boundary chunk is sliced without re-copying the head bytes
- Full-stream reassembly: `head` + `tail` yields the complete original byte sequence
## Usage: Basic Example
```typescript
import { detectFromStream } from "@suaveplan/mime/detection/from-stream";
const result = await detectFromStream(fileStream);
console.log(result.mime); // "image/png" (or undefined if unknown)
console.log(result.source); // "stream"
// Consume the full stream content after detection
for await (const chunk of result.tail) {
process(chunk);
}
```
## Usage: Advanced Example
```typescript
import { detectFromStream } from "@suaveplan/mime/detection/from-stream";
async function processUpload(stream: ReadableStream<Uint8Array>, signal: AbortSignal) {
const { mime, head, tail } = await detectFromStream(stream, {
peekBytes: 1024, // inspect up to 1 KB
signal, // cancel if the request is aborted
});
if (mime !== "image/jpeg" && mime !== "image/png") {
throw new Error(`Unsupported image type: ${mime}`);
}
// Process the full image data: head is already a Uint8Array,
// tail is an async iterable that delivers the rest
const chunks: Uint8Array[] = [head];
for await (const chunk of tail) {
chunks.push(chunk);
}
return processImage(mime, chunks);
}
```
## API Reference
### `detectFromStream(source, options?): Promise<StreamDetectionResult>`
| Parameter | Type | Description |
|-----------|------|-------------|
| `source` | `ReadableStream<Uint8Array> \| AsyncIterable<Uint8Array>` | The byte stream to inspect. |
| `options.peekBytes?` | `number` | Maximum bytes to buffer for detection (default: 512). |
| `options.signal?` | `AbortSignal` | Cancellation signal. |
**Returns:** `StreamDetectionResult`:
| Field | Type | Description |
|-------|------|-------------|
| `source` | `"stream"` | Always `"stream"` |
| `mime` | `string \| undefined` | Detected MIME type |
| `priority` | `number \| undefined` | Signature priority score |
| `head` | `Uint8Array` | The peeked bytes used for detection |
| `tail` | `AsyncGenerator<Uint8Array>` | Remaining stream bytes after head |
## Implementation Notes
The `toAsyncIterable` helper in `from-stream-reader.ts` coerces `ReadableStream` to `AsyncIterable`. In Bun and Node 18+, `ReadableStream` already implements `Symbol.asyncIterator`, so the fast path is always taken. The legacy `getReader()` fallback exists for embedded runtimes and is excluded from coverage. The `tail` generator yields carry bytes (the overflow portion of the last buffered chunk, if the chunk straddled the `peekBytes` boundary) before draining the iterator, ensuring byte ordering is preserved across the head/tail split.

View File

@@ -0,0 +1,63 @@
# error-codes
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Defines the stable numeric error-code registry for every error class in `@suaveplan/mime`. Each code is a non-overlapping integer in the `5000–5099` range, allocated via `defineCodes` from `@suaveplan/error`. Stable codes enable error identification across serialization, transport, and version upgrades without relying on class names or message strings.
## Features
- Five distinct error codes: `INVALID_CONTENT_TYPE`, `UNKNOWN_MIME_TYPE`, `INSUFFICIENT_BYTES`, `PARSE_ERROR`, `UNKNOWN_SIGNATURE`
- Allocated via `@suaveplan/error`'s `defineCodes` for namespace isolation
- Zero runtime overhead — values are plain numbers resolved at import time
- Deterministic: the same integer is guaranteed across package versions
## Usage: Basic Example
```typescript
import { MIME_ERROR_CODES } from "@suaveplan/mime/errors/codes";
// Identify any MIME error by numeric code
if (error.code === MIME_ERROR_CODES.INVALID_CONTENT_TYPE) {
console.error("Malformed Content-Type header");
}
```
## Usage: Advanced Example
```typescript
import { MIME_ERROR_CODES } from "@suaveplan/mime/errors/codes";
import { InvalidContentTypeError } from "@suaveplan/mime/errors";
// The code on each thrown error matches the registry
try {
parseContentType("not-a-content-type");
} catch (err) {
if (err instanceof InvalidContentTypeError) {
console.log(err.code === MIME_ERROR_CODES.INVALID_CONTENT_TYPE); // true
}
}
```
## API Reference
### `MIME_ERROR_CODES`
| Name | Description |
|------|-------------|
| `INVALID_CONTENT_TYPE` | Malformed or unparseable Content-Type header |
| `UNKNOWN_MIME_TYPE` | MIME type not found in the built-in database |
| `INSUFFICIENT_BYTES` | Too few bytes provided for magic-byte detection |
| `PARSE_ERROR` | General parse failure (XML/HTML charset sniffing) |
| `UNKNOWN_SIGNATURE` | Byte signature pattern not recognized |
## Implementation Notes
The range `5000–5099` is reserved for `@suaveplan/mime`. Adjacent packages use non-overlapping ranges so that a project-wide error router can dispatch on code alone. The `defineCodes` call enforces uniqueness and throws at import time if a range collision is detected during development.
### Registry Layout
Every code lives under the `MIME` namespace and is produced by `defineCodes` from `@suaveplan/error`: `MIME.PARSE.INVALID`, `MIME.CONTENT_TYPE.INVALID`, `MIME.TYPE.UNKNOWN`, `MIME.SIGNATURE.UNKNOWN`, `MIME.BYTES.INSUFFICIENT`. The `MimeErrorCode` union is used by the `MimeError<TCode>` generic so subclasses advertise exactly which codes they can carry.

View File

@@ -0,0 +1,65 @@
# errors/insufficient-bytes-error
## Purpose
Thrown when a magic-byte detection operation receives fewer bytes than the minimum required by the signature. Carries both the number of bytes received and the number required in `context` for precise error reporting.
## Features
- Error code `MIME_ERROR_CODES.INSUFFICIENT_BYTES`
- `context.received` — number of bytes provided
- `context.required` — minimum bytes needed for detection
- `name` set to `"InsufficientBytesError"`
- Full `SuaveplanError` serialization
## Usage: Basic Example
```typescript
import { InsufficientBytesError } from "@suaveplan/mime/errors";
function requireMinBytes(bytes: Uint8Array, minBytes: number): void {
if (bytes.length < minBytes) {
throw new InsufficientBytesError({
context: { received: bytes.length, required: minBytes },
});
}
}
```
## Usage: Advanced Example
```typescript
import { InsufficientBytesError } from "@suaveplan/mime/errors";
try {
detectFromBytes(tinyBuffer);
} catch (err) {
if (err instanceof InsufficientBytesError) {
console.warn(
`Need ${err.context?.required} bytes, got ${err.context?.received}. ` +
"Read more bytes and retry."
);
}
}
```
## API Reference
### `class InsufficientBytesError extends MimeError`
**Constructor options:**
| Field | Type | Description |
|-------|------|-------------|
| `message?` | `string` | Human-readable message |
| `context?.received` | `number` | Bytes actually received |
| `context?.required` | `number` | Minimum bytes required |
| `cause?` | `unknown` | Underlying cause |
## Implementation Notes
This error is not currently thrown by the public `detectFromMagicBytes` API (which returns `undefined` for insufficient bytes rather than throwing). It is available for use by higher-level wrappers that prefer a fail-fast behavior over returning a nullable result. Matching the `received`/`required` context fields against monitoring thresholds enables proactive detection of truncated upload streams.
### Callers
Thrown by stream-oriented detection paths (`detectFromStream`) when the upstream `ReadableStream<Uint8Array>` yields fewer bytes than any candidate signature's `minBytes` requires. Callers that do their own buffering should catch this specifically and request more bytes rather than giving up — most signatures need only 4–16 bytes, so a single additional chunk is usually enough. The error carries the `requested` and `available` byte counts for log context.

View File

@@ -0,0 +1,72 @@
# errors/invalid-content-type-error
## Purpose
Thrown when a `Content-Type` header or media type string fails RFC 7231 validation. Carries the raw input string and optional byte position in the `context` field to help callers produce useful error messages for end users and debugging.
## Features
- Error code `MIME_ERROR_CODES.INVALID_CONTENT_TYPE`
- Optional `context.raw` field with the offending input string
- Optional `context.position` field with the byte offset where parsing failed
- `name` set to `"InvalidContentTypeError"`
- Full `SuaveplanError` serialization (JSON + binary wire format, redaction, OTel)
## Usage: Basic Example
```typescript
import { parseContentType } from "@suaveplan/mime/content-type/parse-content-type";
import { InvalidContentTypeError } from "@suaveplan/mime/errors";
try {
parseContentType("not a valid content type");
} catch (err) {
if (err instanceof InvalidContentTypeError) {
console.error("Invalid input:", err.context?.raw);
}
}
```
## Usage: Advanced Example
```typescript
import { InvalidContentTypeError } from "@suaveplan/mime/errors";
// Throw directly with full context
function assertValidContentType(header: string): void {
if (!header.includes("/")) {
throw new InvalidContentTypeError({
message: `Content-Type must contain a slash: "${header}"`,
context: { raw: header, position: 0 },
});
}
}
// Wire-format serialization is inherited from SuaveplanError
const err = new InvalidContentTypeError({
message: "bad input",
context: { raw: "text" },
});
const json = err.toJSON(); // { code, message, context, name, ... }
```
## API Reference
### `class InvalidContentTypeError extends MimeError`
**Constructor options:**
| Field | Type | Description |
|-------|------|-------------|
| `message` | `string` | Human-readable error description |
| `context?.raw` | `string` | The raw input that failed parsing |
| `context?.position` | `number` | Byte offset where the error occurred |
| `cause?` | `unknown` | Underlying cause (for cause-chain traversal) |
## Implementation Notes
The `context` field uses `exactOptionalPropertyTypes` semantics — the field is entirely absent when not provided, not set to `undefined`. This matches `@suaveplan/error`'s strict typing conventions and avoids false positives when checking `err.context?.raw`.
### Callers
Thrown by `parseContentType` when the RFC 7231 grammar is violated: missing `/`, invalid token characters in type or subtype, malformed parameter syntax, or an unterminated quoted-string. The error's `input` field carries the raw header value so API logs can show exactly what the client sent. Consumers generally respond with HTTP 400 — the request is unparseable, not merely unsupported.

View File

@@ -0,0 +1,59 @@
# errors/mime-error
## Purpose
The base error class for all `@suaveplan/mime` errors. Extends `SuaveplanError` from `@suaveplan/error` with the `MIME_ERROR_CODES` namespace and provides a common ancestor for `instanceof` checks across all MIME-specific error types.
## Features
- Extends `SuaveplanError` for consistent error serialization and OTel integration
- `code` field set to `MIME_ERROR_CODES.INVALID_CONTENT_TYPE` by default (overridden by subclasses)
- `name` property set to `"MimeError"` for easy identification in logs
- Compatible with `SuaveplanError`'s cause-chain traversal and redaction APIs
- Used as the base for `instanceof MimeError` checks when callers don't need the specific subtype
## Usage: Basic Example
```typescript
import { MimeError } from "@suaveplan/mime/errors";
try {
// Any MIME operation
} catch (err) {
if (err instanceof MimeError) {
console.error("MIME error:", err.message, "code:", err.code);
}
}
```
## Usage: Advanced Example
```typescript
import { MimeError } from "@suaveplan/mime/errors";
import { InvalidContentTypeError, UnknownMimeTypeError } from "@suaveplan/mime/errors";
// Route by specific subtype
function handleMimeError(err: MimeError) {
if (err instanceof InvalidContentTypeError) {
return Response.json({ error: "bad_content_type" }, { status: 400 });
}
if (err instanceof UnknownMimeTypeError) {
return Response.json({ error: "unsupported_type" }, { status: 415 });
}
throw err; // Unexpected subtype — re-throw
}
```
## API Reference
### `class MimeError extends SuaveplanError`
The base class. All `@suaveplan/mime` error classes extend this.
## Implementation Notes
`MimeError` exists primarily to provide a stable `instanceof` target for callers who want to catch any MIME-related error without caring about the specific subtype. All subclasses set their own `code` from `MIME_ERROR_CODES` and override `name` with their class name. The inheritance chain is: `SuaveplanError → MimeError → {subtype}`.
### Subclass Index
Direct subclasses are `InvalidContentTypeError`, `UnknownMimeTypeError`, `UnknownSignatureError`, `InsufficientBytesError`, and `ParseError`. Every subclass sets its own `code` from the stable `MIME_ERROR_CODES` registry, overrides `name`, and inherits the wire-serialisation, OTel span-event adapter, and i18n resolver behaviour from `SuaveplanError`. Tests typically assert `err instanceof MimeError` first for broad catches, then narrow with `instanceof` on the specific subclass.

View File

@@ -0,0 +1,69 @@
# errors/parse-error
## Purpose
Thrown when a general parsing operation fails — for example, XML declaration parsing or BOM detection failures. Unlike `InvalidContentTypeError`, which is specific to RFC 7231 Content-Type headers, `ParseError` is the catch-all for any other structured-text parsing failure within the `@suaveplan/mime` package.
## Features
- Error code `MIME_ERROR_CODES.PARSE_ERROR`
- `name` set to `"ParseError"`
- Optional context fields for the offending input
- Full `SuaveplanError` serialization
## Usage: Basic Example
```typescript
import { ParseError } from "@suaveplan/mime/errors";
function parseXmlEncoding(text: string): string {
const match = /encoding=['"]([^'"]+)/.exec(text);
if (!match) {
throw new ParseError({
message: "No encoding declaration found in XML prolog",
context: { raw: text.slice(0, 100) },
});
}
return match[1];
}
```
## Usage: Advanced Example
```typescript
import { MimeError, ParseError } from "@suaveplan/mime/errors";
// Catch any MIME parse error generically
try {
processDocument(buffer);
} catch (err) {
if (err instanceof ParseError) {
console.error("Document parse failed:", err.message);
return { error: "parse_failed", details: err.context };
}
if (err instanceof MimeError) {
// Other MIME errors
}
throw err;
}
```
## API Reference
### `class ParseError extends MimeError`
**Constructor options:**
| Field | Type | Description |
|-------|------|-------------|
| `message` | `string` | Human-readable error description |
| `context?` | `object` | Arbitrary structured context for debugging |
| `cause?` | `unknown` | Underlying cause |
## Implementation Notes
`ParseError` is intentionally generic so that future parsing features (XML namespace detection, HTTP range header parsing, etc.) can reuse it without creating a proliferation of narrow error classes. If a specific context shape becomes widely used, a dedicated subclass can be introduced with a more precise `context` type.
### Callers
Thrown by lower-level parser stages when a structurally-recognised format cannot be fully parsed — for example a truncated multipart boundary, an invalid base64 parameter value, or a charset declaration that doesn't round-trip through `TextDecoder`. Distinct from `InvalidContentTypeError`, which is reserved for the RFC 7231 grammar checker. The error captures both the `stage` that failed and the raw `input` for log replay.

View File

@@ -0,0 +1,63 @@
# errors/unknown-mime-type-error
## Purpose
Thrown when a MIME type string is not found in the built-in database and no fallback is available. Carries the unrecognized MIME type string in `context.mime` so callers can log, redirect, or prompt users to provide additional information.
## Features
- Error code `MIME_ERROR_CODES.UNKNOWN_MIME_TYPE`
- Optional `context.mime` field with the unrecognized MIME type string
- `name` set to `"UnknownMimeTypeError"`
- Full `SuaveplanError` serialization
## Usage: Basic Example
```typescript
import { getEntryByMime } from "@suaveplan/mime/db/registry";
import { UnknownMimeTypeError } from "@suaveplan/mime/errors";
function requireMimeEntry(mime: string) {
const entry = getEntryByMime(mime);
if (!entry) {
throw new UnknownMimeTypeError({
context: { mime },
});
}
return entry;
}
```
## Usage: Advanced Example
```typescript
import { UnknownMimeTypeError } from "@suaveplan/mime/errors";
// Auto-generated message includes the MIME type
const err = new UnknownMimeTypeError({ context: { mime: "application/x-exotic" } });
console.log(err.message); // "Unknown MIME type: application/x-exotic"
// Without context — uses "unknown" fallback in message
const err2 = new UnknownMimeTypeError({ message: "MIME type not recognized" });
console.log(err2.context); // undefined
```
## API Reference
### `class UnknownMimeTypeError extends MimeError`
**Constructor options:**
| Field | Type | Description |
|-------|------|-------------|
| `message?` | `string` | Human-readable message (auto-generated from context.mime if omitted) |
| `context?.mime` | `string` | The unrecognized MIME type string |
| `cause?` | `unknown` | Underlying cause |
## Implementation Notes
The error message is auto-generated as `"Unknown MIME type: {mime}"` when `context.mime` is provided and no explicit `message` is supplied. This convention reduces boilerplate for the common case of constructing the error from a lookup failure.
### Callers
Thrown by the strict variants of registry lookups (`mimeTypeOrThrow`, `mimeCategoryOrThrow`) when the supplied MIME string has no entry in the curated IANA database. Non-strict variants return `undefined` instead. The error carries the requested MIME string so callers can log, suggest the nearest registered type, or fall back to a default. Usually handled at an API boundary with HTTP 415.

View File

@@ -0,0 +1,67 @@
# errors/unknown-signature-error
## Purpose
Thrown when a byte signature identifier is referenced but is not present in the signature registry. Used as a sentinel for development-time validation when code expects a specific signature to be registered (for example, in plugin systems that verify their registration succeeded).
## Features
- Error code `MIME_ERROR_CODES.UNKNOWN_SIGNATURE`
- `context.signature` — the expected signature identifier or MIME type
- `name` set to `"UnknownSignatureError"`
- Full `SuaveplanError` serialization
## Usage: Basic Example
```typescript
import { UnknownSignatureError } from "@suaveplan/mime/errors";
import { getSignatures } from "@suaveplan/mime/detection/from-magic-bytes/registry";
function requireSignature(mime: string): void {
const found = getSignatures().some(s => s.mime === mime);
if (!found) {
throw new UnknownSignatureError({
context: { signature: mime },
});
}
}
```
## Usage: Advanced Example
```typescript
import { UnknownSignatureError } from "@suaveplan/mime/errors";
// In a plugin registration system
function verifySignaturePlugin(pluginMime: string): void {
const isRegistered = getSignatures().some(s => s.mime === pluginMime);
if (!isRegistered) {
throw new UnknownSignatureError({
message: `Plugin signature for "${pluginMime}" was not registered`,
context: { signature: pluginMime },
});
}
}
```
## API Reference
### `class UnknownSignatureError extends MimeError`
**Constructor options:**
| Field | Type | Description |
|-------|------|-------------|
| `message?` | `string` | Human-readable error description |
| `context?.signature` | `string` | The expected signature identifier |
| `cause?` | `unknown` | Underlying cause |
## Implementation Notes
This error is not thrown by any built-in `@suaveplan/mime` API — it is provided for consumers who build plugin-style extension systems on top of the signature registry and want explicit registration verification. The `detectFromMagicBytes` function returns `undefined` for unrecognized bytes rather than throwing, keeping the detection path exception-free.
### Callers
Thrown by strict magic-byte detection (`detectFromMagicBytesOrThrow`) when the supplied bytes match no registered `Signature`. Non-strict `detectFromMagicBytes` returns `undefined`. The error carries the first few bytes as a hex string in `prefix` so logs capture enough context to diagnose missing signatures without dumping the full payload.
The `prefix` field is capped at the first 16 bytes of the input so log aggregators never see enormous hex blobs even when the caller passed a multi-megabyte buffer. That cap keeps logs readable and stops a surprising memory amplification path at the telemetry layer.

View File

@@ -0,0 +1,65 @@
# legacy/get-mime-type
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides a simplified, backwards-compatible `getMimeType` function that accepts a filename or extension and returns the MIME type string, or a configurable default when no match is found. Designed for codebases migrating from `mime` or `mime-types` npm packages that used a similar single-function API.
## Features
- Simple `getMimeType(filename, fallback?)` interface
- Returns the MIME type string directly (not wrapped in a result object)
- Configurable fallback (default: `"application/octet-stream"`)
- Strips path components and handles extensions with or without dots
- Zero dependencies beyond the built-in registry
## Usage: Basic Example
```typescript
import { getMimeType } from "@suaveplan/mime/legacy/get-mime-type";
console.log(getMimeType("document.pdf")); // "application/pdf"
console.log(getMimeType(".ts")); // "video/mp2t" (TypeScript)
console.log(getMimeType("unknown.xyz")); // "application/octet-stream"
console.log(getMimeType("unknown.xyz", "text/plain")); // "text/plain"
```
## Usage: Advanced Example
```typescript
import { getMimeType } from "@suaveplan/mime/legacy/get-mime-type";
// Migrate from the 'mime' npm package
// Before: mime.getType("photo.jpg") → "image/jpeg"
// After: getMimeType("photo.jpg") → "image/jpeg"
function serveFile(filePath: string, buffer: Uint8Array): Response {
const mimeType = getMimeType(filePath, "application/octet-stream");
return new Response(buffer, {
headers: { "Content-Type": mimeType },
});
}
```
## API Reference
### `getMimeType(filename: string, fallback?: string): string`
| Parameter | Type | Description |
|-----------|------|-------------|
| `filename` | `string` | Filename, path, or extension (with or without dot). |
| `fallback?` | `string` | Returned when no match found (default: `"application/octet-stream"`). |
**Returns:** MIME type string.
**Throws:** Never.
## Implementation Notes
`getMimeType` is a thin convenience wrapper over `detectFromExtension`. It is intentionally limited to extension-based detection — for production code requiring high-confidence detection (including magic-byte analysis), use the `detect()` function from `@suaveplan/mime/detection/detect` instead. This function is in the `legacy/` directory to signal that new code should prefer the more explicit multi-source API.
### Deprecation Plan
`getMimeType` is retained for callers migrating from the pre-`@suaveplan/mime` helpers; new code should call `mimeFromExtension` / `detectFromMagicBytes` directly. A future major release may remove this wrapper — it currently forwards to the real extension-to-MIME map and returns `undefined` when nothing matches, which is the exact contract the old helper advertised.

View File

@@ -0,0 +1,70 @@
# reverse/reverse
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Provides `getMimeExtension` for reverse lookup: given a MIME type string, returns the canonical file extension associated with that type. This is the inverse of extension-based detection — useful for generating filenames, setting `Content-Disposition` headers, and file download naming.
## Features
- Returns the canonical (first) extension for any registered MIME type
- Returns `undefined` for unknown MIME types (no throws)
- Case-insensitive MIME type matching (delegates to registry)
- Returns extensions without a leading dot
- O(1) lookup via the registry's forward map
## Usage: Basic Example
```typescript
import { getMimeExtension } from "@suaveplan/mime/reverse/reverse";
console.log(getMimeExtension("image/png")); // "png"
console.log(getMimeExtension("application/pdf")); // "pdf"
console.log(getMimeExtension("text/javascript")); // "js"
console.log(getMimeExtension("unknown/type")); // undefined
```
## Usage: Advanced Example
```typescript
import { getMimeExtension } from "@suaveplan/mime/reverse/reverse";
function buildFilename(baseName: string, mimeType: string): string {
const ext = getMimeExtension(mimeType);
if (!ext) return baseName; // Unknown type — keep original name
// Avoid double extension
return baseName.endsWith(`.${ext}`) ? baseName : `${baseName}.${ext}`;
}
// Content-Disposition header generation
function contentDisposition(mimeType: string, name: string): string {
const ext = getMimeExtension(mimeType);
const filename = ext ? `${name}.${ext}` : name;
return `attachment; filename="${filename}"`;
}
console.log(contentDisposition("application/pdf", "report"));
// 'attachment; filename="report.pdf"'
```
## API Reference
### `getMimeExtension(mime: string): string | undefined`
| Parameter | Type | Description |
|-----------|------|-------------|
| `mime` | `string` | MIME type string (e.g., `"image/png"`). |
**Returns:** The canonical file extension string without a leading dot, or `undefined` if the MIME type is not in the database.
**Throws:** Never.
## Implementation Notes
The "canonical" extension is the first element of the `extensions` array in the `MimeEntry`. The built-in database places the most common extension first by convention (e.g., `"jpeg"` before `"jpg"` for `image/jpeg`). Callers that need all extensions for a type should use `getEntryByMime` directly from `@suaveplan/mime/db/registry` and access `entry.extensions`.
### Lookup Tables
The reverse index maps MIME string → preferred extension, MIME string → alias list, and alias → canonical MIME. Tables are precomputed at module load from the same curated IANA database the forward lookup uses, so the two directions can never drift. Lookups are O(1) against frozen `Map` instances with zero runtime cost after first import.

View File

@@ -0,0 +1,61 @@
# telemetry/meters
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
Initializes OpenTelemetry metrics instruments for `@suaveplan/mime`. Provides counters and histograms for tracking MIME detection operations, cache hits, and processing durations. Callers supply their own `Meter` instance so this module has no direct dependency on any specific OTel SDK implementation.
## Features
- Lazy initialization — instruments are created once on first `initMeters` call
- Idempotent — subsequent `initMeters` calls after the first are no-ops
- Counters: detection calls by source, detection failures, charset sniff operations
- Histograms: detection latency, stream read bytes
- No OTel SDK dependency — accepts any `Meter`-compatible interface
- Zero-cost no-op path when telemetry is not initialized
## Usage: Basic Example
```typescript
import { initMeters } from "@suaveplan/mime/telemetry/meters";
import { metrics } from "@opentelemetry/api";
// Call once at application startup
const meter = metrics.getMeter("@suaveplan/mime");
initMeters(meter);
// All subsequent MIME operations will record metrics automatically
```
## Usage: Advanced Example
```typescript
import { initMeters, getMimeMeters } from "@suaveplan/mime/telemetry/meters";
import { metrics } from "@opentelemetry/api";
// Initialize with a named meter for better grouping in dashboards
initMeters(metrics.getMeter("@suaveplan/mime", "1.0.0"));
// Access instruments directly for custom recording
const meters = getMimeMeters();
meters?.detectionCounter.add(1, {
source: "magic-bytes",
mime: "image/png",
});
```
## API Reference
### `initMeters(meter: Meter): void`
Initializes the metric instruments using the provided OTel `Meter`. Idempotent — only the first call has effect.
### `getMimeMeters(): MimeMeters | undefined`
Returns the initialized meters object, or `undefined` if `initMeters` has not been called.
## Implementation Notes
The idempotency guard uses a module-level `let meters: MimeMeters | undefined` variable checked at the top of `initMeters`. This avoids re-creating instruments if `initMeters` is accidentally called multiple times (e.g., from multiple entry points in a large application). The no-op fallback (`getMimeMeters()` returning `undefined`) means callers can use optional chaining without any conditional initialization checks: `getMimeMeters()?.detectionCounter.add(1, attrs)`.