@btravstack/entity / Entity
Entity
Type Aliases
Abstract
type Abstract<Name, S, A> = AbstractEntitySrc<Name, S, A>;Defined in: entity.ts:678
What Entity.abstract(name)(fields, options) returns.
Type Parameters
| Type Parameter |
|---|
Name extends string |
S extends Fields |
A extends Schemas |
Aggregate
type Aggregate<Tag, S, A, Ev, O> = AggregateStaticSrc<Tag, S, A, Ev, O>;Defined in: entity.ts:628
What Entity.aggregate(tag)(fields, options) returns.
Type Parameters
| Type Parameter |
|---|
Tag extends string |
S extends Fields |
A extends Schemas |
Ev extends Events |
O extends string |
AggregateInstance
type AggregateInstance<S, A, Ev, O> = AggregateInstanceSrc<S, A, Ev, O>;Defined in: entity.ts:636
Type Parameters
| Type Parameter |
|---|
S extends Fields |
A extends Schemas |
Ev extends Events |
O extends string |
BaseInstance
type BaseInstance<S, A> = BaseInstanceSrc<S, A>;Defined in: entity.ts:655
Type Parameters
| Type Parameter |
|---|
S extends Fields |
A extends Schemas |
ComputedField
type ComputedField<T, D> = ComputedFieldSrc<T, D>;Defined in: entity.ts:610
One derived field: its schema, and the function that produces it.
Type Parameters
| Type Parameter |
|---|
T extends z.core.$ZodType |
D |
ConstructionKey
type ConstructionKey = ConstructionKeySrc;Defined in: entity.ts:656
CreateInput
type CreateInput<E> = E["__createInput"];Defined in: entity.ts:604
What create accepts from a caller.
Type Parameters
| Type Parameter |
|---|
E extends object |
Decision
type Decision<A, E> = DecisionSrc<A, E>;Defined in: entity.ts:622
What an aggregate's command returns: the decided events and the verified state.
Type Parameters
| Type Parameter |
|---|
A |
E |
DecisionKey
type DecisionKey = DecisionKeySrc;Defined in: entity.ts:642
Event
type Event<E> = E["__event"];Defined in: entity.ts:625
An aggregate's declared event union — for a repository, an outbox or an event store.
Type Parameters
| Type Parameter |
|---|
E extends object |
FieldSpec
type FieldSpec<T, F> = FieldSpecSrc<T, F>;Defined in: entity.ts:613
A schema plus its flags — what Entity.field(...) returns.
Type Parameters
| Type Parameter |
|---|
T extends z.core.$ZodType |
F extends Flags |
Input
type Input<E> = E["__input"];Defined in: entity.ts:598
What the wire sends — for mapper and request signatures.
Type Parameters
| Type Parameter |
|---|
E extends object |
Inspection
type Inspection<D> = InspectionSrc<D>;Defined in: entity.ts:619
What inspect returns: a stored row's plain data, and the invariants it breaks.
Type Parameters
| Type Parameter |
|---|
D |
Instance
type Instance<E> = E["__instance"];Defined in: entity.ts:689
The instance type of an entity or a union — one line that cannot drift out of step with the members, where a hand-written InstanceType<typeof A> | InstanceType<typeof B> silently could.
Type Parameters
| Type Parameter |
|---|
E extends object |
Invariant
type Invariant<D> = InvariantSrc<D>;Defined in: entity.ts:616
One whole-entity rule: the predicate, and what to say when it fails.
Type Parameters
| Type Parameter |
|---|
D |
MergedComputed
type MergedComputed<A, A2> = MergedComputedSrc<A, A2>;Defined in: entity.ts:659
A root's computed map merged with a variant's — what extend hands Static as its A.
Type Parameters
| Type Parameter |
|---|
A extends Schemas |
A2 extends Schemas |
MergedFields
type MergedFields<S, S2> = MergedFieldsSrc<S, S2>;Defined in: entity.ts:661
A root's field map merged with a variant's — what extend hands Static as its S.
Type Parameters
| Type Parameter |
|---|
S extends Fields |
S2 extends Fields |
Output
type Output<E> = E["__output"];Defined in: entity.ts:601
What the entity stores — for make and repository signatures.
Type Parameters
| Type Parameter |
|---|
E extends object |
Patch
type Patch<E> = E["__patch"];Defined in: entity.ts:607
What update accepts.
Type Parameters
| Type Parameter |
|---|
E extends object |
Sealed
type Sealed<D> = SealedSrc<D>;Defined in: entity.ts:657
Type Parameters
| Type Parameter |
|---|
D |
Static
type Static<Tag, S, A, B> = EntityStaticSrc<Tag, S, A, B>;Defined in: entity.ts:670
What Entity(tag)(fields, options) returns — the static surface itself.
Exported for the same reason as the four above: a consumer's emitted declarations have to name it, and the cost of them not being able to was two build failures rather than a verbose .d.ts. See index.ts.
Type Parameters
| Type Parameter | Default type |
|---|---|
Tag extends string | - |
S extends Fields | - |
A extends Schemas | - |
B | Record<never, never> |
Union
type Union<K, M> = EntityUnionSrc<K, M>;Defined in: entity.ts:651
What Entity.union(...) returns.
Type Parameters
| Type Parameter |
|---|
K extends string |
M extends readonly UnionMember[] |
Variables
abstract
abstract: <Name>(name) => <S, A>(fields, options?) => AbstractEntity<Name, S, A>;Defined in: entity.ts:534
Type Parameters
| Type Parameter |
|---|
Name extends string |
Parameters
| Parameter | Type |
|---|---|
name | Name |
Returns
<S, A>(fields, options?) => AbstractEntity<Name, S, A>
aggregate
aggregate: <Tag>(tag) => <S>(fields) => <Ev, O, A>(options) => AggregateStatic<Tag, S, A, Ev, O>;Defined in: entity.ts:535
Type Parameters
| Type Parameter |
|---|
Tag extends string |
Parameters
| Parameter | Type |
|---|---|
tag | Tag |
Returns
<S>(fields) => <Ev, O, A>(options) => AggregateStatic<Tag, S, A, Ev, O>
codeOf
codeOf: (issue) => string | undefined;Defined in: entity.ts:542
The domain code a failing Entity.invariant declared, or undefined.
Read from params.code, where construct puts it and where zod keeps a custom issue's metadata — so a field schema's own .refine(…, { params: { code } }) is read the same way. params is not part of Standard Schema's issue type, and anything may sit there, so this checks the shape instead of trusting it.
Parameters
| Parameter | Type |
|---|---|
issue | Issue |
Returns
string | undefined
computed
computed: <T, D>(schema, from) => ComputedField<T, D>;Defined in: entity.ts:530
Grouped under Entity rather than exported loose, and the package exports nothing else: computed and union are both too generic to take from a consumer's import scope — computed collides outright with Vue, MobX, Angular signals and Solid — and each reads as a sibling of whatever already holds that name when it is nothing of the sort. InvalidEntity would pass that test on its own, and is grouped anyway so the rule has no exceptions.
Declares one derived field:
computed: {
fullName: computed(FullName, (d) => `${d.first} ${d.last}`),
initials: computed(Initials, (d) => `${d.first[0]}${d.last[0]}`),
}from reads the declared fields and re-runs on every construction, so a derived value cannot go stale against its sources. D is fixed by the expected type at the call site, so d needs no annotation, and the return type is checked against this field's schema — a wrong type reports on the field that produced it rather than on the whole map.
A deriver may call the entity's own statics, given an explicit return annotation — (d): boolean => Doc.isActive(d.tags). The unannotated form is TS2506: the options object sits in the extends clause, and inferring the arrow's return resolves the class mid-declaration; the annotation preempts that, and is still checked against both the body and the schema. Pinned in computed.test-d.ts.
Type Parameters
| Type Parameter |
|---|
T extends $ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>> |
D |
Parameters
| Parameter | Type |
|---|---|
schema | T & T extends object ? T : IsNominalField<output<SchemaOf<T>>> extends true ? T : DomainFieldMustBeBrandedOrAnEntity |
from | (d) => input<T> |
Returns
ComputedField<T, D>
field
field: <T, F>(schema, flags) => FieldSpec<T, object & F extends object ? object : unknown & F extends object ? object : unknown>;Defined in: entity.ts:531
Declares a field with modifiers, public as Entity.field:
id: Entity.field(OrgId, { identity: true, generated: true }),generated drops the key from createInput and hands it to a factory generator; immutable drops it from updateInput so update refuses it.
identity makes the field part of the entity's business identity, which sameIdentityAs compares (#38). Several flagged fields form a composite identity. It implies immutable — an entity cannot update itself into a different one — and the value must be a required primitive, since identity is compared with Object.is.
unbranded exempts this one field from the rule that every field be branded, an entity or a narrow literal — for a descriptive leaf (a label, a free-text note) with no second value it could be confused with, whose brand would only leak into every consumer of the derived schemas (#73). It is a deliberate, visible opt-out per field, never a default.
The flags argument is required — the function exists to flag; an empty object is legal and does nothing.
Type Parameters
| Type Parameter |
|---|
T extends $ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>> |
F extends Partial<Flags & object> |
Parameters
| Parameter | Type |
|---|---|
schema | T |
flags | F & Record<Exclude<keyof F, keyof Flags | "identity" | "unbranded">, UnknownFlagIsRejected> & { readonly [K in "generated" | "immutable" | "identity" | "unbranded"]: RejectWidenedBoolean<F[K]> } & F extends object ? output<T> extends string | number | bigint | boolean ? unknown : object : unknown |
Returns
FieldSpec<T, object & F extends object ? object : unknown & F extends object ? object : unknown>
InvalidEntity
InvalidEntity: typeof InvalidEntity;Defined in: entity.ts:536
InvalidEntity
InvalidEntity: any;invariant
invariant: <D>(rule) => Invariant<D>;Defined in: entity.ts:532
Declares one rule spanning the whole entity:
invariants: [
invariant({
code: "NAME_TOO_LONG",
ensure: (d) => d.name.length <= 80,
message: "name must be at most 80 characters",
}),
invariant({
code: "ENDS_BEFORE_START",
ensure: (d) => d.endsAt > d.startsAt,
message: (d) => `endsAt must be after ${d.startsAt}`,
}),
]code is the rule's stable identity, and it is required: a caller keys behaviour off which rule failed — an HTTP error code, a field to highlight, a localised string — and a rule without one is exactly the gap that left adopters matching on message text (#70). The message may vary with the data; the code must not. It rides on the issue as params.code — zod's own slot for a custom issue's metadata, which zod carries through nested entities, arrays and unions with the path prefixed — and Entity.codeOf(issue) reads it back.
One object rather than positional arguments, so each part is named at the call site and a misspelled key is an excess-property error.
ensure returning true means valid — the rule reads as the assertion it makes, not as the failure it detects. D is fixed by the expected element type of the surrounding array, so d needs no annotation.
d is the declared fields, not the output: a rule cannot read a computed field. Every computed value is a function of the declared data, so any rule about one is expressible over its sources, and a computed value that fails its own schema is already a Defect rather than something to re-check here. Typing d as the output would also make it unusable — OutputOf<S, A> carries the deferred ComputedOf<A> conditional, and A is not yet resolved when this array is checked, so d would degrade to a bag of unknown.
A predicate calling the entity's own statics needs an explicit return annotation — ensure: (d): boolean => Doc.isActive(d.tags) — or the class resolves inside its own base expression (TS2506). Same idiom as computed; pinned in computed.test-d.ts.
message takes the data when the text depends on it. Every failing rule in the list reports, not just the first, and none carries a path: an invariant spans the entity, which is what distinguishes it from a field complaint.
A predicate that throws is a Defect rather than an InvalidEntity, on the same reasoning as computed — a rule is pure and total, so a violation is a bug in domain code rather than bad caller input.
Type Parameters
| Type Parameter |
|---|
D |
Parameters
| Parameter | Type |
|---|---|
rule | { code: string; ensure: (d) => boolean; message: string | ((d) => string); } |
rule.code | string |
rule.ensure | (d) => boolean |
rule.message | string | ((d) => string) |
Returns
Invariant<D>
keysOf
keysOf: (issue) => PropertyKey[];Defined in: entity.ts:543
A Standard Schema path as plain keys, which is what zod's addIssue wants.
Parameters
| Parameter | Type |
|---|---|
issue | Issue |
Returns
PropertyKey[]
renderIssue
renderIssue: (issue) => string;Defined in: entity.ts:544
Human-readable text for a defect message, which has nowhere to put structure.
Parameters
| Parameter | Type |
|---|---|
issue | Issue |
Returns
string
union
union: <K, M>(discriminant, members) => EntityUnion<K, M>;Defined in: entity.ts:533
A union of entities that is itself usable like one: it validates, it makes the right class, and it hands a contract layer plain schemas.
Returns a value, so a union is declared and named the way any other value is:
const Member = union("kind", [User, ServiceAccount]);
type Member = Entity.Instance<typeof Member>;
Member.make(row).getOrThrow(); // User | ServiceAccountEntity.Instance<typeof Member> is where the exact member union lives — there is no class body to hold statics or to narrow through, and nothing is ever constructed from Member itself.
discriminant names a declared domain field, not the entity's _tag. The tag is non-enumerable and absent after serialisation, so a union built on it could not survive a JSON round trip. The two mechanisms are not redundant: the field discriminates data, the tag matches an instance with P.tag(...).
input and output are real discriminated unions, so a contract layer gets one branch per member and JSON Schema in both directions.
Type Parameters
| Type Parameter |
|---|
K extends string |
M extends readonly [UnionMember, UnionMember, UnionMember] |
Parameters
| Parameter | Type |
|---|---|
discriminant | K |
members | M |
Returns
EntityUnion<K, M>
