feat: add page version listing and purge commands#171
Conversation
Three new CLI commands and matching client methods: versions <pageId> list historical versions version-delete <pageId> <vN> [-y] delete a single non-current version versions-purge <pageId> [-y] delete every non-current version The list and delete calls fall back transparently to the /rest/experimental/ path when /rest/api/ returns 404 or 405, so the commands work on Confluence Server/Data Center (where the version endpoints aren't exposed under /rest/api/) as well as Cloud. The fallback is sticky for paginated listVersions so long histories don't double their round-trip count. versions-purge accepts --throttle <seconds> to space the version-delete calls inside one page.
- README: add Commands-table rows for `versions`, `version-delete`,
`versions-purge` in the same shape as adjacent entries.
- listVersions: drop the unused `expand=content.version` query param.
The mapping function only reads top-level fields, so the expand
was inflating responses without effect.
- bin/confluence.js: drop the use-case-specific second sentence from
the `versions-purge` block comment so it reads generically.
- tests: add three cases that were missing
- purgeNonCurrentVersions through both fallbacks (list 404 →
experimental, deletes 404 → experimental)
- listVersions returns [] cleanly when results array is empty
- listVersions propagates 404 when both modern and experimental
paths return 404 (page-not-found case)
pchuri
left a comment
There was a problem hiding this comment.
Thanks for the thoughtful PR — really appreciate the level of care here. The 12 new test cases, the path-strategy comment in listVersions, and the descending-order rationale in purgeNonCurrentVersions made this very easy to review.
A few things I particularly liked:
- Sticky fallback in
listVersions— flippinguseExperimentalonce and reusing it across paginated requests avoids doubling round-trips on long histories. Nice touch. - Descending-order delete with the renumber rationale — exactly the kind of subtlety that bites you in production; glad it's both implemented and documented inline.
- Partial-failure tolerance — collecting errors and continuing the loop (with a covering test) is the right behavior for a bulk-cleanup tool.
- Consistent
assertWritableusage on both mutating commands.
A few minor suggestions (none blocking — happy to land as-is and address as follow-ups):
- Double
listVersionscall inversions-purgeCLI —bin/confluence.jscallslistVersionsfor the early-exit checks, thenpurgeNonCurrentVersionscalls it again internally. For pages with long histories this doubles the listing cost. Could pass pre-fetched versions in via options, or move the early-exit logic into the library. --throttleinput validation —parseFloat('abc')returnsNaN, which silently disables throttling (NaN > 0is false). ANumber.isFinite()guard with a friendly error would catch typos.versionNumbervalidation in CLI — currently happens insidedeleteVersion, so runningversion-delete 123 abcshows the prompt asDelete vNaN of page 123?before the validation rejects. Validating in the CLI before the prompt is a small UX win.- README vs. CLI argument names — README documents
versions <pageId_or_url>but the CLI registersversions <pageId>(similar for the other two). The behavior is correct sinceextractPageIdaccepts both, but--helpoutput won't tell users URLs are accepted. Existingdelete <pageIdOrUrl>is the precedent. viaExperimentalin the public API — you flagged this in the PR description; agree it's a nice debugging signal but worth documenting as@unstableor similar so future removal isn't a breaking change for library consumers.- Hardcoded
limit: 200— other paginated methods (e.g.getSpaces) accept apageSizeoption viaparsePositiveInt. Worth aligning for consistency, though not urgent.
CI note
The failing security check is unrelated to this PR — it's the recent batch of axios advisories (validateStatus prototype pollution, etc.) tripping npm audit --audit-level moderate. The same job fails on main at the latest commit, and this PR doesn't touch package.json or package-lock.json. A separate axios bump PR is needed; please don't let it block this one.
Approving. Thanks again!
# [2.5.0](v2.4.0...v2.5.0) (2026-05-06) ### Bug Fixes * **deps:** bump axios to ~1.15.2 to address security advisories ([#174](#174)) ([0a1492b](0a1492b)), closes [GHSA-w9j2-pv#6h63](https://github.com/GHSA-w9j2-pv/issues/6h63) [#173](#173) * **walker:** preserve attributes on <ul>/<ol>/<li> in markdown→storage ([#170](#170)) ([b5c172a](b5c172a)), closes [#153](#153) ### Features * add page version listing and purge commands ([#171](#171)) ([2bd5c37](2bd5c37))
|
🎉 This PR is included in version 2.5.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Description
Three new commands and matching client methods for managing Confluence page version history:
versions <pageId>— list historical versions (read-only)version-delete <pageId> <versionNumber>— delete a single non-current versionversions-purge <pageId>— convenience: delete every non-current version, keeping only currentThe list and delete calls fall back transparently to the
/rest/experimental/endpoint on a 404 or 405 from the configured/rest/api/path. Confluence Server/Data Center exposes content versions only at the experimental path; Cloud accepts both. The fallback is sticky for paginatedlistVersionsso long histories don't double their round-trip count.versions-purgeaccepts--throttle <seconds>to space the version-delete calls inside one page, useful when the underlying instance has aggressive rate limits.Type of Change
Testing
12 new test cases cover:
listVersionsresultsarray (no infinite loop)Checklist
listVersions, descending-order rationale inpurgeNonCurrentVersions)Additional Context
viaExperimentalflag returned bydeleteVersionand yielded frompurgeNonCurrentVersions'sonProgresscallback is intended as a debugging signal so a user can see when their instance routed via the fallback path. Happy to drop or rename if you'd prefer it stay implementation-detail.--dry-runonversions-purgeonProgresscallback intopurgeNonCurrentVersionsitself so library consumers can pace without a custom callbackversionNumberfor friendlier error UX