Skip to content

feat: Python and Node bindings (v1: conversion)#24

Merged
been-there-done-that merged 16 commits into
spec/operator-consolefrom
feature/bindings
May 1, 2026
Merged

feat: Python and Node bindings (v1: conversion)#24
been-there-done-that merged 16 commits into
spec/operator-consolefrom
feature/bindings

Conversation

@been-there-done-that
Copy link
Copy Markdown
Collaborator

Summary

  • Adds Python (folio PyPI) and Node.js (@folio/folio npm) bindings that drive Folio in-process — no HTTP server, no Docker.
  • Engine selectable via cargo features (chromium / libreoffice).
  • New engine::chrome_fetch module detects system Chrome or downloads a pinned Chrome-for-Testing build into a platform cache on first use.
  • v1 covers HTML / URL / Markdown / Office → PDF. v2 (screenshots + PDF ops) is fully spec'd and deferred.

Spec & plan

  • Spec: + "docs/superpowers/specs/2026-05-01-bindings-design.md" +
  • Plan: + "docs/superpowers/plans/2026-05-01-bindings.md" + (12 TDD tasks)

What changed

Area Status
+ "engine::chrome_fetch" + (detect / cache / download) 8 unit tests pass
+ "crates/py" + (PyO3, sync + "Folio" + + async + "AsyncFolio" + , abi3-py38) builds clean
+ "bindings/python" + (maturin) wheel builds; 5 smoke tests pass
+ "crates/js" + (napi-rs async + "Folio" + ) builds clean
+ "bindings/node" + (npm) tagged-error decoding; 4 smoke tests pass
E2E render tests (gated on + "FOLIO_E2E=1" + ) 4 Python + 3 Node, default-skip verified
CI workflows Linux/macOS/Windows smoke matrix + manual-dispatch E2E
Docs root README + + "bindings/README.md" +

Test plan

  • Smoke CI green on all three OS for both bindings workflows
  • Manual: + "pip install" + the wheel on a clean machine without Chrome → + "Folio().html_to_pdf(...)" + produces a valid PDF (auto-download path)
  • Manual: same for Node — + "npm install && Folio.create()" + then + "htmlToPdf" +
  • Trigger + "bindings-python" + + "e2e" + job via workflow_dispatch and confirm pass
  • Trigger + "bindings-node" + + "e2e" + job via workflow_dispatch and confirm pass

🤖 Generated with Claude Code

__deesh__ and others added 16 commits May 1, 2026 18:10
- README.md: leaner, ~265 lines (was ~615). Drops marketing comparison
  table and inline 32-row spec list; foregrounds operator console as the
  real differentiator vs Gotenberg; calls out deliberate gaps (TLS, RBAC)
  and empty placeholders explicitly.
- comparison.md (new, root): in-depth audit vs Gotenberg in 16 sections —
  endpoint matrix, per-engine feature tables, what-we-did / didn't-do /
  shouldn't-do scorecards.
- docs/markdown-plus.md (new): design proposal for an enhanced Markdown
  route (front-matter, math, mermaid, syntax highlighting, includes,
  themes). Sits alongside the basic markdown route, not a replacement.
- docs/specs/ → docs/specs-archive-2026-05-01.zip. 32 legacy spec files
  archived; fresh contributor-facing specs will be re-introduced under
  docs/ in better-organised form.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
v1 ships HTML/URL/Markdown/Office to PDF for Python (sync Folio + async
AsyncFolio) and Node (async). Chrome auto-download lives in a new
engine::chrome_fetch module so the CLI/server can opt in later.
v2 (full parity: screenshots + PDF ops) is fully specified and deferred.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Plan covers chrome_fetch module, PyO3 sync+async Folio, napi-rs async
Folio, maturin/napi-rs packaging, smoke + E2E tests, CI matrix.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds the chrome-fetch feature to the engine crate, wiring optional deps
(reqwest, sha2, zip, flate2, tar, dirs) and a skeleton chrome_fetch module
with stub submodules ready for Tasks 2-4 to implement.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements detect_system_chrome() with a testable detect_with() helper
that accepts injectable env vars, path lookup, and exists functions.
Adds minimal placeholders for cache.rs and download.rs (Tasks 3 & 4)
and adds missing doc comments to EnsureOptions fields to satisfy
the crate-level missing_docs deny lint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replaces the Task-4 placeholder with the real Chrome-for-Testing
downloader: fetches the per-version manifest, streams the zip archive,
extracts atomically via a .partial staging dir → rename, and chmod 755s
chrome binaries on unix. walkdir added as optional dep gated behind the
chrome-fetch feature.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements Task 5: PyO3 _native module with sync Folio class, error
hierarchy mapped from EngineError, tokio runtime singleton, and JSON
round-trip for engine option types via serde_json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Wire up bindings/python/ as a maturin mixed project: pyproject.toml
targets crates/py, folio/__init__.py re-exports all public symbols from
_native, and tests/test_smoke.py verifies 3 structural checks (exports,
error hierarchy, class methods) without launching Chrome.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implement AsyncFolio using pyo3-async-runtimes 0.22.0 (matched to PyO3 0.22
workspace dep). Engine futures are bridged to the caller's running event loop
via `future_into_py`; the tokio runtime builder is registered in `_native` init.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Implements Task 8: napi-rs cdylib with async Folio class exposing
html_to_pdf, url_to_pdf, markdown_to_pdf, office_to_pdf, and close.
Error tagging convention ([Tag] prefix) wires into Task 9 JS loader.
Also adds tokio_rt/serde-json features and napi-build to workspace deps.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Adds bindings/node with package.json (@folio/folio), a hand-written
index.js that wraps the napi-rs native loader (_native.js) with typed
error subclasses (FolioError hierarchy), TypeScript declarations, and
4 vitest smoke tests that all pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@been-there-done-that been-there-done-that merged commit af27fb9 into spec/operator-console May 1, 2026
11 of 17 checks passed
@been-there-done-that been-there-done-that deleted the feature/bindings branch May 3, 2026 14:39
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