Changelog¶
All notable changes to this project will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
The repository CHANGELOG.md
is the full Keep a Changelog record (including 0.0.x). This page is the
user-facing summary.
Unreleased¶
0.6.0 - 2026-08-26¶
Opt-in write-404: same-params hit is write_rejected, not
AuthorizationError. Custom-route action classify. Trackers: #166 #171.
Upgrade notes¶
- Opt-in
classify_write_404: a same-params GET hit is nowNotFoundError(classified_from=write_rejected), notAuthorizationError. Remaining-draft delete / publish is stillAuthorizationError(classified_from=write_404). Catchclassified_from, not the 0.5.0 auth type. - Custom
StrapiClientimplementors must addpublish/unpublish/discard_draft(withclassify_write_404).
Added¶
- Opt-in
classify_write_404on custom-routeunpublish()/discard_draft()(sync + async). Probes the document path (write addressing params, then draft), never/actions/*. DefaultFalsestill surfaces the stock 404/405. A document miss keeps the original missing-route 404. Draft-only isNotFoundError/classified_from=draft_only(nothing published to unpublish or restore via discard). A readable published variant isNotFoundError/classified_from=write_rejected(refused write, no permission copy). Helpers remain custom-route only (#166). publish/unpublish/discard_draft(withclassify_write_404) on theStrapiClientprotocol.
Fixed¶
- Opt-in
classify_write_404no longer raisesAuthorizationErrorwhen the same-params GET hits. That arm isNotFoundErrorwithclassified_from=write_rejectedand copy that names no permission cause ("{operation} was refused"). A remaining-draft delete / publish is stillAuthorizationError/write_404(#171).
0.5.0 - 2026-08-26¶
Locale-aware existence checks and write-404 classification. Trackers:
161 #163.¶
exists_in_locale(collection, document_id, locale=None) on the sync
and async clients (and StrapiClient) is the same published-then-draft
probe as exists(), plus optional locale. Non-i18n types retry
without locale on Invalid key locale. exists() stays
document-level.
Opt-in classify_write_404 no longer treats every readable draft as a
missing token. The probe uses the write's addressing params, then the
draft variant. Draft-only updates stay NotFoundError
(classified_from=draft_only). A publish() 404 with only a draft
readable is AuthorizationError — stock PUT ?status=published
publishes drafts, so a remaining draft means the write did not run.
A DELETE 404 while the document is still readable stays
AuthorizationError. remove accepts optional query for
locale-scoped deletes. AuthorizationError copy names the operation
(Update / Delete / Publish).
Upgrade notes¶
exists()stays document-level. Useexists_in_locale(..., locale=)for a locale-aware probe.- Opt-in
classify_write_404: draft-only update 404s stayNotFoundError; publish 404 with a remaining draft isAuthorizationError.
0.4.0 - 2026-08-19¶
i18n localizations, nested component/dynamic-zone relations, dest media writes, FAIL-write missing locales, dry-run / JSONL preflight, and Docker e2e CI. Tracker: #144.
Import restores i18n localizations of a shared documentId (first locale
creates; later locales PUT {destDoc}?locale=). SKIP is per-locale.
FAIL writes missing locales, does not overwrite existing locales or their
outbound relations, then raises after the full write pass.
exists() no longer treats an unrelated draft 400 as “does not exist.”
Import writes nested component/dynamic-zone relations, posts dest media
ids (media_write()), and falls back to numeric relation PUTs on a v4
destination.
The e2e Docker fixture includes an i18n localized-articles type.
Export metadata includes walked component schemas. Media remapping
follows FieldType.MEDIA when a schema is present.
FAIL dry-run probes locale conflicts and raises without writing.
Dry-run no longer maps missing dests to id 0 or the source
documentId; existing dests still map real dest ids. Dry-run reports
unresolved dest relations as warnings (relations_unresolved) and
counts entities_to_publish. success on dry-run is write-safety,
not “relations would apply.” Incomplete dest-relation fields are not
written. Live entities_to_publish increments only when a dest
documentId is queued. JSONL export persists total_entities /
total_media on the metadata line (None is unknown, 0 is empty;
import still recounts when those fields are None or 0). Finished
JSON / in-memory export also snapshots both counts (total_media == 0
when there is no media). Relation
IDs keep numeric-looking documentIds as strings. RelationId is a
Pydantic input type, not an isinstance target; extract rejects bool.
JSONL import shares preflight validation with
import_data and does not pre-create empty mapping dicts. Component
extract/strip unwraps v4 {data} / {data, meta} wrappers and logs
unexpected payload shapes.
The e2e workflow also runs on library path changes and keeps the
compose stack until logs are collected. The compose probe retries
docker compose and does not fall back to a missing docker-compose
v1 binary.
Upgrade notes¶
- Extra locales of a shared
documentIdare restored (PUT {destDoc}?locale=). SKIPis per-locale.FAILwrites missing locales then raises.- Dry-run
successis write-safety; checkrelations_unresolved. - JSONL totals:
Noneis unknown,0is empty. - Relation IDs are
StrictInt | StrictStr;"5"stays a string.
0.3.0 - 2026-08-16¶
v5 export/import populate contract. Trackers: #96 #97 #98 #99 #100 #101.
Upgrade notes¶
- Export/import require
pluralNamefrom the content-type schema (no UID path invention). - Import writes relations with
relation_write()(v5 documentId strings). ExportedEntitystorespublished_at/locale; import publishes live source documents after relation writes.- Extra locales of the same
documentIdare not restored as i18n localizations (follow-up). - Short stream pages before
totalraise instead of stopping early. upload_file/upload_filesre-raise auth / not-found / server errors.
0.2.0 - 2026-08-16¶
Strapi 5 connector surface: Draft & Publish, Content-Type Builder discovery, origin-path probe, relation writes, blocks ↔ markdown, and complete stream/export. Tracker: #55.
Upgrade notes¶
stream_entities/StrapiExporterdefault todocument_status=DocumentStatus.DRAFT(v5status=draft, confirmed v4publicationState=preview). Passdocument_status=Nonefor published-only (the previous implicit default).publish()is stock RESTPUT ?status=published.unpublish()anddiscard_draft()still need custom/actions/*routes (not stock REST).- Non-JSON 2xx responses raise
UnstructuredResponseError(notFormatError). get_components()andget_content_types()raise on unparsable items unlessskip_unparsable=True.StrapiQuery.filter()/.populate()fail immediately on the wrong type.- Import existence checks are draft-inclusive (published GET, then
status=draft). Auth / 5xx / network errors on the probe are no longer treated as “does not exist”. - Export/import still extracts relations and media from the v4
{data: ...}populate shape. Flat Strapi 5populate=*objects are a follow-up (do not treat 0.2.0 as a complete v5 migrate path).
Added¶
- Typed Blocks nodes (
BlockNode,TextNode, …) (#87). ContentTypeOptionswithextra="allow"; Draft & Publish is stripped fromoptions(#88).- Stream/export
document_status(#84, #85). UnstructuredResponseError.reason(#86).- Stock REST
publish()(#65). - Stream/export completeness via pagination echo (#81, #67).
- Inline
markdown_to_blocks(#77). - Relation write helper
relation_write()/RelationWriteOp(#54). assert_pagination_echo()(#48).is_uniqueness_violation()andValidationError.field_errors(#53, #76).- Origin-path
api_prefix=Falseandget_admin_information()(#46). collection_endpoint()/document_endpoint()frompluralNameonly (#49).- First-class CTB
draft_and_publish(True/False/None) (#45). - Blocks ↔ markdown and
FieldType.BLOCKS(#51). DocumentStatus,PublicationFilter,exists(), write-404 classify,publish/unpublish/discard_draft, wire enums,MethodNotAllowedError,UnstructuredResponseError.- Percent-encoded
document_id=on typed CRUD (#50).
Changed¶
markdown_to_blockslifts images to root siblings (#89).- v5 multi-page streams keep
status=draftafter detect; auto + v4 re-fetch page 1 withpublicationState(#93). get_components()raises unlessskip_unparsable=True(#79).- CTB options lift schema-root keys (#80).
filter()fail-fast (#60); shared document path encoder (#82); writedataobject + parser wrap (#58, #59).- Line-prefix escaping in
blocks_to_markdown(#78). - Every HTTP error carries
status_code. Non-JSON 2xx isUnstructuredResponseError. Default CI /make testrunspytest tests/unitonly.
Fixed¶
- Concurrent
AsyncClientwrites no longer stamp the wrong HTTP status onUnstructuredResponseError. join_document_pathrejects whitespace-only collection names.- Streamers raise on empty later pages (or an empty first page with
total > 0). - Default
status=draftis dropped after a first-page 400 (Draft & Publish off). - Trailing
/apistripped fromStrapiConfig.base_url(#47). - Document IDs containing
/,?,#, or%are percent-encoded (#50).
0.1.0 - 2026-02-04¶
Fixed¶
- Race conditions in async bulk operations (#30).
- JSONL media manifest no longer consumes the entity stream.
- v5 string relation IDs (
documentId) on import. - JSONL import path traversal protection and two-pass streaming (#29).
update_mediaversion detection and streaming downloads (#28).
Added¶
- Schema-driven relation extraction and JSONL streaming export/import (#28).
0.0.6 - 2026-02-03¶
Added¶
- Content-Type Builder API, UID conversion utilities, SEO detection.
Fixed¶
- Extra
STRAPI_*env vars no longer breakStrapiConfig. - v5 Content-Type Builder list flattening.