Catalog PyPI
PACKAGE EXPLORERView in registry

adflux

PyPI

Pure-Python Markdown ↔ Atlassian Document Format (ADF) converter with lossless round-tripping

adfatlassianatlassian-document-formatcommonmarkconfluenceconverterdocument-conversiongfmjiramarkdown
Versions collected1
Direct dependencies7From release 0.3.1
Last observed20 Oct, 19:28 UTC

README

Version 0.3.1 documentation

adflux

CI Release PyPI Python License: MIT

A pure-Python converter between Markdown (CommonMark + GFM) and Atlassian Document Format (ADF) — the JSON document model used by Confluence and Jira. Round-trips losslessly, including ADF-only constructs like panels, status badges, mentions, task lists, expand sections, and macros.

flowchart LR
  MD[Markdown] -->|markdown-it-py| IR((Internal IR + adf-* envelopes))
  ADF[ADF JSON] -->|mapping.yaml| IR
  PF[Panflute JSON] -->|pf.load| IR
  IR -->|options filter| W{writer}
  W -->|CommonMark serializer| MD2[Markdown]
  W -->|mapping.yaml| ADF2[ADF JSON]
  W -->|pf.dump| PF2[Panflute JSON]

Why adflux

ADF is JSON, but no mature pure-Python tool converts it to and from Markdown without losing information. adflux fills that gap:

  • No system dependencies. pip install adflux — no Pandoc binary, no Node, no Ruby.
  • Lossless ADF round-trips. A declarative mapping.yaml plus a small envelope convention round-trips every ADF construct, including ones the library has never seen.
  • Idiomatic Markdown output. Panels become GitHub alert blockquotes, expand sections become <details>, smart cards become autolinks. The output looks correct in GitHub, VS Code, and any standard viewer.
  • Conversion options make lossy / lossless behavior explicit and selectable per call.

Install

pip install adflux

Requires Python ≥ 3.11. Linux, macOS, and Windows are CI-tested.

Quick start

from adflux import convert

# Markdown → ADF JSON
adf = convert(open("README.md").read(), src="md", dst="adf")

# ADF JSON → Markdown, dropping ADF-only constructs to plain content
md = convert(adf, src="adf", dst="md", options={"envelopes": "drop"})
adflux convert --from md  --to adf README.md > readme.adf.json
adflux convert --from adf --to md page.json
adflux convert --from adf --to md --option envelopes=drop page.json
adflux validate page.adf.json
adflux list-options

Full API and CLI reference: docs/usage.md.

Conversion options

OptionChoicesDefaultDescription
envelopeskeep, drop, keep-strictkeepHow ADF envelope nodes are handled on lossy targets.
jira-stricttrue, falsefalseReject non-Jira ADF nodes during serialization.

Worked examples: docs/options.md.

Markdown rendering of ADF constructs

ADF constructRendered as
panel (info/note/warning/…)GitHub alert blockquote (> [!NOTE], > [!TIP], …)
expand<details><summary>title</summary>…</details>
inlineCard / blockCard / embedCardAutolink (<https://…>)
taskList / taskItemGFM task list (- [ ], - [x])
emoji, mentionPlain text (🚀, @alice)
Everything elseHTML-comment marker (<!--adf:status …-->)

The reader recognises every form on its way back, so MD → ADF → MD is stable.

Supported formats

FormatAliasesDescription
Markdownmd, markdownCommonMark + GFM via markdown-it-py
ADFadfAtlassian Document Format JSON
Panflutepanflute, pfPandoc JSON AST for integration with panflute tools

How it works

  • IR: an in-memory document tree built on panflute AST classes (used purely as Python data structures — no Pandoc binary involved), with Div / Span envelopes for ADF-only constructs.
  • Envelopes: class prefix adf-<nodeType> plus a base64-JSON blob for complex attributes. A universal adf-raw fallback guarantees zero data loss.
  • Mapping: every ADF node type is described in src/adflux/formats/adf/mapping.yaml. Adding a new node type is a YAML edit.

Deeper dives: docs/design.md · docs/architecture.md · docs/options.md · docs/fidelity-matrix.md · docs/extending.md · docs/e2e-testing.md.

Examples

Runnable scripts in examples/:

python examples/md_to_adf.py README.md
python examples/adf_to_markdown.py
python examples/confluence_roundtrip.py

Development

git clone https://github.com/mikejhill/adflux
cd adflux
uv sync --all-groups
uv run poe check

Common tasks via poethepoet:

poe test         # unit + integration + round-trip + property tests
poe test-e2e     # live Confluence + Jira round-trip suite (requires .env)
poe lint         # ruff format --check + ruff check
poe typecheck    # ty check src
poe cov          # pytest with coverage report
poe check        # lint + typecheck + test (run before opening a PR)
poe build        # sdist + wheel into ./dist

CI runs the full matrix on Linux, macOS, and Windows for Python 3.11–3.13.

Releasing

Releases are automated. Land changes on main using Conventional Commits, then run the Release workflow from the GitHub Actions tab. go-semantic-release computes the next version, builds the sdist + wheel, creates a tagged GitHub Release, and publishes to PyPI via OIDC trusted publishing.

Contributing

Issues and pull requests are welcome. Before opening a PR:

  1. poe check must pass (lint + typecheck + tests).
  2. Use a Conventional Commit message (feat:, fix:, docs:, …).
  3. New ADF node types need a fixture in tests/roundtrip/test_node_coverage.py.

License

MIT

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.3.118 Apr 20267

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

ACCESSIBLE
mikejhill/adflux

Shared repository data · GitHub

License detected in repository
MIT
License branch / ref
main
Last push
22 Aug 2026
Archived
No
Contributor identities
— · partial collection

Public sponsor counts have not been collected. These signals belong to the repository owner.

Downloads & popularity

0count
Gauge · observed snapshots22 Oct 2026
10.50
2026-10-22: 0 · observed 2026-10-22T16:35:45.8681459Z
22 Oct22 Oct

github · 1 observations shown. Missing days are unknown.

View observed values
Date / source periodValueObserved at
2026-10-2202026-10-22T16:35:45.8681459Z

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 19:28 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:178b3e20a0ef91679fe39591e9f170d5dbe3c6b5de096ee0ca24cc4e78572cbd