From d29222bb48359e12dd23bc8384760bab80c4d15c Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 22 Jul 2026 18:10:30 +0000 Subject: [PATCH] Release 3.6.1: document the 3.6.0 API in README and add house rules The README predated the 3.6.0 API. Add a "Bytes, non-throwing decode, and constant-time compare" section covering the `toBytes` byte encoders, the non-throwing `tryFrom` decoders, `constantTimeEquals`, and the exported converter base classes; note the case-insensitive Crockford decoding; refresh the install snippet; and bump `pubspec.yaml` + CHANGELOG to 3.6.1 (docs-only, no library change). Also add two AGENTS.md conventions: always update `README.md` in the same commit as a public-API change, and treat 100% line coverage and 100% dartdoc as hard gates on every commit. --- AGENTS.md | 13 +++++++++++++ CHANGELOG.md | 6 ++++++ README.md | 24 ++++++++++++++++++++++-- pubspec.yaml | 2 +- 4 files changed, 42 insertions(+), 3 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 057a66a..0ce4bd8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -163,6 +163,19 @@ release. After a release, bump the dependency in `~/projects/hashlib` - _(new)_ When a change touches public API surface, update `example/` and `test_integration/main.dart` in the same commit if the new API belongs in the showcase — the integration job is the smoke test of the public surface. +- _(new)_ **Always update `README.md` in the same commit as any public-API + change** — every new top-level `to`/`from`/`try` function, codec, alphabet + variant, or exported class gets a line in the relevant README section (the + "Supported codecs" table, an alphabet list, or the conveniences section), and + the install snippet's version tracks the release. README is a shipped artifact + scored by pana; a stale README is a release defect, not a follow-up. +- _(new)_ **100% line coverage and 100% dartdoc are hard gates, not goals.** + Every commit keeps `lib/` at 100% line coverage (`bash scripts/coverage.sh`) + and every public symbol documented. New code ships in the same commit as the + tests that exercise all of its branches, and every public class, member, + constant, and top-level function carries dartdoc. Dartdoc is enforced by the + `public_member_api_docs` lint and pana; there is no coverage gate in CI, so + the coverage bar is on you — run the script before you commit. ## Failure modes — named, with the rule that prevents each diff --git a/CHANGELOG.md b/CHANGELOG.md index feec9cc..82a1695 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,9 @@ +# 3.6.1 + +- Update `README.md` for the 3.6.0 API: the `toBytes` byte encoders, the + non-throwing `tryFrom` decoders, `constantTimeEquals`, the exported + converter base classes, and the case-insensitive Crockford decoding. + # 3.6.0 - [**Breaking Changes**] diff --git a/README.md b/README.md index 0e6a165..f375aaf 100644 --- a/README.md +++ b/README.md @@ -36,12 +36,16 @@ text. It carries no runtime dependencies of its own. raise typed errors on genuinely invalid input. - **Convenient output**: results integrate with `ByteCollector`, the digest container shared with `hashlib`, for one-call re-encoding. +- **Bytes or strings, throwing or not**: every encoder has a `toBytes` + twin returning a `Uint8List`, every decoder a non-throwing `tryFrom` + returning `null` on bad input, plus a top-level `constantTimeEquals` for + MAC/digest checks. ## Install ```yaml dependencies: - convertlib: ^3.5.2 + convertlib: ^3.6.1 ``` or run `dart pub add convertlib`. A single import exposes every codec: @@ -112,7 +116,8 @@ Every snippet in this README is also a runnable program in the - `Base32Codec.standard` (RFC-4648) — `ABCDEFGHIJKLMNOPQRSTUVWXYZ234567` (default) - `Base32Codec.lowercase` — `abcdefghijklmnopqrstuvwxyz234567` - `Base32Codec.hex` / `.hexLower` — base32hex, `0-9A-V` / `0-9a-v` -- `Base32Codec.crockford` — `0123456789ABCDEFGHJKMNPQRSTVWXYZ` +- `Base32Codec.crockford` — `0123456789ABCDEFGHJKMNPQRSTVWXYZ` (decoding is + case-insensitive and accepts `I`/`i`/`L`/`l` as `1` and `O`/`o` as `0`) - `Base32Codec.geohash` — `0123456789bcdefghjkmnpqrstuvwxyz` - `Base32Codec.z` — z-base-32, `ybndrfg8ejkmcpqxot1uwisza345h769` - `Base32Codec.wordSafe` — `23456789CFGHJMPQRVWXcfghjmpqrvwx` @@ -129,6 +134,21 @@ to drop `=`, or a `codec:`: - `BigIntCodec.msbFirst` — treats bytes in big-endian order - `BigIntCodec.lsbFirst` — treats bytes in little-endian order (default) +### Bytes, non-throwing decode, and constant-time compare + +- **Byte output** — every string encoder has a `toBytes` twin that returns + the encoded ASCII output as a `Uint8List`, skipping the intermediate `String`: + `toHexBytes`, `toBinaryBytes`, `toOctalBytes`, `toBase32Bytes`, `toBase64Bytes`. +- **Non-throwing decoders** — every decoder has a `tryFrom` that returns + `null` instead of throwing a `FormatException` on invalid input: `tryFromHex`, + `tryFromBinary`, `tryFromOctal`, `tryFromBase32`, `tryFromBase64`, + `tryFromUtf8`, `tryFromBigInt`. +- **Constant-time compare** — `constantTimeEquals(a, b)` compares two byte lists + without exiting early on the first mismatch, for verifying MACs and digests. +- **Low-level building blocks** — the generic converters `BitEncoder`/ + `BitDecoder`, `ByteEncoder`/`ByteDecoder`, and `AlphabetEncoder`/ + `AlphabetDecoder` are exported for building custom codecs. + ### ByteCollector `ByteCollector` is the byte container shared with `hashlib`, holding the output diff --git a/pubspec.yaml b/pubspec.yaml index b7462de..d7a9df3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -2,7 +2,7 @@ name: convertlib description: Fast and error resilient codecs. Supports Base2, Base8, Base16, Base32, Base64, BigInt, UTF-8, and the PHC/crypt string format. homepage: https://github.com/bitanon/convertlib -version: 3.6.0 +version: 3.6.1 topics: - codec