Skip to main content

CHANGELOG.md

# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

## [0.1.1] - 2026-06-27

### Changed

- Dependency updates: `rustler` 0.37 → 0.38 (Elixir + crate), `zip` 7 → 8,
  `quick-xml` 0.39 → 0.40, plus `uuid`, `credo`, and `ex_doc` bumps. The
  quick-xml `Attribute::unescape_value` API was deprecated; migrated to
  `normalized_value(XmlVersion::Implicit1_0)`, which is behavior-identical
  (same XML 1.0 implicit version, depth, and predefined-entity resolver). No
  user-facing API change.
- Reader no longer depends on the `iepub` crate. All structural data
  (manifest, spine, metadata, cover, TOC) now comes from a pure
  `zip` + `quick_xml` stack. This removes a large transitive dependency
  surface and lets us tolerate non-OCF-conformant `mimetype` files
  (trailing `\n`, `\r\n`, space, or a leading UTF-8 BOM) — common in
  Calibre exports and other real-world EPUBs that other readers accept
  silently. Genuine non-EPUB content is still rejected with a new
  `:invalid_mimetype` error kind.

### Added

- `LangelicEpub.Error` may now have `kind: :invalid_mimetype` when the
  `mimetype` zip entry is missing or its content (after trimming a
  UTF-8 BOM and whitespace) is not `application/epub+zip`.
- OTP 29 support: `rustler` 0.38 builds against OTP 29's NIF interface, and CI
  now tests on OTP 29 / Elixir 1.20 in addition to OTP 26/27. The precompiled
  NIF 2.16 artifact forward-loads on OTP 29's newer NIF ABI, so no new artifact
  is shipped (a 2.17 artifact would needlessly drop OTP 26 compatibility).

### Fixed

- Dublin Core metadata is now matched by its namespace URI rather than a
  hard-coded `dc:` prefix (the OPF is parsed with quick-xml's `NsReader`), so
  `dc:`-equivalent elements bound to a non-standard prefix or a default
  namespace are recovered instead of silently dropped. Parsing the OPF also
  now rejects elements that declare more than 256 namespace bindings, bounding
  a malformed or hostile package.

## [0.1.0] - 2026-04-20

### Added

- `LangelicEpub.parse/1` — parse EPUB 2 and EPUB 3 bytes into a
  `%LangelicEpub.Document{}` with spine, assets, table of contents, and
  metadata (including fields like `<dc:language>`, `<dc:rights>`, and
  multiple `<dc:creator>` entries that the underlying iepub crate does not
  expose natively).
- `LangelicEpub.build/1` — emit EPUB 3 bytes from a
  `%LangelicEpub.Document{}`, with a backward-compatible `toc.ncx`
  alongside the EPUB 3 `nav.xhtml` so EPUB 2-only readers still navigate
  correctly.
- Validation of required fields (`title`, `identifier`, `language`) and
  spine/asset ID uniqueness at build time. UTF-8 is enforced on chapter
  `data` to prevent silent corruption in downstream readers.
- Rust panics inside the NIF are caught (`std::panic::catch_unwind`) and
  returned as `{:error, %LangelicEpub.Error{kind: :panic, ...}}` so a
  malformed input cannot crash the BEAM scheduler.
- Precompiled NIFs published via GitHub Releases for
  `aarch64-apple-darwin`, `x86_64-apple-darwin`,
  `aarch64-unknown-linux-gnu`, `x86_64-unknown-linux-gnu`, and
  `x86_64-unknown-linux-musl`.

[Unreleased]: https://github.com/xlabs-hq/langelic-epub/compare/v0.1.1...HEAD
[0.1.1]: https://github.com/xlabs-hq/langelic-epub/compare/v0.1.0...v0.1.1
[0.1.0]: https://github.com/xlabs-hq/langelic-epub/releases/tag/v0.1.0