import { type DomainObjectContract } from '../manipulation/getContract';
import { type WithImmute } from '../manipulation/immute/withImmute';
import type { DomainObjectShape } from './DomainObjectShape';
import { MARK_AS_DOMAIN_OBJECT } from './markers';
import { type SchemaOptions } from './validate/validate';
export type { DomainObjectShape } from './DomainObjectShape';
export { MARK_AS_DOMAIN_OBJECT } from './markers';
export interface DomainObjectInstantiationOptions {
    /**
     * allow callers to skip certain aspects of instantiation
     *
     * e.g., for performance optimizations in specific usecases
     */
    skip?: {
        /**
         * allow callers to skip schema validation
         *
         * usecase examples
         * - deserialize: schema validation increases deserialization time dramatically (10-100x) (especially if using Joi, that thing is slow!)
         */
        schema?: boolean;
    };
}
/**
 * Domain Object
 *
 * Responsibilities:
 * - optionally validate the properties that are passed into the constructor against the schema at runtime, if schema is supplied
 * - assign all properties that are passed into constructor to self, after optional runtime validation
 */
export declare class DomainObject<T extends DomainObjectShape> {
    constructor(props: T, options?: DomainObjectInstantiationOptions);
    /**
     * DomainObject marker symbol for cross-version compatibility.
     *
     * Uses Symbol.for() to create a global symbol that works across different versions
     * of the domain-objects library. The value is the version string.
     */
    static readonly [MARK_AS_DOMAIN_OBJECT]: string;
    /**
     * DomainObject.alias
     *
     * When set, declares the alias via which domain objects of this class are known to be referred to.
     *
     * For example
     * - PlantPot.alias = 'pot'
     * - Bird.alias = { singular: 'bird', plural: 'flock' }
     *
     * Relevance
     * - formalizes that we can expect to see the dobj referred to by this alias
     * - enables code generators to use this colloquial alias in contracts for better devex
     */
    static alias?: string | {
        plural?: string;
        singular?: string;
    };
    /**
     * DomainObject.schema
     *
     * When set, will be used to validate the properties passed into the constructor at runtime (i.e., during instantiation)
     *
     * Supports [`Zod`](https://github.com/colinhacks/zod), [`Joi`](https://github.com/sideway/joi), and [`Yup`](https://github.com/jquense/yup)
     */
    static schema?: SchemaOptions<any>;
    /**
     * DomainObject.contract
     *
     * returns the domain object's `schema` stamped with its identity + key metadata, as an `x-domain-object` pragma.
     *
     * whereas `schema` *validates* the data, `contract` *identifies* it: the contract is the schema that knows its
     * own name, primary/unique keys, alias, and nested dobj names. the stamp rides through `z.toJSONSchema()` so a
     * cross-service consumer can name, de-dupe, and reconstruct the dobj from the wire (no re-validation needed).
     *
     * requires a `static schema` that is a `Zod` schema; throws a `ConstraintError` otherwise.
     *
     * the returned contract also carries a `.ref(by)` method: `Seaturtle.contract.ref('primary')`
     * returns the schema-level *reference* to this dobj by key (an `x-domain-object-ref` pragma) —
     * a key-only slice for a field that references another dobj rather than composes it.
     *
     * @example
     * z.object({ surfboard: SeaturtleSurfboard.contract });          // composes the whole dobj
     * z.object({ rider: Seaturtle.contract.ref('primary') });        // references it by primary key
     */
    static get contract(): DomainObjectContract;
    /**
     * DomainObject.nested
     *
     * When set, will be used to hydrate nested DomainObjects. This is especially useful when instantiating nested domain objects from data stores (e.g., apis or databases). This is _required_ in order to safely use the `serialize` and `getUniqueIdentifier` functions - as those functions check static properties of domain objects to function correctly.
     *
     * For example:
     * ```ts
     * // define the plant pot
     * interface PlantPot {
     *   diameterInInches: number;
     * }
     * class PlantPot extends DomainObject<PlantPot> implements PlantPot {}
     *
     * // define the plant owner
     * interface PlantOwner {
     *   diameterInInches: number;
     * }
     * class PlantOwner extends DomainObject<PlantOwner> implements PlantOwner {}
     *
     * // define the plant
     * interface Plant {
     *   pot: PlantPot;
     *   owners: PlantOwner[];
     *   lastWatered: string;
     * }
     * class Plant extends DomainObject<Plant> implements Plant {
     *   public static nested = { pot: PlantPot, owners: PlantOwner }
     * }
     *
     * // and show that it hydrates the plant pot
     * const plant = new Plant({ pot: { diameterInInches: 7 }, owners: [{ name: 'bob' }], lastWatered: 'monday' }); // notice how the param of `pot` we're inputting is _not_ an instance of `PlantPot`, and `owners` are not instances of `PlantOwner`
     * expect(plant.pot).toBeInstanceOf(PlantPot); // now succeeds, as during instantiation we hydrated `pot` into an instance of `PlantPot`, due to `Plant.nested.pot` being set
     * plant.owners.forEach((owner) => expect(owner).toBeInstance(PlantOwner)); // also succeeds, since during instantiation we hydrated each `owner` into an instance of `PlantOwner`, due to `Plant.nested.owners` being set
     * ```
     */
    static nested?: Record<string, DomainObject<any> | DomainObject<any>[]>;
    /**
     * DomainObject.metadata
     *
     * When set, customizes the keys that are considered as metadata of this domain object.
     *
     * Context,
     * - domain objects are often persisted inside of storage mechanisms that assign metadata to them, such as ids or timestamps
     * - metadata is a special subset of readonly: attributes set by the persistence layer that describe the persistence of the object (not intrinsic attributes of the domain object itself)
     * - metadata is applicable to all domain objects (entities, events, and literals)
     *
     * Metadata vs Readonly,
     * - both metadata and readonly are set by the persistence layer
     * - metadata describes how the object is persisted (e.g., id, uuid, createdAt) - not the object itself
     * - readonly (non-metadata) describes intrinsic attributes of the object that the persistence layer sets (e.g., deployed ip address, deployed host, etc)
     *
     * Relevance,
     * - metadata properties do not contribute to the unique key of a DomainLiteral
     * - metadata properties can be easily stripped from an object by using the `omitMetadata` method
     * - all readonly properties (metadata + explicit readonly) can be stripped using `omitReadonly`
     *
     * By default,
     * - `id`, `createdAt`, `updatedAt`, and `effectiveAt` are considered metadata keys
     * - `uuid` is also considered a metadata key, if it is not included in the unique key of the DomainEntity or DomainEvent
     *
     * @see DomainEntity.readonly - for broader readonly attributes on entities (intrinsic attributes set by persistence layer)
     */
    static metadata: readonly string[];
    /**
     * .what = an interface via which to construct instances w/ immute operations
     *
     * .why =
     *   - immute operations such as .clone produce more maintainable code by preventing unexpected mutations
     *   - these immute operations provide a safe pit of success for common operations
     *
     * .note =
     *   - you can add withImmute to any dobj yourself, even if it wasn't built via this .build procedure
     *   - you can override the .build to add your own domain's getters, too
     */
    static build<TInstance extends DomainObjectShape>(this: new (props: TInstance, options?: DomainObjectInstantiationOptions) => TInstance, props: TInstance, options?: DomainObjectInstantiationOptions): WithImmute<TInstance>;
    /**
     * .as = alias for .build
     */
    static as: typeof DomainObject.build;
}
