Skip to content

better-content 0.9.0

Choose a tag to compare

@Invalid8 Invalid8 released this 06 Sep 16:45
· 18 commits to main since this release

Both adapters now read back the same shape

The seam exists so behaviour does not change when the backend does. Write contracts were pinned per adapter; read shapes were not, and two divergences had survived there in opposite directions:

  • The Postgres adapter added a collection field to every record it read. Firestore never did, Item does not declare it, and toRow already stripped it going in — so it was synthesized on read rather than stored. It is gone.
  • The Postgres adapter stripped createdAt and updatedAt, which Firestore returned. Those are real stored columns written by the adapter itself, so they are no longer hidden.

The rule both now follow: a read returns the record's stored fields plus id. An adapter neither invents fields nor hides them.

{ id: "hero", title: "Hello", createdAt: "2026-09-06T…", updatedAt: "…" }

Timestamps are ISO strings on both backends

Matching field names is not parity if one backend yields a Date and the other a string — and these records are JSON-serialized across createContentHandler and server-rendered payloads, where a Date becomes a string in transit regardless.

Writes accept either form, because an adapter that cannot be handed back what it just returned is not round-trippable, and copying one environment into another does exactly that. That was found by a test rather than by reasoning: the first cut serialized reads and broke seedItemMap.

seedItemMap strips the timestamps too

Alongside id and collection. All four address the record or belong to the adapter rather than being content, and a snapshot read from a backend carries all four. Passing the timestamps through would also have broken the documented promise that createdAt is reset — Firestore's createWithId overrides what it is handed, while Postgres would have inserted it verbatim. Divergence again, one layer up.

⚠️ Behavior change for Postgres consumers

Reading item.collection now returns undefined, and reads carry createdAt/updatedAt that were not there before. Nothing in the library consumed collection from a fetched record, and it was undocumented.

Added

A cross-adapter parity suite running the real Postgres adapter against PGlite and the Firestore adapter against a stub, pinning that the two agree on field names, on how a timestamp is represented, and that each accepts back what it returned. Its first run is what found the timestamp divergence.

Verified against the real Postgres consumer: portfolio-2026 typechecks and builds green, its SSG pages still generating from live Postgres rows.

Docs: adapters

Requires create-better-content@0.8.0 for newly generated apps to pick this up.