Skip to content

better-content 0.7.0

Choose a tag to compare

@Invalid8 Invalid8 released this 06 Sep 14:58
· 27 commits to main since this release

Say what your content is

Item takes an optional type argument: Item<T> = T & { id: string }. Item on its own is unchanged, so nothing breaks.

const banner = engine.getItem<Banner>("portfolio", "banner");
banner?.skills;   // Skill[] | undefined, previously unknown

This is less a new concept than a name for one: DataAdapter has returned T & { id: string } from fetchCollection, fetchById, create and createWithId since v1, and those four are now stated as Item<T>.

The parameter is on the addressed read in every binding — getItem<T> in core, useCmsItem<T> in React and Vue, itemStore<T> in Svelte — and on the write path:

await engine.createItem<Project>("projects", { title: "Portfolio", order: 0 });
await engine.updateItem<Project>("projects", id, { order: 3 });

Read and write are not the same promise

On a read, T is an assertion: nothing verifies the stored record matches, exactly as fetchCollection<T> has never verified it. On a write, T is checked, because you hand over the object and TypeScript compares it to the shape.

The write constraint is T extends object, deliberately not Record<string, unknown> — an interface has no implicit index signature and would have been rejected. This also fixes an older papercut: createItem's previous parameter rejected interface-typed objects outright, so passing your own interface Project needed a cast or a type-alias workaround. It no longer does.

editField stays unknown. A single field value cannot be typed without a collection-keyed content schema, which is deliberately not shipped.

ItemMap stays non-generic, and that was measured

Against the real consumer that motivated this release: deleting its entire compatibility shim left six type errors, and every one was a getItem read. None came from reading items, because list reads there narrow with a runtime type guard, which checks for real. A collection-keyed schema would have returned a union for a collection of mixed singletons — so it would not have fixed the case that prompted this — while encouraging consumers to drop the guard protecting them.

Docs: core API

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