# Changelog

All notable changes to this project are documented here.
Format follows [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
Versioning follows [Semantic Versioning](https://semver.org/).

---

## [0.9.0] — 2026-07-06

### Added
- **Binding Swift / iOS** (`swift/`, 5ᵉ binding). Swift Package local
  posé au-dessus du C ABI de `syllabify-fr-ffi` — pas de nouveau code
  Rust, juste un consumer Swift idiomatique + harness de build
  XCFramework.
  - API idiomatique : `SyllabifyFr.syllables(_) -> [String]`,
    `syllabifyText(_) -> [TextChunk]` (enum `.word/.raw`),
    `phonemes(_) -> [Phoneme]`, `renderHtml`, `renderWordHtml`,
    `highlightLetters(_:preset:mode:)` (enums `HighlightPreset` /
    `RenderMode`). Gestion mémoire C transparente (`withCString` +
    `defer { syllabify_free }`).
  - `scripts/build-xcframework.sh` produit un XCFramework 5 slices :
    iOS device (arm64), iOS simulator (arm64 + x86_64 lipo), et macOS
    universel (dev-only, pour `swift test` sur l'hôte). Le produit
    reste iOS-only.
  - Validé de bout en bout : `swift test` (host macOS) + `xcodebuild
    test` (simulateur iOS), 8 XCTest verts.
  - Distribution **locale** pour l'instant (`.package(path: …)`), pas
    de publication CocoaPods / SwiftPM registry.
- `ffi/include/syllabify_fr.h` complété avec `syllabify_highlight_letters`
  (manquait depuis 0.8.1) ; `ffi/cbindgen.toml` aligné.

### Changed
- **CI reproductible** : `Cargo.lock` désormais committé (le workspace
  est un hybride lib + binaire CLI + 4 cdylibs, et on avait été mordu
  par la non-reproductibilité). `--locked` ajouté aux jobs test /
  regression / release. Le job MSRV garde son `rm -f Cargo.lock`
  (teste volontairement le MSRV contre les deps les plus récentes).
- **Alerte précoce** : nouveau workflow `deps-canary.yml` (cron hebdo +
  manuel, non-bloquant) — `cargo update && cargo test` pour capter une
  rupture upstream sans polluer la CI des PR.
- **Dependabot** activé : `github-actions` (groupé mensuel) + `cargo`
  (minor/patch groupés, majors laissés à l'audit manuel).
- Bumps via dependabot : `actions/checkout` 4→7, `codecov-action` 4→7,
  `setup-node` 4→6, `clap_complete` 4.6.5→4.6.6.

### Notes
- **Aucun changement de l'API publique de la crate core** ni des
  bindings publiés (wasm/py/jni) depuis 0.8.5 — le binding Swift est le
  seul ajout fonctionnel, et il est local. Le core republié sur
  crates.io est identique à 0.8.5.
- `CLAUDE.md` : section « Parité bindings » passe à 5 bindings (ajout
  swift).
- Oracle 4830 mots : toujours à 100%.

---

## [0.8.5] — 2026-06-20

### Changed
- **Bumps deps majeurs** (toutes les majors disponibles prises en une
  passe, audit deps clos) :
  - `criterion` 0.5 → 0.8 (dev-dep, benches uniquement). Migration
    `criterion::black_box` → `std::hint::black_box` (déprécié 0.6+).
  - `jni` 0.21 → 0.22 (binding Android). Refactor vers la nouvelle
    API `EnvUnowned` + closure `with_env`, avec politique d'erreur
    custom `SilentDefault` qui **préserve le contrat null-sur-erreur**
    pré-0.22 (pas de RuntimeException jetée côté Java). Migration
    `env.get_string(...).into()` → `JString::try_to_string(env)?`,
    `find_class("...")` → `find_class(jni_str!("..."))` (compile-time
    JNI string).
  - `pyo3` 0.23 → 0.29 (binding Python). Aucun changement de code
    applicatif nécessaire — le binding utilisait déjà les patterns
    modernes Bound API depuis le départ.

### Notes
- Aucun changement d'API publique observable (le contrat de chaque
  binding est préservé). `cargo-semver-checks` gate validé sur les
  3 PRs.
- L'app Android consommatrice (binding JNI) bénéficie maintenant
  des améliorations de sécurité 0.22 (séparation `EnvUnowned`/`Env`).
- Cargo méthodes deprecated `find_class` / `new_object_array` /
  `set_object_array_element` en jni 0.22 sont conservées avec
  `#[allow(deprecated)]` localement — leur migration vers
  `JObjectArray::<T>::new` / `set_element` reste à programmer dans
  une PR isolée (refactor type-generic non-trivial).

---

## [0.8.4] — 2026-06-15

### Fixed
- **Sortie JSON RFC 8259-conforme dans CLI / FFI / JNI** : les
  trois bindings exposant du JSON (`syllabify --json`,
  `syllabify_text_json` / `syllabify_phonemes` côté FFI,
  `SyllabifyFr.syllabifyText` / `phonemes` côté JNI) utilisaient
  chacun un échappement hand-rolled incomplet (couvrait au mieux
  `" \ \n \r \t`, au pire seulement `" \`). Conséquence : tout
  caractère de contrôle U+0000..U+001F en entrée (via `TextChunk::Raw`)
  ressortait brut → JSON syntaxiquement invalide rejeté par
  `serde_json::from_str`, `JSON.parse`, `json.loads`.
  Migration vers `serde_json` comme backend unique d'échappement.
  RFC 8259-conforme par construction.
- 3 nouveaux tests de régression (un par surface) qui pinnent la
  non-régression sur 6 caractères de contrôle adversariaux + parse
  round-trip via `serde_json::Value`.

### Changed
- `serde_json` ajouté en dépendance optionnelle sous feature `cli`
  (auparavant en dev-dependency seulement), et en dépendance prod
  des crates `syllabify-fr-ffi` et `syllabify-fr-jni`. Coût binaire
  estimé : ~30-50 KB sur les `cdylib`, négligeable pour des bindings
  Rust standards.
- ~150 lignes d'helpers JSON maison supprimées sur les 3 surfaces.

### Notes
- L'échappement HTML hand-rolled (5 entités : `& < > " '`) reste
  inchangé : ergonomiquement complet pour les contextes HTML5 texte
  et attribut, pas de bug observé.
- Oracle 4830 mots : toujours à 100%.

---

## [0.8.3] — 2026-05-10

### Performance
- Pipeline parser/decoder/homographs : élimine les allocations
  `String` pour les codes phonémiques qui proviennent de données
  statiques (`data.rs`). Détail :
  - `parser::Phoneme.code` : `String` → `Cow<'static, str>`. Tous
    les `rule.phoneme.to_string()` deviennent `Cow::Borrowed`. Hot
    path du parser : zéro alloc côté code.
  - `decoder::DecodedPhoneme.code` : idem. Les post-traitements qui
    réécrivent (`x^`, `o_ouvert`, `j`) construisent maintenant un
    `Cow::Borrowed` sur littéral, donc également sans alloc.
  - `homographs::lookup() -> Option<Vec<(&'static str, &'static str)>>`
    (était `Option<Vec<(String, String)>>`). Les pairs codes/lettres
    viennent directement de `HOMOGRAPHES` ; plus de `to_string()`
    inutile.
  - L'API publique `phonemes() -> Vec<(String, String)>` reste
    inchangée — la conversion en `String` n'a lieu qu'à la frontière
    publique. Aucun breaking pour les consommateurs.

### Notes
- Compatible avec `Phoneme`/`DecodedPhoneme` : ces types sont
  `pub(crate)` (lib.rs:23-29), donc le changement de type du champ
  `code` n'est pas observable hors-crate.
- Oracle 4830 mots : toujours à 100%.

---

## [0.8.2] — 2026-05-07

### Fixed
- **npm publish** : le `README.md` était bien copié dans `wasm/pkg/`
  par `wasm-pack` mais pas listé dans le champ `files` du
  `package.json` généré. Conséquence : le registre npm stockait un
  `readme` vide au niveau de la version, et npmjs.org affichait « no
  README » sur toutes les versions du package
  `@dyscolor/syllabify-fr-wasm` depuis v0.5.0.
  Fix : nouveau step dans `release.yml` qui patche
  `wasm/pkg/package.json` pour ajouter `"README.md"` à `files` avant
  `npm publish`. Aucun changement de code ou d'API ; pure release CI.
- **wasm/README.md** : le lien relatif `[syllabify-fr](../README.md)`
  donnait un 404 sur npmjs.org (qui résout les liens relatifs dans son
  propre domaine). Remplacé par l'URL GitHub absolue. Idem pour la
  référence au workflow CI (corrigée au passage : `release.yml`, pas
  `publish-npm.yml`).

---

## [0.8.1] — 2026-05-07

### Added
- **Bindings** : la fonction `letters` arrivée en 0.8.0 est désormais
  exposée dans les 4 bindings (oubli en 0.8.0). Une seule fonction par
  binding, qui prend un préset nommé (`"bdpq"`, `"mnu"`, `"pir-pri"`)
  et un mode optionnel (`"inline"` par défaut, `"classes"`) :
  - **WASM** : `highlightLetters(word, preset, mode?)` → `string`
  - **FFI C** : `syllabify_highlight_letters(word, preset, mode)` → `*mut c_char`
    (mode peut être NULL ; libérer avec `syllabify_free`)
  - **Python** : `highlight_letters(word, preset, mode="inline")` → `str`
    (lève `ValueError` sur preset/mode inconnu)
  - **JNI** : `SyllabifyFr.highlightLetters(word, preset, mode)` → `String`

### Notes
- Aucun changement d'API côté lib `syllabify-fr` (déjà exposée en 0.8.0).
- Pour les utilisateurs avec règles `LetterRule` custom, l'API reste
  accessible en Rust uniquement ; un binding « power-user » sera ajouté
  si la demande remonte.

---

## [0.8.0] — 2026-05-06

### Added
- **Module `letters`** : mise en évidence de lettres ou successions de
  lettres (`b`, `pir`, …) via couleurs, gras, contour, classes CSS.
  Port fidèle de la fonction `lettres` de LireCouleur 6
  (`functionlc6.js:222-296`) — destinée notamment aux confusions de
  lettres pour lecteurs dyslexiques (b/d/p/q, m/n/u, séquences `pir`/`pri`).
  - API publique : `match_letters`, `render_letters_html`,
    `LetterRule`, `LetterStyle`, `LetterSpan`, `RenderMode`
    (`Inline` par défaut, `Classes` en option).
  - Sous-module `letters::presets` : `bdpq()`, `mnu()`, `pir_pri()`.
  - 13 tests unitaires couvrant scan, folding diacritique, priorité
    règles, escaping HTML, multi-byte UTF-8.
- CLI : nouveau flag `--highlight-letters <PRESET>` (valeurs : `bdpq`,
  `mnu`, `pir-pri`) qui produit du HTML avec styles inline. 3 tests
  d'intégration ajoutés.

### Notes
- Les groupes de confusion ne sont pas hard-codés dans LC6 — les presets
  Rust sont du sucre pour la découvrabilité, le moteur reste générique
  (l'utilisateur peut passer ses propres `Vec<LetterRule>`).
- Aucun changement dans le pipeline syllabique : oracle 4830 mots
  toujours à 100%.

---

## [0.7.1] — 2026-05-03

### Performance
- `parser::lookup_letter` : remplace le scan linéaire sur les 41 entrées
  d'`AUTOMATON` par un index `OnceLock<HashMap<char, &'static LetterEntry>>`
  construit une fois au premier appel (lecture concurrente lock-free ensuite).
  Gain : 41 string-compares → 1 hash lookup par lettre. Comportement
  strictement identique (oracle 4830 mots toujours à 100%). (#19)

### Documentation
- `CLAUDE.md` : retire la section « Technical debt » périmée — le cache regex
  utilise déjà `OnceLock` et `AssembleMode::Lc` est déjà `#[deprecated]`.

---

## [0.7.0] — 2026-05-01

### Added
- **CLI : migration vers `clap`** — parser robuste, `--help` et `--version`
  auto-générés, validation arguments, messages d'erreur clairs. Aucun breaking
  sur la surface CLI existante (`syllabify <mot>`, `--text`, `--json`,
  `syllabify -` pour stdin).
- `--novice-reader` : expose l'option `novice_reader` de la lib (désactive
  post-traitements subtils — yod, o ouvert/fermé).
- `--oral` : expose `SyllableMode::Oral` (q caduc final fusionné, ex. `école`
  → `é-cole` au lieu de `é-co-le`).
- `--completions <SHELL>` : génère les completions shell sur stdout
  (`bash`, `zsh`, `fish`, `powershell`, `elvish`). Documentation install dans
  README.
- Tests d'intégration CLI (`tests/cli.rs`) via `assert_cmd` — 14 tests
  couvrant tous les modes et flags.
- Property tests `proptest` sur invariants stables (non-panique sur entrées
  adversariales UTF-8/emojis/mots géants, conservation des lettres). (#16)
- Workspace `fuzz/` (cargo-fuzz) avec 4 cibles + smoke-fuzz 60 s/cible en CI
  sur push main. (#16)
- Coverage CI via `cargo-llvm-cov` + Codecov (non bloquant). (#16)
- Audit supply-chain `cargo-deny` (advisories CVE, licences GPL-compat, bans
  wildcards, sources crates.io). Job CI `deny`. (#17)

### Changed
- **MSRV** bumpée `1.74 → 1.85` (requise par `clap` 4.6.x, adopté pour rester
  à l'état de l'art de l'écosystème CLI Rust 2026). Le job CI `MSRV` est mis
  à jour en conséquence.
- `Cargo.toml` : nouvelle feature `cli` (regroupe `regex-full` + `clap` +
  `clap_complete`), incluse dans `default`. Lib users avec
  `default-features = false` ne compilent pas `clap`.

### Notes
- Aucun breaking change pour les utilisateurs de la lib (API publique
  inchangée).
- Le binaire `syllabify` reste 100% retro-compatible sur sa surface
  pré-existante.

---

## [0.6.2] — 2026-04-30

### Fixed
- CI : `publish-npm` du workflow `release.yml` échouait sur le tag `v0.6.0`
  car npm 10.x (livré avec Node 22) ne supporte pas l'OIDC trusted publishing
  (requiert npm ≥ 11.5.1). Le job tentait `npm install -g npm@latest` mais
  plantait sur le bug d'auto-upgrade (`Cannot find module 'promise-retry'`).
  Une première tentative via `corepack prepare --activate` n'a pas non plus
  rendu npm 11 prioritaire dans le PATH. Le fix retenu : bumper le runner à
  **Node 24** (qui ship npm 11.x nativement) et ajouter une vérification
  défensive de la version d'npm avant le `publish`. Aucune modification
  fonctionnelle ; même API que 0.6.0.

---

## [0.6.0] — 2026-04-30

### Added
- `[lints]` table dans `Cargo.toml` : `forbid(unsafe_code)`, et `warn` clippy
  sur `dbg_macro`, `todo`, `unimplemented` (appliqué à tous les targets).
- `#[must_use]` sur les fonctions publiques pures :
  `syllables`, `syllables_with`, `phonemes`, `syllabify_text`,
  `liaison_amont`, `liaison_aval`, `liaison_possible`,
  `render_word_html`, `render_html`.
- Documentation des champs publics de `Phoneme` et `DecodedPhoneme`.

### Changed
- **Breaking** : `TextChunk`, `AssembleMode`, `SyllableMode` sont désormais
  `#[non_exhaustive]`. Les consommateurs qui matchent dessus doivent ajouter
  un bras `_ => …` pour rester forward-compatibles.
- **MSRV** bumpée `1.70 → 1.74` (requise par la table `[lints]`). Le job CI
  MSRV est mis à jour en conséquence.

---

## [0.5.0] — 2026-04-25

### Added
- `jni/` : nouvelle crate `syllabify-fr-jni 0.5.0` — bindings JNI pour Java, Kotlin et Android.
  - 5 fonctions exportées : `syllables` (→ `String[]`), `syllabifyText`, `phonemes`,
    `renderWordHtml`, `renderHtml` (→ JSON/HTML `String`)
  - Classe Java `com.dyscolor.syllabify.SyllabifyFr` prête à l'emploi
  - README avec instructions Android (cross-compilation par ABI)
- Roadmap : Java/JNI coché.

### Changed
- Version workspace `0.4.1 → 0.5.0` (nouvelle crate publiée).

---

## [0.4.1] — 2026-04-25

### Added
- `tests/edge_cases.rs` : 9 tests sur les entrées atypiques (chaîne vide, chiffres,
  majuscules, apostrophe, trait d'union, ponctuation seule).
- README : badges crates.io, docs.rs et CI GitHub Actions.
- Roadmap : FFI C et Python cochés ; nom de crate corrigé (`syllabify-fr-ffi`).

---

## [0.4.0] — 2026-04-19

### Added
- `src/html.rs`: `render_word_html(word)` and `render_html(text)` — HTML output
  with `<span class="syl syl-a/b">` alternating syllable spans, word wrapping,
  and `<span class="liaison">` inter-word liaison markers.
- WASM: `renderHtml` / `renderWordHtml` exported via `wasm-bindgen`.
- HTML character escaping for `<`, `>`, `&`, `"`, `'`.

### Changed
- Core bumped `0.3.0 → 0.4.0`; WASM `0.1.0 → 0.2.0`.

---

## [0.3.0] — 2026-04-19

### Added
- `src/liaisons.rs`: `liaison_amont`, `liaison_aval`, `liaison_possible` — port of
  `liaisonAmont` / `liaisonAval` from LireCouleur 6 (`module.js` ll. 1041-1057).
- `LIAISONS_AVAL` word list (40 entries) embedded in `src/data.rs` via `generate_data.py`.
- `h` aspiré vs muet correctly handled: `hôtel` allows liaison, `héros`/`haricot` block.

### Changed
- Core bumped `0.2.0 → 0.3.0`.

---

## [0.2.0] — 2026-04-18

### Added
- `wasm/` crate (`syllabify-fr-wasm 0.1.0`): WebAssembly bindings via `wasm-bindgen`.
  - Exports `syllables(word)`, `syllabifyText(text)`, `phonemes(word)`.
  - Target: ESM (`wasm-pack build --target web`); gzip budget < 300 KB.
- Feature-gate `regex-full` (default) / `regex-lite` for WASM size.
- Workspace with two members: root + `wasm/`.
- `ffi/` crate (`syllabify-fr-ffi 0.1.0`): C FFI bindings (`cdylib` + `staticlib`).
  - Exports `syllabify_syllables`, `syllabify_text_json`, `syllabify_phonemes`,
    `syllabify_render_word_html`, `syllabify_render_html`, `syllabify_free`.
  - Pre-generated C header `ffi/include/syllabify_fr.h`.
- `py/` crate (`syllabify-fr-py 0.1.0`): Python bindings via PyO3 0.23.
  - Exports `syllables`, `syllabify_text`, `phonemes`, `render_html`, `render_word_html`.
  - Build with maturin; test suite: 11 pytest tests.

### Changed
- Core bumped `0.1.0 → 0.2.0`.

### Fixed
- Regex cache: replaced `OnceLock<Mutex<HashMap<String, Regex>>>` with
  `OnceLock<HashMap<&'static str, Regex>>` — all patterns from `AUTOMATON`
  compiled once at first parse, lock-free reads thereafter.
- `AssembleMode::Lc` marked `#[deprecated(since = "0.4.0")]`: non-conformant
  with LireCouleur 6 v6; prefer `AssembleMode::Std`.

---

## [0.1.0] — 2026-04-18

### Added
- Initial Rust port of LireCouleur 6 (`module.js`), 100% conformant on 4830-word oracle.
- Five-stage pipeline: `cleaner → parser → decoder post-processing → homographs → assembly`.
- `syllables(word)`, `syllables_with(word, …)`, `phonemes(word)`, `syllabify_text(text)`.
- 19 unit tests + 4830-word regression oracle.
- `AUTOMATON`: 43 letters × ~480 rules; 15 word lists; 16 homograph entries.
- CLI binary `syllabify` (requires `regex-full` feature).
- NOTES-v6.md documenting 10 v5→v6 algorithm changes and 4 porting pitfalls.
