# Changelog

## [0.2.0] — 2026-03-22

### Added

- **Write operations** — `save` (upsert), `create` (insert only), `delete` on `Cendry`/`AsyncCendry`
- **Partial updates** — `update` with field-level changes, dot-notation, Firestore transforms (`Increment`, `ArrayUnion`, `DELETE_FIELD`, `SERVER_TIMESTAMP`)
- **Refresh** — `refresh(instance)` re-fetches from Firestore and mutates in-place
- **Batch writes** — `save_many`, `delete_many` (max 500, atomic), `batch()` context manager with `Batch`/`AsyncBatch`
- **Transactions** — `transaction(fn)` callback with auto-retry, `transaction()` context manager (single attempt), `Txn`/`AsyncTxn` with read + write methods
- **`DocumentAlreadyExistsError`** — raised by `create` when document exists, preserves `__cause__`
- **Firestore re-exports** — `DELETE_FIELD`, `SERVER_TIMESTAMP`, `Increment`, `ArrayUnion`, `ArrayRemove`, `Maximum`, `Minimum`
- **`validate_required_fields`** — standalone function in `serialize.py`
- **Optimistic locking** — `get_metadata(instance)` returns `DocumentMetadata` with `update_time`/`create_time`, tracked via weakref; `if_unchanged=True` on `update`/`delete` passes Firestore `LastUpdateOption` precondition
- **`DocumentMetadata`** and **`get_metadata`** — document metadata tracking, auto-populated on reads and writes

### Changed

- **`type_registry` threading** — custom `TypeRegistry` on context now flows through all serialization, deserialization, query, and write paths
- **`to_dict`/`from_dict`/`deserialize`** — accept optional `registry` parameter
- **`Query`/`AsyncQuery`** — accept and propagate `registry` through all construction sites
- **`resolve_field_path`** — recurses into nested Map fields for dot-notation alias resolution
- **`serialize_update_value`** — accepts `hint` parameter for proper container serialization (e.g. `list[Money]`)
- **`_cursor_value`** — uses `to_dict` instead of `dataclasses.asdict` for proper type handler support
- **`WritesMixin`** — shared write methods extracted to `_writes.py`, used by `Batch`, `AsyncBatch`, `Txn`, `AsyncTxn`
- **`assert` → `CendryError`** — defensive guards safe under `-O` flag
- **`resolve_field_path` / `validate_required_fields`** — cached for O(1) lookups
- **`BATCH_LIMIT`** — extracted as named constant with `_check_batch_limit` helper
- **Migrated docs** from mkdocs-material to [Zensical](https://zensical.org/)

### Documentation

- **Firestore intro** — "What is Firestore?" explanation with links to official docs
- **SDK vs NDB vs Cendry** — 10 side-by-side use cases comparing all three approaches
- **Async tutorial** — full `async`/`await` tutorial with sync-vs-async comparison table
- **Mermaid architecture diagrams** — module graph, dependency graph, read/write/batch/transaction sequence diagrams
- **How-To guides** — split into single-doc operations + batch/transactions
- **Integration testing guide** — testcontainers setup, fixtures, CI configuration
- **Fire color palette** — custom CSS theme for docs

### Testing

- **523 unit tests** — 100% coverage on 1195 source lines
- **38 integration tests** — against real Firestore emulator via testcontainers
- **12 BDD integration scenarios** — CRUD, batch, and transaction feature files

## [0.1.0] — 2026-03-21

First release — read-only Firestore ODM for Python >= 3.13.

### Added

- **Models** — `Model`, `Map`, `Field[T]`, `field()` with `dataclass_transform` for full IDE support
- **Queries** — `Cendry` (sync) and `AsyncCendry` (async) contexts with `get`, `find`, `get_many`, `select`, `select_group`
- **Query object** — chainable `Query`/`AsyncQuery` with `filter`, `order_by`, `limit`, `first`, `one`, `exists`, `count`, `to_list`, `paginate`
- **Filters** — Python operators (`==`, `!=`, `>`, `<`, `&`, `|`), named methods, `FieldFilter` re-export, `And`/`Or` composition
- **Field aliases** — `field(alias="firestoreName")` for when Python and Firestore names differ
- **Enum support** — `Field[MyEnum]` with `field(enum_by="value"|"name")` auto-conversion
- **Type validation** — `TypeRegistry` validates `Field[T]` at class definition time
- **Type handlers** — `register_type(MyType, handler=..., serialize=..., deserialize=...)` for custom type conversion
- **Serialization** — `from_dict`, `to_dict` standalone functions with alias support
- **Context manager** — `with Cendry() as ctx:` / `async with AsyncCendry() as ctx:`
- **Copy-pasteable repr** — all filters, queries, and ordering objects produce valid Python repr
- **Subcollections** — `parent=` parameter for nested document queries
- **Collection groups** — `select_group` for cross-subcollection queries
