Skip to content

@btravstack/entity


@btravstack/entity / Entity

Entity ​

Type Aliases ​

Abstract ​

ts
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 ​

ts
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 ​

ts
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 ​

ts
type BaseInstance<S, A> = BaseInstanceSrc<S, A>;

Defined in: entity.ts:655

Type Parameters ​

Type Parameter
S extends Fields
A extends Schemas

ComputedField ​

ts
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 ​

ts
type ConstructionKey = ConstructionKeySrc;

Defined in: entity.ts:656


CreateInput ​

ts
type CreateInput<E> = E["__createInput"];

Defined in: entity.ts:604

What create accepts from a caller.

Type Parameters ​

Type Parameter
E extends object

Decision ​

ts
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 ​

ts
type DecisionKey = DecisionKeySrc;

Defined in: entity.ts:642


Event ​

ts
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 ​

ts
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 ​

ts
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 ​

ts
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 ​

ts
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 ​

ts
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 ​

ts
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 ​

ts
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 ​

ts
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 ​

ts
type Patch<E> = E["__patch"];

Defined in: entity.ts:607

What update accepts.

Type Parameters ​

Type Parameter
E extends object

Sealed ​

ts
type Sealed<D> = SealedSrc<D>;

Defined in: entity.ts:657

Type Parameters ​

Type Parameter
D

Static ​

ts
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 ParameterDefault type
Tag extends string-
S extends Fields-
A extends Schemas-
BRecord<never, never>

Union ​

ts
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 ​

ts
abstract: <Name>(name) => <S, A>(fields, options?) => AbstractEntity<Name, S, A>;

Defined in: entity.ts:534

Type Parameters ​

Type Parameter
Name extends string

Parameters ​

ParameterType
nameName

Returns ​

<S, A>(fields, options?) => AbstractEntity<Name, S, A>


aggregate ​

ts
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 ​

ParameterType
tagTag

Returns ​

<S>(fields) => <Ev, O, A>(options) => AggregateStatic<Tag, S, A, Ev, O>


codeOf ​

ts
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 ​

ParameterType
issueIssue

Returns ​

string | undefined


computed ​

ts
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:

ts
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 ​

ParameterType
schemaT & T extends object ? T : IsNominalField<output<SchemaOf<T>>> extends true ? T : DomainFieldMustBeBrandedOrAnEntity
from(d) => input<T>

Returns ​

ComputedField<T, D>


field ​

ts
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:

ts
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 ​

ParameterType
schemaT
flagsF & 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 ​

ts
InvalidEntity: typeof InvalidEntity;

Defined in: entity.ts:536


InvalidEntity ​

ts
InvalidEntity: any;

invariant ​

ts
invariant: <D>(rule) => Invariant<D>;

Defined in: entity.ts:532

Declares one rule spanning the whole entity:

ts
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 ​

ParameterType
rule{ code: string; ensure: (d) => boolean; message: string | ((d) => string); }
rule.codestring
rule.ensure(d) => boolean
rule.messagestring | ((d) => string)

Returns ​

Invariant<D>


keysOf ​

ts
keysOf: (issue) => PropertyKey[];

Defined in: entity.ts:543

A Standard Schema path as plain keys, which is what zod's addIssue wants.

Parameters ​

ParameterType
issueIssue

Returns ​

PropertyKey[]


renderIssue ​

ts
renderIssue: (issue) => string;

Defined in: entity.ts:544

Human-readable text for a defect message, which has nowhere to put structure.

Parameters ​

ParameterType
issueIssue

Returns ​

string


union ​

ts
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:

ts
const Member = union("kind", [User, ServiceAccount]);
type Member = Entity.Instance<typeof Member>;

Member.make(row).getOrThrow(); // User | ServiceAccount

Entity.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 ​

ParameterType
discriminantK
membersM

Returns ​

EntityUnion<K, M>

Released under the MIT License.