easeo documentation
easeo is a deterministic SEO metadata generator. It turns a content entity,
a route path, and a site configuration into one structured payload: canonical
URL, title, description, robots directives, Open Graph, Twitter Cards, and
JSON-LD. The core is written in Rust and shipped to Python and
JavaScript/TypeScript, so the same inputs produce byte-for-byte identical
output in every language.
content entity + route + config
|
v
easeo (library)
|
v
SEOPayload
|
+--> framework adapter --> <head>
|
+--> SEO contract --> validation
What easeo is¶
- A Rust workspace with three crates:
easeo-core: all the logic, with no I/O.easeo-python: PyO3 bindings.easeo-node: napi-rs bindings.- One primary function:
build_seo_payload(entity, route, config). - Pure and deterministic: no timestamps, no randomness, no environment reads.
- Framework-agnostic: adapters for Next.js, Astro, Vite, Nuxt, SvelteKit, React, FastAPI, Django, Flask, and Zensical.
Why easeo¶
- Deterministic output. Same inputs, same bytes. Snapshot test it, hash it, cache it forever, diff it across deployments.
- One implementation, three languages. The Rust core guarantees that Python and JavaScript agree, verified by cross-language conformance tests.
- Contract-first. Encode your SEO intent as a machine-readable contract and fail the build when it drifts.
- Zero ceremony. One function call returns a payload that renders itself to safe, ready-to-inject head HTML.
What easeo is not¶
- Not an SEO crawler. It does not fetch your site.
- Not a score generator. It does not grade content.
- Not a keyword tool. It formats the data you give it.
- Not a browser automation framework. It performs no I/O.
- Not an analytics platform. It stores no state.
Quick start¶
from easeo import SEOConfig, SEOEntity, build_seo_payload
config = SEOConfig(
canonical_host="example.com",
public_base_url="https://example.com",
site_name="Example",
)
entity = SEOEntity(
entity_type="post",
title="Hello World",
excerpt="An example post.",
)
payload = build_seo_payload(entity, "/blog/hello", config)
print(payload.render_html())
use easeo_core::{SEOConfig, SEOEntity, EntityType, build_seo_payload};
let config = SEOConfig {
canonical_host: "example.com".into(),
public_base_url: "https://example.com".into(),
..Default::default()
};
let entity = SEOEntity {
entity_type: EntityType::Post,
title: Some("Hello World".into()),
excerpt: Some("An example post.".into()),
..Default::default()
};
let payload = build_seo_payload(&entity, "/blog/hello", &config)?;
assert_eq!(payload.canonical, "https://example.com/blog/hello");
Guides¶
- Tutorial: install, first payload, fallbacks, rendering, contracts, configuration.
- Concepts: determinism, the entity and payload models, fallback chains, URL normalization, schemas, validation.
- Guides: custom JSON-LD, hooks, contracts in CI, framework recipes, migration.
- Reference: Python, JavaScript, and Rust APIs.
- Integrations: per-framework setup.
- Recipes: real-world patterns.
Repository layout¶
easeo/
├── Cargo.toml # Rust workspace
├── pyproject.toml # maturin / Python package
├── README.md
├── LICENSE
├── crates/
│ ├── easeo-core/ # all logic
│ ├── easeo-python/ # PyO3 bindings
│ └── easeo-node/ # napi-rs bindings
├── packages/core/ # @easeo/core wrapper
├── integrations/ # JS framework integrations
├── python/easeo/ # Python package and adapters
├── tests/ # Python, JavaScript, conformance
└── docs/
└── (this directory)
License¶
MIT.