v1.9.0
What's changed in v1.9.0
-
refactor: public API hygiene — non_exhaustive, re-export pruning, crate docs (#86) (by @patrickleet)
- refactor: public API hygiene — non_exhaustive, re-export pruning, crate docs
API-affecting cleanup that is free at 0.1.0 and expensive later (2026-06-10
review). Root re-export pruning and #[non_exhaustive] additions change the
public surface; reachability is preserved under module paths.-
#[non_exhaustive] sweep on the remaining growing types: ReadModelError,
HandlerError, ConsumerAckKind, KnativeIntegrationKind, TransportCapabilities,
and OutboxMessage. Added TransportCapabilities::new(..) so external transport
authors can still build a custom capability profile now that the struct
literal is blocked. OutboxMessage keeps its new()/create()/encode()/Default
constructors. NOTE: RepositoryError and LockError were intentionally NOT
re-touched — #[non_exhaustive] was already added to them in #83. -
Crate-level docs: #![doc = include_str!("../README.md")] so docs.rs renders
the README. README rust fences are taggedrust,ignore(the snippets are
deliberately abbreviated fragments referencing app-specific types, per the
README's own "Example Conventions" note); the Project Structure tree is now
text. cargo test --doc compiles clean. The real drift-catching fix is in
the in-source doctests: the stale microsvc examples that showed a SYNC
dispatch /fn handleare corrected to async (dispatch(..).await,
pub async fn handle, closures returning a future) in microsvc/mod.rs,
context.rs, and service.rs. -
Pruned crate-root re-exports: the low-level outbox row plumbing
(outbox_message_insert_plan, outbox_message_row_values) and the whole
table::* surface are no longer re-exported at the crate root. They stay
reachable under distributed::outbox::* and distributed::table::* (the outbox
and outbox_worker modules are nowpub). The documented quick-start API is
unchanged. Internal/test references were repointed to the module paths. -
Made BusTopologyConfig and the namespace/consumer-group validators
(validate_namespace, validate_consumer_group, resolve_consumer_group,
DEFAULT_BUS_NAMESPACE, MAX_TOPOLOGY_NAME_LEN) pub and re-exported frombus,
matching validate_stable_message_id. Third-party transports can now reuse the
portable naming rules, and this removes the dead-code warnings that appeared
under --no-default-features and partial feature combos. -
Resolved the dual outbox publisher story by documenting the boundary (no
parallel hierarchies introduced): AsyncMessagePublisher (BusPublisher over a
Bus, wired by service.with_bus) is THE production extension point; the sync
OutboxPublisher/OutboxWorker/LogPublisher trio is rustdoc'd as the dev/test
drain path. Folding/retiring the sync trait was deferred — it is exercised by
tests/todos and retiring it is out of scope for a hygiene PR.
Verified: cargo fmt --all --check; cargo build for default, --no-default-features,
postgres+nats, sqlite+rabbitmq+http (all zero warnings, no topology dead code);
cargo test --doc; cargo test and cargo test --features sqlite (all green).Implements [[tasks/public-api-docs-hygiene]]
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
- fix: keep TableSchemaRegistry re-exported at crate root
The root re-export pruning in this PR was too aggressive: it dropped
table::*(includingTableSchemaRegistry) and the outbox row-plumbing
fns from the crate root, butTableSchemaRegistryis consumed by the
crate's own integration tests viadistributed::TableSchemaRegistry
(tests/postgres_repository/main.rs) and is the entry point downstream
users build before bootstrapping table migrations. A symbol that the
integration tests import through the crate root is effectively public
API, so it must stay re-exported.The local verification used
--features sqlite, which does not compile
the postgres/all-features test crates, so the unresolved-import breaks
were missed in CI (coverage--all-featuresand postgres jobs).Re-add only
TableSchemaRegistryto the crate root. The remaining
table primitives stay reachable underdistributed::table::*(no
test/external root consumers), and the genuinely internal outbox row
plumbing (outbox_message_insert_plan,outbox_message_row_values)
stays pruned — confirmed zero consumers outsidesrc/outbox/.All test targets now compile, including the postgres and all-features
(librdkafka) coverage targets that previously failed.Refines [[tasks/public-api-docs-hygiene]]
Co-authored-by: Claude Fable 5 noreply@anthropic.com
-
refactor: deduplicate postgres/sqlite read-model layer into sqlx_repo (#89) (by @patrickleet)
- refactor: hoist pure read-model helpers into sqlx_repo (phase 1)
Move the 17 byte-identical, sqlx-free read-model/validation functions
(plus the sharedIncludeSpec) out of postgres_repo and sqlite_repo into
a newsqlx_repo::read_modelsubmodule and import them from both backends.These functions (schema resolution, write-plan validation, row-version
arithmetic, key/patch reconciliation, identifier quoting, etc.) carried no
database types, so each fix previously had to land twice and could drift.Net ~283 lines removed (two ~316-line copies deleted, one ~336-line shared
copy added). No public API change; no behavior change — every function was
verified byte-identical before the move.- refactor: make the relational write path generic over sqlx::Database (phase 2)
The upsert/patch/delete/insert/update/row_version
*_in_txfunctions were
byte-identical between postgres_repo and sqlite_repo modulo thePostgres/
Sqlitetype parameter —QueryBuilder<DB>already renders each dialect's
placeholder style. Hoist them intosqlx_repo::read_modelas free functions
overDB: SqlxReadModelBackend.Only the genuinely dialect-specific bits stay per-backend, behind the one
SqlxReadModelBackendtrait (two impls, one line per method): value binding
(Bool/typed-NULL— Postgres binds nativebool+::jsonb/::timestamptz
casts; SQLite stores bools asi64and collapses integer/bool NULLs), the
rows_affectedaccessor (sqlx exposes it only as an inherent method per
backend), and the backend/storage label strings. The unavoidable sqlx
executor/encode/decode bounds are stated explicitly on each write function —
no abstraction layer, no wrapper types.Net ~263 lines removed (two ~470-line copies replaced by one generic copy plus
two small trait impls). No public API change; behavior verified identical on
real Postgres and SQLite.- refactor: make the relational load path generic over sqlx::Database (phase 3)
The relational read path (load_relational_row_by_key, the relationship/
has_many/belongs_to loaders, the SELECT builder, ORDER BY, and row→Versioned
mapping) was mirrored between postgres_repo and sqlite_repo. Hoist it into
sqlx_repo::read_modelas free functions overDB: SqlxReadModelBackend.The two genuinely dialect-specific reads stay per-backend as trait methods:
row_value(Postgres has a native BOOLEAN; SQLite stores booleans as INTEGER
and decodesvalue != 0) andpush_select_column(Postgres casts JSON/
Timestamp to::texton SELECT so they decode as String; SQLite stores them as
text already). The fetch/decode sqlx bounds are stated explicitly on each load
function, mirroring the write path — no abstraction layer.Net ~126 lines removed. No public API change; behavior verified identical on
real Postgres and SQLite.Implements [[tasks/sqlx-read-model-dedup]]
-
feat: DRY code (#91) (by @patrickleet)
-
refactor: reduce duplicated source code
-
refactor: make outbox APIs async-only
-
docs: align async-only API references
-
fix: address outbox review comments
-
-
feat: Add SQLite bus transport (#90) (by @patrickleet)
-
feat: add sqlite bus transport
-
fix: run cli integration on prs
-
fix: address sqlite bus review hardening
Implements [[tasks/sqlite-bus-transport-impl]]
- docs: include sqlite bus in transport status
Implements [[tasks/sqlite-bus-transport-impl]]
-
See full diff: v1.8.2...v1.9.0