import type { StandardSchemaV1 } from "@standard-schema/spec"; /** * Any Standard Schema compatible validator. */ export type StandardSchema = StandardSchemaV1; /** * Infer the parsed output type from a Standard Schema. */ export type InferOutput = StandardSchemaV1.InferOutput; type AnyMethods = Record; /** * Base entity instance passed to the `.methods(...)` builder: validated props * plus the `with` and `toJSON` helpers, before domain methods attach. * * `with(...)` is typed as returning the base instance here because the final * methods object is still being defined while the builder runs. At runtime * the returned instance carries the entity's methods, and `EntityInstance` * maps base-instance returns back to the full instance type so consumers keep * typed method chaining. */ export type EntityBaseInstance = InferOutput & { /** * Create a new validated entity instance with patched properties. */ with(patch: Partial>): EntityBaseInstance | Promise>; /** * Convert the entity to a plain JSON object for persistence. */ toJSON(): InferOutput; }; /** * Replace base-instance returns with the full entity instance type, unwrapping * promises, so methods that return `self` or `self.with(...)` chain with the * entity's methods attached. */ type WithEntityInstance = R extends Promise ? Promise> : R extends EntityBaseInstance ? Instance : R; /** * Entity instance with validated props, attached methods, and immutable update * helpers. Method returns that are typed as the base instance (`self` or * `self.with(...)`) are mapped to the full instance type so chaining keeps * the entity's methods. */ export type EntityInstance = InferOutput & { /** * Create a new validated entity instance with patched properties. */ with(patch: Partial>): EntityInstance | Promise>; /** * Convert the entity to a plain JSON object for persistence. */ toJSON(): InferOutput; } & { [K in keyof Methods]: Methods[K] extends (...args: infer A) => infer R ? (...args: A) => WithEntityInstance, Schema> : Methods[K]; }; /** * Entity definition returned by `defineEntity(...).build()`. */ export interface EntityDef { /** Name used for debugging and introspection. */ name: Name; /** Standard Schema used to validate entity props. */ schema: Schema; /** * Create a new frozen entity instance from props. * * Returns a promise when the underlying Standard Schema validates async. */ create(props: InferOutput): EntityInstance | Promise>; /** * Reconstruct an entity instance from JSON, usually from persistence. */ fromJSON(json: InferOutput): EntityInstance | Promise>; /** Type-only alias for the entity instance type. Undefined at runtime. */ Type: EntityInstance; } /** * Builder class for creating Entities/Aggregates. */ declare class EntityBuilder { private readonly cfg; constructor(cfg: { name: Name; schema?: Schema; methods?: (self: EntityBaseInstance) => Methods; }); /** * Define the schema for this entity using any Standard Schema compatible validator. */ props(schema: S): EntityBuilder; /** * Define methods to attach to entity instances. * The method builder receives the typed base instance: validated props plus * `with` and `toJSON`. */ methods(build: (self: EntityBaseInstance) => M): EntityBuilder; /** * Finalize and build the entity definition. */ build(): EntityDef; } /** * Create a new entity builder. * * Entities validate props, attach domain methods, and return frozen immutable * instances. `with(...)` revalidates the merged props. `.Type` is type-only and * should be used with `typeof Entity.Type`. * * @example * ```ts * const Todo = defineEntity("Todo") * .props(z.object({ * id: z.string(), * title: z.string(), * assigneeIds: z.array(z.string()).default([]), * })) * .methods((self) => ({ * addAssignee(id: string) { * if (self.assigneeIds.includes(id)) return self; * return self.with({ assigneeIds: [...self.assigneeIds, id] }); * }, * })) * .build(); * * type Todo = typeof Todo.Type; * ``` */ export declare function defineEntity(name: Name): EntityBuilder>; export {}; //# sourceMappingURL=entity.d.ts.map