Skip to content

feat(page): 'page markdown' + 'page set-markdown' (server-side markdown I/O)#47

Merged
4ier merged 1 commit into
mainfrom
feat/page-markdown
Apr 30, 2026
Merged

feat(page): 'page markdown' + 'page set-markdown' (server-side markdown I/O)#47
4ier merged 1 commit into
mainfrom
feat/page-markdown

Conversation

@4ier
Copy link
Copy Markdown
Owner

@4ier 4ier commented Apr 30, 2026

Closes #37. Wraps Notion's 2025 endpoints:

  • GET /v1/pages/:id/markdownnotion page markdown <id>
  • PATCH /v1/pages/:id/markdownnotion page set-markdown <id>

page markdown

  • Prints server-rendered markdown (strictly better than block list --md for page-level dumps).
  • --out <path> writes directly to file.
  • Surfaces Notion's truncated / unknown_block_ids as stderr notes.

page set-markdown

Smoke: replace → append → read-back → stdin replace → multi-mode rejection all pass against a real page.

Closes #37. Wraps the two 2025 Notion endpoints that do server-side
markdown I/O on a full page:

  GET   /v1/pages/:id/markdown     →  notion page markdown <id>
  PATCH /v1/pages/:id/markdown     →  notion page set-markdown <id>

### page markdown

Prints a page's content as markdown. Strictly better than `block list
--md` for page-level dumps: the server handles toggles, columns, synced
blocks, and databases-as-pages uniformly and always matches what the
Notion UI shows.

  notion page markdown <id>
  notion page markdown <id> > page.md
  notion page markdown <id> --out page.md
  notion page markdown <id> --format json   # full response incl. truncated flag

Surfaces Notion's 'truncated' flag and 'unknown_block_ids' as stderr
notes in default output so silent partial renders don't go unnoticed.

### page set-markdown

Updates a page from markdown in one API call. Supports all four
mutation modes Notion's PATCH endpoint accepts:

  --replace         (default) replace_content: overwrite whole page
  --append          insert_content at end
  --after <anchor>  insert_content after ellipsis anchor ("start...end")
  --range <anchor>  replace_content_range bounded by ellipsis anchor

  --file <path>     (use '-' for stdin)
  --text <str>      inline markdown
  --allow-deleting-content   pass allow flag for destructive modes

Because Notion handles the full markdown → blocks conversion server-side,
this is the cleanest way to write long documents — the 100-children
batching (#21) doesn't apply to set-markdown.

### Implementation

Two pure helpers isolate the fiddly bits for unit testing without network:
- buildSetMarkdownBody: produces the correct envelope for each mode;
  rejects ambiguous flag combinations up front.
- readMarkdownSource: picks the source (--file / --text / stdin); same
  mutual-exclusion rules as the rest of the CLI.

### Tests

- Exhaustive buildSetMarkdownBody coverage across all four modes +
  allow_deleting_content + multi-mode rejection.
- readMarkdownSource: required / conflict / file / stdin / file-missing.

### Smoke

Verified end-to-end against a real workspace: replace via --file,
append --text, markdown read with --out, stdin replace, and multi-mode
rejection all behave as documented.
@4ier 4ier merged commit 8e4cd81 into main Apr 30, 2026
3 of 4 checks passed
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.

page markdown / page set-markdown: first-class wrappers around the new markdown endpoints

1 participant