Skip to content

v0.5.0

Choose a tag to compare

@Invalid8 Invalid8 released this 05 Sep 19:00
· 33 commits to main since this release
npm install better-content@0.5.0

One change, kept on its own rather than batched with features, so that a
behavior change has exactly one suspect.

createWithId now means the same thing on every adapter

It meant two opposite things depending on the backend:

Before After
Firestore set — overwrote the existing document rejects
Postgres insert — threw a unique violation rejects (unchanged)

The same call, against the same seam, did opposite things depending on which
adapter was underneath. That is exactly what the adapter seam exists to
prevent, and neither behavior was pinned by a test, so it was unspecified
rather than a deliberate difference.

Firestore now issues create instead of set, so the rejection is enforced
by Firestore itself with no read-then-write race. The Postgres adapter was
already correct and does not change.

Breaking

Code relying on Firestore's createWithId silently overwriting will now see
it reject.

  • To write regardless of what is there, use upsert.
  • To replace a document wholesale, delete then createWithId.

There is deliberately no single "replace this document" call. Neither verb
expresses it portably, since upsert merges rather than replaces.

The write contract, now specified and tested

It is stated on the DataAdapter type, in the adapter guide, and pinned by
tests on both shipped adapters.

Method On an id that already exists
createWithId rejects; never overwrites
upsert writes, merging; omitted fields are kept
update patches the given fields
delete succeeds even when the id does not exist

If you maintain your own DataAdapter, check it against that table. The
in-memory adapter in the playground and the "write your own adapter" snippet
both overwrote silently and have been corrected.

Docs

The adapter guide told authors to "treat upsert as create-or-replace", when
both shipped adapters merge, so the guidance contradicted the implementations
it described. Corrected, with the write contract added to the core and
adapters API pages, the how-it-works guide and the README.

Also documented, found while investigating this: reads order by createdAt by
default and Firestore omits documents that lack the ordering field, so records
written only through upsert, which does not stamp createdAt, can be missing
from an unfiltered read.

Full detail in CHANGELOG.md.