diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c70ce85 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,51 @@ +# Changelog + +Notable changes per release. Versions follow [semantic versioning](https://semver.org/), +with the caveat that 0.x means the API may still move between minor versions. + +## [0.1.2] — 2026-07-25 + +### Changed + +- `convex::paginated_query` now splits an oversized page instead of resetting + pagination. A page's range is pinned by its query journal, so inserts into + that range make it grow; previously a page that outgrew the server's read + limits (`pageStatus: "SplitRequired"`) reset the whole session, dropping every + loaded page and snapping the list back to the first one. It is now split in + two at the server's `splitCursor`, with both halves bounded by explicit + `cursor`/`endCursor` and swapped in only once both have loaded, so loaded + pages stay put. Pages are also split on `SplitRecommended`, or once a page + grows past twice `initial_num_items`. This matches convex-js's + `usePaginatedQuery`. +- A page the server reports as `SplitRequired` may be missing part of its range, + so `paginated_snapshot::results` now stops before it and the status returns to + `loading_more` (or `loading_first_page`) until a split repairs it. Previously + such a page was published, which could leave a gap in the middle of the list. + Also matches `usePaginatedQuery`. +- An incomplete page the server gives no `splitCursor` for is left alone rather + than reset. Its subscription stays live, so a later result that fits the read + limits repairs it. A query too heavy to ever return a complete page leaves the + list loading, with no error — as in convex-js, which has no recovery path for + that case either. + +No public API change: `paginated_query`, `paginated_snapshot`, and +`pagination_status` are unchanged. + +## [0.1.1] — 2026-07-14 + +### Added + +- FetchContent consumption snippet in the README. + +## [0.1.0] — 2026-07-14 + +First release. Realtime sync client over the Convex WebSocket protocol (live +query subscriptions, ordered mutations with read-your-writes, actions, auth, +reconnection with jittered backoff), the `convex::paginated_query` helper, +`convex::http_client` for one-shot calls, file storage upload/download, and full +Convex value fidelity including Int64, Bytes, and non-finite floats. The +protocol core is sans-IO; a bundled IXWebSocket transport is optional. + +[0.1.2]: https://github.com/Potionify/convex-cpp/compare/v0.1.1...v0.1.2 +[0.1.1]: https://github.com/Potionify/convex-cpp/compare/v0.1.0...v0.1.1 +[0.1.0]: https://github.com/Potionify/convex-cpp/releases/tag/v0.1.0 diff --git a/README.md b/README.md index e5354b8..b735b8d 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ include(FetchContent) FetchContent_Declare( convex GIT_REPOSITORY https://github.com/Potionify/convex-cpp.git - GIT_TAG v0.1.0 + GIT_TAG v0.1.2 ) set(CONVEX_WITH_IXWEBSOCKET ON) # bundled desktop transport; omit if you supply your own FetchContent_MakeAvailable(convex) diff --git a/scripts/bump_version.cmake b/scripts/bump_version.cmake index e890792..dad6766 100644 --- a/scripts/bump_version.cmake +++ b/scripts/bump_version.cmake @@ -1,5 +1,6 @@ -# Bumps the library version (include/convex/version.h), commits, and creates -# the matching annotated tag. Run from anywhere inside the repo: +# Bumps the library version (include/convex/version.h and the README's +# FetchContent pin), commits, and creates the matching annotated tag. Run from +# anywhere inside the repo: # # cmake -DBUMP=patch -P scripts/bump_version.cmake # 0.1.0 -> 0.1.1 # cmake -DBUMP=minor -P scripts/bump_version.cmake # 0.1.0 -> 0.2.0 @@ -10,9 +11,18 @@ # Or use the wrappers (no quoting needed): scripts/bump_version.bat patch # on Windows, scripts/bump_version.sh patch on Linux/macOS. # -# Then publish (this triggers the release workflow): +# Publishing. The release workflow triggers on the tag, and git does not push +# tags by default — neither does a plain `git push` nor the Push button in a +# GUI client. A tag left behind that way looks like a release that silently +# never happened. So this script sets push.followTags in the repo's local +# config, which makes every subsequent push carry annotated tags reachable +# from the commits it sends. Publish with either: # -# git push origin main --tags +# cmake -DBUMP=patch -DPUSH=ON -P scripts/bump_version.cmake # bump + push +# git push origin main # after a bump +# +# Both send the tag. `git push origin main --tags` still works and is what to +# use if an earlier tag was stranded locally. if(NOT DEFINED BUMP) message(FATAL_ERROR "Pass -DBUMP=major|minor|patch|x.y.z (the -D must come before -P)") @@ -62,7 +72,24 @@ foreach(part MAJOR MINOR PATCH) endforeach() file(WRITE "${version_h}" "${content}") -execute_process(COMMAND git -C "${repo_root}" add include/convex/version.h +# The README's FetchContent snippet pins a tag. Left to a human it goes stale +# silently, and a stale pin hands every new user an old library. +set(readme "${repo_root}/README.md") +file(READ "${readme}" readme_content) +if(NOT readme_content MATCHES "GIT_TAG v[0-9]+\\.[0-9]+\\.[0-9]+") + message(FATAL_ERROR "No 'GIT_TAG vX.Y.Z' pin found in ${readme}; update it by hand " + "or fix this script's pattern") +endif() +string(REGEX REPLACE "GIT_TAG v[0-9]+\\.[0-9]+\\.[0-9]+" "GIT_TAG v${new_version}" + readme_content "${readme_content}") +file(WRITE "${readme}" "${readme_content}") + +# Make an ordinary push carry the tag. Without this the release workflow waits +# on a tag that never arrives, and the failure is silent. +execute_process(COMMAND git -C "${repo_root}" config --local push.followTags true + COMMAND_ERROR_IS_FATAL ANY) + +execute_process(COMMAND git -C "${repo_root}" add include/convex/version.h README.md COMMAND_ERROR_IS_FATAL ANY) execute_process(COMMAND git -C "${repo_root}" commit -m "Release v${new_version}" COMMAND_ERROR_IS_FATAL ANY) @@ -70,4 +97,11 @@ execute_process(COMMAND git -C "${repo_root}" tag -a "v${new_version}" -m "v${ne COMMAND_ERROR_IS_FATAL ANY) message(STATUS "${old_version} -> ${new_version}, committed and tagged v${new_version}") -message(STATUS "Publish with: git push origin main --tags") + +if(PUSH) + execute_process(COMMAND git -C "${repo_root}" push origin HEAD "v${new_version}" + COMMAND_ERROR_IS_FATAL ANY) + message(STATUS "Pushed the release commit and tag v${new_version}") +else() + message(STATUS "Publish with: git push origin main (push.followTags carries the tag)") +endif() diff --git a/src/paginated.cpp b/src/paginated.cpp index 661a528..b23d676 100644 --- a/src/paginated.cpp +++ b/src/paginated.cpp @@ -234,7 +234,7 @@ struct paginated_impl : std::enable_shared_from_this { s.first.key = ++next_key; // The first half starts where the original page started — NOT at the // beginning of the query. convex-js sent a null cursor here, which - // duplicated every item before the page (get-convex/convex-js#54981). + // duplicated every item before the page (convex-js commit 7ceee3e). s.first.cursor = orig->cursor; s.first.end_cursor = split_cursor; s.second.key = ++next_key; diff --git a/tests/paginated_test.cpp b/tests/paginated_test.cpp index d1a751d..5a5b788 100644 --- a/tests/paginated_test.cpp +++ b/tests/paginated_test.cpp @@ -472,7 +472,7 @@ TEST(Paginated, IncompletePageHidesItselfButNotThePagesBeforeIt) { } TEST(Paginated, SplitFirstHalfStartsAtTheOriginalPagesCursor) { - // Regression guard for get-convex/convex-js#54981: convex-js sent a null + // Regression guard for convex-js commit 7ceee3e: convex-js sent a null // cursor for the first half, so splitting any page but the first re-read // the list from the beginning and duplicated everything before it. harness h;