Entity / foldkit-entity

Ids of their own

An Entity is identified by its id field, and the id keeps the type you give it. Brand it, and a ref to an Author can no longer stand in for a Post's:

ts
const AuthorId = Schema.String.pipe(Schema.brand('AuthorId'))
const PostId = Schema.String.pipe(Schema.brand('PostId'))

const Writer = Entity.define('Author', Schema.Struct({ id: AuthorId, name: Schema.String }))
const Article = Entity.define('Post', Schema.Struct({ id: PostId, title: Schema.String }))
const Press = Entity.relate({ Writer, Article }, { Article: { author: Relation.one(Writer) } })

type WriterId = import('foldkit-entity').IdOf<typeof Press.Writer> // AuthorId

const ByLine = Entity.select(Press.Article, { author: true })
// { author: EntityRef<'Author', AuthorId> }

Entity.input(Press.Article, Schema.Struct({ authorId: AuthorId }), {
  authorId: Relation.input(Press.Article.relations.author), // a PostId, or any text, is a type error
})
  • IdOf<E> is the type of the id field; EntityRef<Name, Id> carries it, and Relation.input takes it (Id | null for an optional one, a list for a many).
  • A ref's schema is the id's own, so a pattern or a check on the id holds for every ref to it.
  • An id is text. A ref travels and is stored as text, so an Entity whose id is a number (or has no id field) has refs with a plain string id: the number as text.
  • A Selection carries its Entity's id type, so foldkit-remote's Data.get(PostPage, id) takes a PostId and refuses an AuthorId, or plain text.
  • Nothing here is new API to opt into. An Entity with id: Schema.String types exactly as before.