commonmeta-schema
v1.0.0-rc30
Published
Commonmeta JSON Schemas and conformance fixtures
Maintainers
Readme
commonmeta-schema
Language-neutral JSON Schema definitions and conformance fixtures for Commonmeta, the scholarly metadata interchange format.
This repository is the shared source of truth consumed by the following Commonmeta implementations:
- commonmeta-rs (Rust)
- commonmeta-py (Python)
Keeping the schema and the golden fixtures in one place lets every implementation validate against the same contract and run the same cross-format conformance tests.
Layout
schemas/
commonmeta_v1.0.json # the Commonmeta JSON Schema (current)
commonmeta_v1.0rc*.json # earlier release candidates, retained but not exported
fixtures/
commonmeta/ # canonical Commonmeta records (round-trip + expected output)
<format>/ # input fixtures in a given source format
<format>_out/ # expected writer output (Commonmeta -> format)
<format>_commonmeta/ # expected reader output for non-JSON inputs
utils/ # shared utility fixtures for helpers outside format conversionSchema versions
Schemas are versioned by filename. The current version is commonmeta_v1.0.json,
and the schema_version field of a Commonmeta record is the URL
https://commonmeta.org/commonmeta_v1.0.json.
Earlier release candidates (commonmeta_v1.0rc1…rc7) are kept in schemas/
for reference, but the packages export only v1.0 — new versions are added
alongside existing files rather than replacing them, so implementations can pin
a version.
Fixture conventions
The conformance harness in each implementation follows these naming rules (a missing pair is skipped, so partial coverage is fine):
| Test kind | Input | Expected |
|----------------------|----------------------------------------|---------------------------------------------|
| Round-trip | fixtures/commonmeta/<name>.json | itself (re-serialized, semantically equal) |
| Reader (JSON input) | fixtures/<format>/<name>.json | fixtures/commonmeta/<name>.json |
| Reader (text input) | fixtures/<format>/<name>.<ext> | fixtures/<format>_commonmeta/<name>.json |
| Writer | fixtures/commonmeta/<name>.json | fixtures/<format>_out/<name>.<ext> |
Formats currently covered: crossref, crossref_xml, datacite,
datacite_xml, schemaorg, csl, bibtex, cff, ris, jsonfeed,
inveniordm, codemeta, orcid, orcid_xml, openalex_source.
OpenAlex scope
The openalex_source fixtures cover OpenAlex sources/containers (for
example journal, repository, and blog containers), not OpenAlex works.
fixtures/openalex_source/*.jsonare OpenAlex source records (S...IDs).fixtures/commonmeta/openalex_source_*.jsonare the expected Commonmeta container entities.
OpenAlex works should be added under a separate format namespace (for example
openalex_work) to keep reader and writer behavior unambiguous.
Most fixtures are work entities; orcid and orcid_xml cover person
instead. Both read a full ORCID record — with employments and educations as
affiliations — from two different sources:
orcid/<id>.jsonis the ORCID REST API/recordresponse (JSON). Because it carries the full person, its commonmeta output also hasdescriptionandurls. →commonmeta/<id>.jsonorcid_xml/<id>.xmlis the ORCID Public Data File record (XML), a summary that omits biography and researcher URLs. →orcid_xml_commonmeta/<id>.json
Both fixtures trim the record's works list to the 10 most recent (the readers convert person identity and affiliations, not works).
Semantic comparison
Fixtures are compared as parsed JSON trees, not as strings, using an
omitempty-aware and numeric-aware diff: key order and whitespace are
irrelevant, an absent field equals an empty/zero/null value, and 52
equals 52.0. This keeps hand-authored fixtures robust across
implementations.
Utility fixtures
fixtures/utils/ is reserved for shared cross-implementation test data for
helper functions outside reader/writer format conversion. The first use is DOI
helper coverage for encode_doi and decode_doi. These fixtures are not
reader/writer conformance cases; they exist so commonmeta-py and
commonmeta-rs can assert the same normalization, checksum, and error
handling behavior from one canonical source.
Using this repository
Each implementation vendors (copies) the schema and fixtures it needs from
here. Update the canonical files in this repository first, then sync them
into commonmeta-rs and commonmeta-py.
Packaging and publishing
This repository can be published as both:
- a Python package on PyPI (
commonmeta-schema) - a Rust crate on crates.io (
commonmeta-schema) - a JavaScript package on npm (
commonmeta-schema)
Publishing is done explicitly via CLI (no automated release pipeline in this repo).
The packages are release candidates on the way to 1.0; both ship the v1.0
schema:
- PyPI uses PEP 440 form:
1.0rc30 - crates.io uses SemVer form:
1.0.0-rc30 - npm uses SemVer form:
1.0.0-rc30
scripts/sync_versions.py is the single entry point for preparing a release. It
does three things, in this order:
- Rewrites
schemas/commonmeta_v1.0.jsonfrom the newestcommonmeta_v1.0rc*.json, resetting$id,title, and theschema_versionconst to the stable v1.0 URL. - Mirrors the canonical
schemas/andfixtures/intorust/schemas/andrust/fixtures/(the crate can only package files underrust/). - Syncs
rust/Cargo.toml's version withpyproject.toml's. - Syncs
package.json's version withpyproject.toml's.
python scripts/sync_versions.pyThe ordering matters and is why the mirror lives in the script rather than in a
separate rsync step: mirroring before step 1 ships a crate whose
commonmeta_v1.0.json still carries the previous rc's schema_version const,
which then rejects the crate's own v1.0-stamped fixtures.
To verify sync in CI/local checks without changing files:
python scripts/sync_versions.py --checkRecommended pre-release checks:
python scripts/sync_versions.py --check
uv build
cargo test --manifest-path rust/Cargo.toml
cargo package --manifest-path rust/Cargo.toml
npm pack --dry-runJavaScript / TypeScript (npm)
Install:
npm install commonmeta-schemaRead packaged assets by path:
import { readFileSync } from "node:fs";
import { fixturesPath, schemaPath } from "commonmeta-schema";
const schema = JSON.parse(readFileSync(schemaPath("1.0"), "utf8"));
const fixture = JSON.parse(
readFileSync(`${fixturesPath("commonmeta")}/journal_article.json`, "utf8")
);Or import JSON files directly from subpath exports:
import schema from "commonmeta-schema/schemas/commonmeta_v1.0.json" with { type: "json" };The helper API mirrors the Python package and exports packageRoot,
schemaPath(version), fixturesPath(formatName), and
availableSchemaVersions().
Python (PyPI) via uv publish
- Ensure you are authenticated for PyPI (recommended: trusted publisher or API token).
- Build distribution artifacts:
uv build- Optionally validate the build metadata:
uvx twine check dist/*- Publish to PyPI:
uv publishRust (crates.io) via cargo publish
- Log in once with a crates.io token:
cargo login- Validate package contents:
cargo package --manifest-path rust/Cargo.toml --allow-dirty- Publish:
cargo publish --manifest-path rust/Cargo.tomlJavaScript / TypeScript (npm) via npm publish
- Validate package contents:
npm pack --dry-run- Publish:
npm publishRecommended release order
- Publish
1.0rc30to PyPI withuv publish. - Publish
1.0.0-rc30to crates.io withcargo publish. - Publish
1.0.0-rc30to npm withnpm publish. - Create a git tag
v1.0rc30after all uploads succeed.
Copy-paste release run
Run this from the repository root:
set -euo pipefail
# 1) Regenerate the v1.0 alias, mirror assets into rust/, sync the crate version
python scripts/sync_versions.py
# 2) Fail the release if anything is still out of sync
python scripts/sync_versions.py --check
# 3) Validate both package builds
uv build
cargo test --manifest-path rust/Cargo.toml
cargo package --manifest-path rust/Cargo.toml
npm pack --dry-run
# 4) Publish Python package
uv publish
# 5) Publish Rust crate
cargo publish --manifest-path rust/Cargo.toml
# 6) Publish npm package
npm publish
# 7) Tag release (replace with the release version)
git tag v1.0rc30
git push origin v1.0rc30License
MIT © 2026 Front Matter
