Public Export API (v1)
Public Export API (v1)
Section titled “Public Export API (v1)”The API freeze (spec 009 T4.2). Five packages froze at 1.0.0:
@atlcli/confluence, @atlcli/docx, @atlcli/pdf, @atlcli/pdf-compiler-browser, and
@atlcli/export-macros. Everything on this page is the stable v1 surface — changes follow
the breaking-change policy in Package Versioning. Every stable
entrypoint’s full symbol list and transitive type closure is committed and CI-guarded at
packages/<p>/etc/<p>.api.md (surface snapshot) and packages/<p>/etc/<p>.closure.md
(closure classification incl. the per-package freeze decision).
In this page
Section titled “In this page”- Stability classes
- Document model: ExportBlock & storageToBlocks
- ADF media discovery and targeted attachments
- Attachment delivery: PageAttachmentWriterV1
- Tree export: TreeSource, fetchExportTree, composeChapters
- DOCX engine: ExportEnv & runExport
- PDF engine: PdfExportEnv & runPdfExport
- PDF compiler port: PdfCompilePort & BrowserPdfCompiler
- Macro rendering: MacroRendererRegistry
- Unstable and internal surfaces
- Related topics
Stability classes
Section titled “Stability classes”| Class | Meaning |
|---|---|
| stable | Frozen v1. Breaking changes = major version, with one @deprecated minor first. Includes the canonical @atlcli/docx/browser-entry. |
| experimental | Exported and usable, but may change in minor releases (0.x packages, ./browser-runtime, ./vite). |
| internal | Non-frozen implementation subpaths (./internal, ./scan, ./fixtures, ./template). May change without notice. |
Document model: ExportBlock & storageToBlocks
Section titled “Document model: ExportBlock & storageToBlocks”@atlcli/confluence — the shared block tree both engines consume.
storageToBlocks(storage, options?)→{ blocks: ExportBlock[], notes: ExportNote[] }— Confluence storage XML → typed block tree.ExportBlock/InlineNode/LinkTarget/TableRow/Caption/MacroParameter— the document model.ExportNotewithcode: ExportNoteCode— every note code is a member of the frozenEXPORT_NOTE_CODESregistry; renaming or removing one is a breaking change.htmlToExportBlocks— the export_view HTML fallback walker.
ADF media discovery and targeted attachments
Section titled “ADF media discovery and targeted attachments”@atlcli/confluence exposes the same contract from its default, ./node, and
./browser entrypoints:
import { collectAdfMediaFileIds, ConfluenceClient, validateAdf,} from "@atlcli/confluence";
const client = new ConfluenceClient(profile);const page = await client.getPageAdf(pageId);const requiredFileIds = collectAdfMediaFileIds( validateAdf(page.body.value),);const media = await client.listPageAttachmentMedia(pageId, { requiredFileIds,});collectAdfMediaFileIds() accepts only validateAdf() output. It traverses
media and mediaInline nodes iteratively, preserves first document order,
deduplicates exact IDs, and excludes external media that never resolves through
the attachment API.
Targeted attachment pagination keeps only matching metadata and returns
termination plus unresolvedRequiredFileIds. complete is true only for
index-exhausted; required-file-ids-satisfied means a later cursor was
intentionally not fetched. Attachment and request limits remain bounded, cursor
loops still throw, and cancellation still rejects. No path guesses by filename.
Most exporters should call getExportPageDetailsWithMedia() instead of wiring
the two operations manually. It performs zero attachment requests for
media-free or invalid ADF and preserves unresolved references as visible
adf-media-unresolved export notes.
Attachment delivery: PageAttachmentWriterV1
Section titled “Attachment delivery: PageAttachmentWriterV1”@atlcli/confluence/browser exports a host-neutral writer for delivering a
generated DOCX/PDF back to a Confluence page. The host injects
ConfluenceProductRequestV1; atlcli owns the bounded filename preflight,
multipart fields, response normalization, and typed failures.
| Host | Request/auth owner | Preferred body |
|---|---|---|
| CLI | Existing ConfluenceClient token/Bearer fetch |
Uint8Array or Blob |
| Browser extension | Ambient Confluence session fetch | Blob |
| Normal browser | Explicit same-origin/session adapter | Blob |
| Forge Custom UI | Consumer adapter around requestConfluence |
Blob |
Minimal browser example:
import { createPageAttachmentWriterV1 } from "@atlcli/confluence/browser";
const writer = createPageAttachmentWriterV1((path, init) => fetch(new URL(path, location.origin), { ...init, credentials: "include", }),);
await writer.create({ pageId, filename: "page.pdf", body: pdfBlob, mimeType: "application/pdf",});For create-or-update flows, call findByFilename() and then updateData() for
an existing attachment. create() repeats the exact-name preflight before its
POST and treats a concurrent duplicate as name-conflict; it never silently
switches to update. No non-idempotent POST is retried internally.
The normal-browser adapter does not bypass CORS. Forge consumers import
@forge/bridge in their own repository and pass requestConfluence into this
factory; atlcli has no Forge dependency. Blob inputs are appended to
FormData without arrayBuffer() materialization. Forge still serializes
multipart data across its bridge, and the common contract does not promise
cancellation because host adapters differ.
See the package README for complete CLI/session/Forge examples and Attachments for page-sync behavior.
Tree export: TreeSource, fetchExportTree, composeChapters
Section titled “Tree export: TreeSource, fetchExportTree, composeChapters”@atlcli/confluence (spec 002).
TreeSource— the fetch port (getPage,getChildren,getPageVersion,getSpaceHomepageId, optionalsearchPages).confluenceTreeSource(client)adapts aConfluenceClient; in-memory implementations are legitimate ports. Cloud hierarchy discovery, page-version snapshots, child positions, and space-homepage resolution use REST v2. Mixed-content discovery falls back fromdirect-childrento depth-1descendantsfor endpoint compatibility failures; authentication, throttling, cancellation, and server failures are never hidden by that fallback. Data Center continues to use its REST v1 page tree. Listed children with a non-currentstatus (draft, archived) are mapped tokind: "unsupported"carrying thatstatus, so traversal never issues a by-id read that would 404. Child versions resolve through the optional bulkgetPageVersionsclient method (one REST v2 listing per discovery round); ids absent from the bulk result fall back togetPageVersion, and a failing child snapshot (401/403/404) is a per-page completeness event (page-unreadable/page-ambiguous-404), not a discovery abort — a failing root snapshot stays fatal.TreeFetchOptions.onDiagnosticexposes a content-free operation/status/request-id projection for host progress, plus the traversalnoderole (root/child) for page-version snapshots; source ids, titles, URLs, bodies, and error messages are deliberately absent.fetchExportTree(source, scope, opts)→{ nodes, notes, complete }— ordered DFS with label filtering, completeness contract (strict/partial), limits, cancellation, progress.composeChapters(nodes, opts?)→{ blocks, notes }— one chapterized document with namespaced anchors and rewritten cross-page links. Pure and deterministic.
DOCX engine: ExportEnv & runExport
Section titled “DOCX engine: ExportEnv & runExport”@atlcli/docx (spec 006).
runExport(input: RunExportInput, env: ExportEnv)→ExportReport.prepareDocxExportRuntime(blocks, options?)→DocxExportRuntimePreparation— intent-time, isomorphic preparation of known highlighting grammars.preloadCodeFont: trueexplicitly overlaps bundled-font validation; otherwise final emitted OOXML owns font demand. Concurrent and repeated calls share work; cancellation stops only the requesting wait.ExportEnvseams:TemplateSource,AssetFetcher,OutputSink,SvgRasterizer— hosts inject them; nothing assumes a browser or Node.- Node adapters (stable, exported from the barrel):
fileTemplateSource,fileOutputSink,resvgSvgRasterizer,unsupportedAssetFetcher. - Transitively frozen input/report types:
ExportInput,ConfluencePageDetails(from confluence),TemplateMeta,ResolveDeps,ScanResult,ExportReport. - Additive
ExportReport.timingsfields expose DOCX highlighting work:highlightEngineInitMs,highlightGrammarLoadMs,highlightTokenizeMs,highlightCodeBlocks, andhighlightLanguageCount. Historical prepared checkpoints normalize missing fields to zero.
PDF engine: PdfExportEnv & runPdfExport
Section titled “PDF engine: PdfExportEnv & runPdfExport”@atlcli/pdf (specs 007/008).
runPdfExport(input: RunPdfExportInput, env: PdfExportEnv)→PdfExportReport(phases: preparing → fetching → compiling → validating → emitting; failures are typedPdfExportErrors).PdfExportEnvseams:PdfAssetResolver,PdfCompilePort,PdfOutputSink— the sink receives aPdfBytesHandle, see Emitting compiled bytes.- Host helper seams (specs 007/008):
preparePdfDocument(pre-compile asset resolution),validatePdfOutput(structural output gate),resolvePdfSettings(template settings),normalizePdfLocale,parseFontMeta/verifyFontBytes(font intake),formatPdfCompilerDiagnostics,PDF_RUNTIME_ASSETS(the canonical font/license manifest), and the versionedresolvePdfFontRequirementsV1/assertResolvedPdfFontRequirementsV1contract. - Transitively frozen types:
PdfBytesHandle,PdfSourceBundle,PdfCompilerDiagnostic,PdfExportMetadata,PdfProfile,PdfThemeOptions,PdfTemplateSettings(spec 007 settings/watermark),PreparePdfOptions/PreparedPdfDocument,ResolvedPdfSettings,ParsedFontFace. - Internal helpers deliberately not frozen (reachable via
./internal):sha256Hex,typstSettingsDict, theDEFAULT_PDF_*watermark consts, and the asset-limit consts.
Emitting compiled bytes: PdfOutputSink & PdfBytesHandle
Section titled “Emitting compiled bytes: PdfOutputSink & PdfBytesHandle”PdfOutputSink.emit(name, bytes, context?) hands the host a PdfBytesHandle — a reference
to the compiled document — not the Uint8Array itself. The handle owns one representation and
converts on demand, memoizing each conversion, so a host that needs a different shape of the
bytes (a Blob for a download, an object URL for a viewer) never ends up holding a second full
copy of the document beside the first. For a 64 MiB PDF that duplicate was measured at
+64.0 MiB.
| Member | Type | Notes |
|---|---|---|
size |
number |
Byte length. Deliberately synchronous and always known, so quota accounting never materializes a payload just to add numbers. |
mimeType |
string |
"application/pdf" for everything this engine emits. |
asUint8Array() |
Promise<Uint8Array> |
Borrowed, not owned — see the caution below. |
asBlob() |
Promise<Blob> |
Memoized: two calls return the same Blob, not two copies. |
objectUrl() |
Promise<string> |
Memoized blob: URL; the handle owns revocation. Rejects on a runtime with no URL.createObjectURL. |
release() |
void |
Revoke the minted object URL and drop memoized conversions. The handle stays usable — a later objectUrl() mints a fresh one. |
Minimal Node sink — write the document to disk:
import { writeFile } from "node:fs/promises";import type { PdfOutputSink } from "@atlcli/pdf";
const sink: PdfOutputSink = { async emit(name, bytes) { await writeFile(name, await bytes.asUint8Array()); },};Browser sink — download without a second copy of the document in memory:
import type { PdfBytesHandle, PdfOutputSink } from "@atlcli/pdf";
const sink: PdfOutputSink = { async emit(name: string, bytes: PdfBytesHandle, context): Promise<void> { context?.signal?.throwIfAborted(); const url = await bytes.objectUrl(); // memoized — concurrent calls cannot leak a URL try { const anchor = document.createElement("a"); anchor.href = url; anchor.download = name; anchor.click(); } finally { bytes.release(); // the handle revokes what it minted } },};Companion exports on the same barrel: pdfBytesFromUint8Array(bytes) and
pdfBytesFromBlob(blob) construct a handle; isPdfBytesHandle(value) narrows an unknown value
to the interface — useful in a sink shared with the DOCX engine, whose OutputSink.emit still
takes a plain Uint8Array.
PDF compiler port: PdfCompilePort & BrowserPdfCompiler
Section titled “PDF compiler port: PdfCompilePort & BrowserPdfCompiler”PdfCompilePort(@atlcli/pdf):compile(bundle, context?)→PdfCompileResult.BrowserPdfCompiler(@atlcli/pdf-compiler-browser): the shipped implementation over the SHA-256-pinned, provenance-bound typst.ts WASM (“browser” names the WASM build target — it runs under Node/Bun/browsers). Assets come in asBrowserPdfCompilerAssets({ wasm, fonts }— see the asset contract).PdfSourceBundle.fontRequirements: deterministic, byte-freeResolvedPdfFontRequirementsV1derived after document, macro, settings, and template resolution. Legacy hand-built bundles may omit it and use the full canonical set.PdfCompileResult.fontEvidence/PdfExportReport.fontEvidence: the requirement key, registered asset IDs, Typst-loaded font names, and whether the legacy full-bundle fallback was used.
Macro rendering: MacroRendererRegistry
Section titled “Macro rendering: MacroRendererRegistry”@atlcli/export-macros (spec 004).
MacroRendererRegistry+defaultRegistry(deps)/createRegistry— hosts inject the walker/converter deps and client ports; the package has zero runtime imports from other@atlcli/*packages.resolveMacroBlocks(blocks, options: MacroResolutionOptions)— the async resolver pass both engines call;MacroResolutionOptionsis embedded in the frozen docx/pdf surfaces.- The frozen surface is the registry/port contract (the injected dep + port interfaces, the
error helpers
portError/isPortError) and the resolver pass — not the concrete renderer instances (tocRenderer,jiraMacroRenderer, …) or their helpers (slugifyHeading,issueTable,jiraStatusColor, …).defaultRegistrywires those internally; they stay reachable via@atlcli/export-macros/internal. The renderer set may grow additively in minor releases.
Unstable and internal surfaces
Section titled “Unstable and internal surfaces”Explicitly not part of the freeze:
./internalsubpaths — non-frozen implementation helpers, still importable in-repo and by adventurous hosts: confluence sync machinery (Bun-only); docx resolver/serializer/OOXML plus placeholder classification (classifyPlaceholder), date formatting, image embedding and the numbering helpers; pdf Typst escaping/serialization/theming (preparePdfDocumentandvalidatePdfOutputthemselves ARE frozen seams — only the serialize/theme/escape internals are not); export-macros’ concrete renderer instances + helpers.@atlcli/docx/scanand@atlcli/docx/fixtures(dev/test API),@atlcli/pdf/template(raw Typst template),./browser-runtimeand./vite(host bootstrap, experimental). The browser runtime also re-exports stableprepareDocxExportRuntime; its narrowerprepareDocxCodeHighlighting(blocks, options?)compatibility helper remains experimental. The stable@atlcli/docx/browser-entryis the supported ordered browser composition of the frozen engine API and those browser capabilities.- The 0.x packages:
@atlcli/core,@atlcli/diagram,@atlcli/jira,@atlcli/plugin-api,@atlcli/template-pack,@atlcli/export-node— see the freeze table in Package Versioning for the per-package reasoning. Types owned by 0.x packages but reachable from frozen surfaces (e.g.Profilefrom core,DiagramTheme/renderDiagramre-exported by docx) are frozen-by-closure: the frozen packages’ 1.0 contract covers their use; the owning package stays 0.x for its wider surface.
Related topics
Section titled “Related topics”- Package Versioning — semver + breaking-change policy, freeze table
- Consuming the @atlcli Packages — install paths
- Export Asset Contract — wasm/font subpaths
- DOCX Export Engine · PDF Export Engine