# How the Convert Project Defines File Formats and Their Capabilities

> Discover how the convert project defines file formats and capabilities using FormatDefinition objects and FileFormat instances. Enables handlers to declaratively specify read and write abilities.

- Repository: [p2r3/convert](https://github.com/p2r3/convert)
- Tags: architecture
- Published: 2026-02-19

---

**The convert project defines file formats and their capabilities using immutable `FormatDefinition` objects for static metadata and `FileFormat` instances for runtime conversion flags, enabling handlers to declaratively specify exactly what they can read and write.**

The `p2r3/convert` repository implements a type-safe, extensible architecture for multimedia transformation. At its foundation, the system defines file formats and their capabilities through a strict separation between unchanging format properties and dynamic handler abilities. This design allows the conversion engine to build transformation graphs without hardcoding format logic into individual handlers.

## Core Architecture: Definitions and Capabilities

The system splits format handling into two distinct layers located in [`src/FormatHandler.ts`](https://github.com/p2r3/convert/blob/main/src/FormatHandler.ts).

**`IFormatDefinition`** stores immutable metadata for a file type: its full name, short label, file extension, MIME type, and category. This data remains constant across all handlers.

**`FileFormat`** extends these properties with runtime capabilities. According to the source code in [`src/FormatHandler.ts`](https://github.com/p2r3/convert/blob/main/src/FormatHandler.ts) lines 5-24, each `FileFormat` object tracks three critical boolean flags:

- **`from`** – the handler can use this format as conversion input
- **`to`** – the handler can produce this format as output
- **`lossless`** – optional flag indicating whether the transformation preserves original quality

The **`FormatDefinition`** class implements `IFormatDefinition` and serves as a factory for creating `FileFormat` instances. It exposes two distinct APIs for binding capabilities: the concise `supported()` method and the flexible `builder()` pattern.

## Pre-Defined Formats and Categories

Common file types are catalogued in [`src/CommonFormats.ts`](https://github.com/p2r3/convert/blob/main/src/CommonFormats.ts) alongside a **`Category`** classification system. Categories group formats logically—such as `Category.IMAGE`, `Category.AUDIO`, or `Category.VIDEO`—enabling UI filtering and organizational features.

Each entry in `CommonFormats` is a `FormatDefinition` instance containing no capability data until bound to a handler. For example, the PNG definition in [`src/CommonFormats.ts`](https://github.com/p2r3/convert/blob/main/src/CommonFormats.ts) lines 21-27 appears as:

```typescript
export const PNG = new FormatDefinition(
  "Portable Network Graphics",
  "png",
  "png",
  "image/png",
  Category.IMAGE
);

```

## Declaring Conversion Capabilities

Handlers register their supported transformations using the `supported()` method defined in [`src/FormatHandler.ts`](https://github.com/p2r3/convert/blob/main/src/FormatHandler.ts) lines 64-73. This method accepts a handler identifier, a `from` boolean, and a `to` boolean, returning a configured `FileFormat` instance.

```typescript
// Handler that reads PNG but writes JPEG
supportedFormats = [
  PNG.supported("png_reader", true, false),   // from: true, to: false
  JPEG.supported("jpeg_encoder", false, true) // from: false, to: true
];

```

An optional fourth parameter enables the **`lossless`** flag. The conversion engine—specifically [`src/TraversionGraph.ts`](https://github.com/p2r3/convert/blob/main/src/TraversionGraph.ts)—uses these `from` and `to` flags to construct a directed graph of possible transformations, automatically routing files through intermediate formats when direct conversion is unavailable.

## Custom Formats with the Builder API

For specialized handlers requiring modified metadata—such as variant MIME types or custom quality profiles—the `builder()` API provides a fluent interface. Implemented in [`src/FormatHandler.ts`](https://github.com/p2r3/convert/blob/main/src/FormatHandler.ts) lines 77-135, this pattern allows incremental construction of `FileFormat` objects.

```typescript
const customWebP = CommonFormats.WEBP.builder("webp_encoder")
  .allowFrom(false)
  .allowTo(true)
  .markLossless()
  .override({ mime: "image/webp;type=optimized" })
  .build();

```

**Key builder methods include:**

- **`allowFrom(boolean)`** – sets input capability
- **`allowTo(boolean)`** – sets output capability
- **`markLossless()`** – enables the lossless quality flag
- **`override(partial)`** – tweaks immutable properties like MIME type or display name
- **`build()`** – finalizes and returns the `FileFormat` instance

## How Handlers Register Support

Every conversion handler implements the **`FormatHandler`** interface exported from [`src/FormatHandler.ts`](https://github.com/p2r3/convert/blob/main/src/FormatHandler.ts). This contract requires a `supportedFormats` array containing `FileFormat` objects that declare the handler's specific abilities.

```typescript
export const ffmpegHandler: FormatHandler = {
  name: "FFmpeg",
  supportedFormats: [
    CommonFormats.MP4.supported("ffmpeg_mp4", true, true),
    CommonFormats.MP3.supported("ffmpeg_mp3", true, true)
  ],
  // init, doConvert implementations...
};

```

Because `FormatDefinition` instances are pure data holders with no side effects, the system remains deterministic and unit-testable. The conversion engine aggregates `supportedFormats` arrays across all registered handlers to determine the complete topology of available transformations.

## Summary

- **Immutable metadata** lives in `FormatDefinition` objects ([`src/FormatHandler.ts`](https://github.com/p2r3/convert/blob/main/src/FormatHandler.ts)), storing names, extensions, MIME types, and categories independently of handlers
- **Runtime capabilities** are captured in `FileFormat` instances through `from`, `to`, and `lossless` boolean flags
- **Common formats** are pre-defined in [`src/CommonFormats.ts`](https://github.com/p2r3/convert/blob/main/src/CommonFormats.ts) with standardized categories for logical grouping
- **Handlers declare support** via the `supported()` method for standard cases or the `builder()` API for customized variants
- **The conversion graph** leverages these capability declarations to route files through valid transformation chains automatically

## Frequently Asked Questions

### What is the difference between FormatDefinition and FileFormat?

`FormatDefinition` stores static metadata about a file format—its name, extension, MIME type, and category—without any conversion logic. `FileFormat` extends this data with runtime capabilities specific to a handler, indicating whether that handler can read (`from`) or write (`to`) the format and whether the conversion is `lossless`.

### How do I add support for a custom file format in convert?

Create a new `FormatDefinition` in [`src/CommonFormats.ts`](https://github.com/p2r3/convert/blob/main/src/CommonFormats.ts) with the appropriate metadata and category, then register it in your handler using `supported(handlerName, canRead, canWrite)` or the `builder()` API if you need custom MIME types or quality flags. Finally, add the resulting `FileFormat` to your handler's `supportedFormats` array.

### What does the lossless flag control in file format capabilities?

The `lossless` boolean flag, set via `markLossless()` in the builder or the fourth parameter of `supported()`, indicates whether conversions using this format preserve the exact source data without quality degradation. This metadata helps the conversion engine prioritize high-fidelity transformation paths when available.

### Where are format categories defined and used?

Categories are defined as constants in [`src/CommonFormats.ts`](https://github.com/p2r3/convert/blob/main/src/CommonFormats.ts) lines 3-13 and assigned to `FormatDefinition` instances during construction. They provide logical groupings—such as image, audio, video, or document—that the UI layer uses for filtering and organizing available conversion options.