Skip to content

feat(sync): public mdfluence.sync library API - #87

Open
geopanther wants to merge 14 commits into
mainfrom
feat/sync-library-api
Open

feat(sync): public mdfluence.sync library API#87
geopanther wants to merge 14 commits into
mainfrom
feat/sync-library-api

Conversation

@geopanther

Copy link
Copy Markdown
Owner

Summary

Exposes a stable mdfluence.sync library surface so downstream tools (e.g. gitfluence) can embed mdfluence as a library instead of shelling out to the CLI. Orchestration helpers are moved out of __main__, decoupled from argparse and the TUI, and a public publish() entry point with extension hooks is added.

No ticket reference provided (personal repo).

What changed

  • PublishOptions dataclass — replaces the argparse Namespace dependency in orchestration helpers.
  • Reporter protocol + NullReporter — decouples the upload loop from Md2cfTUI; library callers get silent progress reporting by default.
  • publish(confluence, pages, options, reporter=None, prepare_pages=None, parent_resolver=None) — public entry point with one-time prepare_pages and per-page parent_resolver hooks; default_parent_resolver extracted.
  • Moved helpers pre_process_page, validate_relative_links, build_document_path_to_page_map, update_pages_with_relative_links from __main__ into mdfluence.sync.
  • RelativeLinkError — the library raises a typed error instead of calling sys.exit; the CLI catches it at its boundary.
  • Title prefix (Ask 1)apply_title_prefix now realizes the prefix during directory collection (title + parent_title + anchors); hosted in mdfluence.document to avoid an import cycle.
  • fix(upsert)upsert_page message typed Optional[str] to match runtime behaviour.
  • Public surface re-exported from mdfluence: MinimalConfluence, Page, get_pages_from_directory, publish, PublishOptions, Reporter, NullReporter, RelativeLinkError, apply_title_prefix.
  • CLI behaviour is unchanged; main() builds PublishOptions from args.

How to test

Written test-first (TDD). Run the suite:

uv run --group dev pytest
  • 209 tests pass locally.
  • ty type checker and ruff are clean.
  • New unit tests: test_package/unit/test_sync.py (PublishOptions, Reporter/NullReporter, moved helpers, publish() hooks, public surface); title-prefix tests in test_document.py.

Notes

  • Internal move of orchestration helpers out of __main__ — no CLI behaviour change.
  • publish() never calls sys.exit; callers decide how to handle RelativeLinkError/HTTPError.

Cover apply_title_prefix (set/None/empty, None parent_title), prefix

realization in get_pages_from_directory, and --title override

re-applying prefix. Tests precede implementation (TDD).
Add mdfluence.sync.apply_title_prefix(page, prefix) as an args-free
helper. get_pages_from_directory now realizes the title prefix on every
page (title + parent_title) at the end of collection, so collected pages
are upsert-ready.

Remove the duplicate prefix f-strings from pre_process_page and apply
the prefix once in collect_pages_to_upload for the stdin and single-file
paths, re-applying after a --title override.
Add plan.md describing the TDD roadmap for exposing the

mdfluence.sync library surface (apply_title_prefix, prefix

realization, PublishOptions, Reporter/NullReporter, publish() hooks)

for embedding mdfluence as a library.
Move the title-prefix helper out of the sync module so document code
no longer depends on sync, removing a cross-module import cycle risk.
The CLI already passes a possibly-None update message, so type the
upsert_page parameter as Optional[str] to match runtime behaviour.
Move pre_process_page, validate_relative_links,
build_document_path_to_page_map and update_pages_with_relative_links
from __main__ into mdfluence.sync, decoupled from argparse via a new
PublishOptions dataclass and from the TUI via a Reporter protocol with
a NullReporter default.

Add publish() with prepare_pages and parent_resolver hooks, extract
default_parent_resolver, and raise RelativeLinkError instead of exiting
so the library never calls sys.exit. Rewire main() onto PublishOptions
and catch RelativeLinkError at the CLI boundary.
Re-export MinimalConfluence, Page, get_pages_from_directory, publish,
PublishOptions, Reporter, NullReporter, RelativeLinkError and
apply_title_prefix so downstream code can embed mdfluence as a library.
main() duplicated the entire upload loop that publish() already
implements, risking divergence. Make the CLI a thin caller of publish()
via an on_page_upserted hook (emits page URL / JSON) and boundary error
handling.

Decouple the reporter from the work: publish() no longer wraps mutations
in a `with reporter:` scope. The reporter is a pure observer, mutations
are gated solely by PublishOptions, and the caller owns the reporter's
display lifecycle (the CLI runs `with tui: publish(...)`). Drop
__enter__/__exit__ from the Reporter protocol accordingly.

Add tests for the on_page_upserted hook, page-error propagation, and
that publish() treats the reporter as a pure observer.
Parent resolution moved to default_parent_resolver, so pre_process_page
no longer reads space_info. Remove the dead parameter and update the
tests that passed it.
apply_title_prefix is defined in mdfluence.document to avoid an import
cycle but advertised on the mdfluence.sync surface. Add a comment at the
import so the split home is not surprising to future readers.
plan.md was a TDD roadmap for building the sync library API. The work
is now implemented, so the plan is stale dev scaffolding rather than
shippable documentation. Remove it from the repo root.
block_code wrote PNG bytes to a temp dir inline, mixing the
markup-rendering concern with filesystem persistence. Move that
persistence into a new diagrams.write_diagram_png helper so the
renderer stays a pure markup transformer.

The temp dirs backing rendered diagrams were also never cleaned up;
they are now tracked and removed at process exit via an atexit hook,
after the upload step has read them.
Move the re, anchor and alerts imports out of heading() and
block_alert() up to module scope. There is no import cycle (anchor,
diagrams and plugins.alerts do not import the renderer), so the
function-local imports only obscured the module's dependencies.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant