@aedart/contracts
Version:
The Ion contracts package. Contains types, interfaces and unique identifiers
775 lines (752 loc) • 24.1 kB
TypeScript
/**
* @aedart/contracts
*
* BSD-3-Clause, Copyright (c) 2023-present Alin Eugen Deac <aedart@gmail.com>.
*/
import { ConstructorLike } from '@aedart/contracts';
import { Throwable } from '@aedart/contracts/support/exceptions';
import { InjectionException as InjectionException$1 } from '@aedart/contracts/support/concerns';
/**
* Concern
*
* A "concern" is component that can be injected into a target class (concern owner).
* The concern itself is NOT responsible for performing the actual injection logic.
*
* @interface
*/
interface Concern {
/**
* The owner class instance this concern is injected into,
* or `this` concern instance if no owner was set.
*
* @readonly
*
* @type {object}
*/
get concernOwner(): object;
}
/**
* Concern Constructor
*
* @template T extends Concern
*
* @see Concern
*/
interface ConcernConstructor<T extends Concern = Concern> {
/**
* Creates a new concern instance
*
* @param {object} [owner] The owner class instance this concern is injected into.
* Defaults to `this` concern instance if none given.
*
* @throws {Error} When concern is unable to preform initialisation, e.g. caused
* by the owner or other circumstances.
*/
new (owner?: object): T;
/**
* Returns list of property keys that this concern class offers.
*
* **Note**: _Only properties and methods returned by this method can be aliased
* into a target class._
*
* @static
*
* @return {PropertyKey[]}
*/
[PROVIDES](): PropertyKey[];
}
/**
* An alias for a property or method in a {@link Concern} class
*/
type Alias = PropertyKey;
/**
* Key-value pair where the key corresponds to a property or method inside the {@link Concern} instance,
* and value is an "alias" for that property or method.
*
* In this context an "alias" means a property or method that is added onto a target
* class' prototype and acts as a proxy to the original property or method inside the
* concern class instance.
*/
type Aliases<T extends Concern = Concern> = {
[key in keyof T]: Alias;
} | {
[key: PropertyKey]: Alias;
};
/**
* Shorthand Concern Injection Configuration
*
* @see [Configuration]{@link import('@aedart/contracts/support/concerns').Configuration}
*/
type ShorthandConfiguration<T extends Concern = Concern> = [
ConcernConstructor<T>
] | [ConcernConstructor<T>, Aliases<T>] | [ConcernConstructor<T>, boolean];
/**
* Concern Injection Configuration
*
* Defines the Concern class that must be injected into a target class,
* along with other aspects of the injection, such as what aliases to use
* and what properties or methods should not be aliased.
*
* @template T extends Concern = Concern
*
* @see {Concern}
* @see {Aliases}
*/
interface Configuration<T extends Concern = Concern> {
/**
* The Concern Class that must be injected into a target class
*
* @template T extends Concern = Concern
*
* @type {ConcernConstructor<T>}
*/
concern: ConcernConstructor<T>;
/**
* Aliases for Concern's properties or methods.
*
* **Note**: _Defaults to same property and method names as "aliases", as those defined by
* a concern class' [PROVIDES]{@link import('@aedart/contracts/support/concerns').PROVIDES},
* if this property is empty or `undefined`._
*
* @template T extends Concern = Concern
*
* @type {Aliases<T>|undefined}
*/
aliases?: Aliases<T>;
/**
* Flag that indicates whether an "injector" is allowed to create
* "aliases" (proxy) properties and methods into a target class's prototype.
*
* **Note**: _Defaults to `true` if this property is `undefined`._
*
* **Note**: _If set to `false`, then {@link aliases} are ignored._
*
* @type {boolean}
*/
allowAliases?: boolean;
}
/**
* Concerns Owner
*
* An owner is an object, e.g. instance of a class, that offers a concerns {@link Container}.
*/
interface Owner {
/**
* The concerns container for this class
*
* @readonly
*
* @type {Container}
*/
readonly [CONCERNS]: Container;
}
/**
* Concerns Container
*/
interface Container {
/**
* The amount of concerns in this container
*
* @readonly
*
* @type {number}
*/
readonly size: number;
/**
* Get the concerns container owner
*
* @type {Owner}
*/
get owner(): Owner;
/**
* Determine if concern class is registered in this container
*
* @param {ConcernConstructor} concern
*
* @return {boolean}
*/
has(concern: ConcernConstructor): boolean;
/**
* Retrieve concern instance for given concern class
*
* **Note**: _If concern class is registered in this container, but not yet
* booted, then this method will boot it via the {@link boot} method, and return
* the resulting instance._
*
* @template T extends {@link Concern}
*
* @param {ConcernConstructor<T>} concern
*
* @return {T} The booted instance of the concern class. If concern class was
* previously booted, then that instance is returned.
*
* @throws {ConcernException}
*/
get<T extends Concern>(concern: ConcernConstructor<T>): T;
/**
* Determine if concern class has been booted
*
* @param {ConcernConstructor} concern
*
* @return {boolean}
*/
hasBooted(concern: ConcernConstructor): boolean;
/**
* Boot concern class
*
* @template T extends {@link Concern}
*
* @param {ConcernConstructor<T>} concern
*
* @return {T} New concern instance
*
* @throws {NotRegisteredException} If concern class is not registered in this container
* @throws {BootException} If concern is unable to be booted, e.g. if already booted
*/
boot<T extends Concern>(concern: ConcernConstructor<T>): T;
/**
* Boots all registered concern classes
*
* @throws {ConcernException}
*/
bootAll(): void;
/**
* Determine if this container is empty
*
* @return {boolean}
*/
isEmpty(): boolean;
/**
* Opposite of {@link isEmpty}
*
* @return {boolean}
*/
isNotEmpty(): boolean;
/**
* Returns all concern classes
*
* @return {IterableIterator<ConcernConstructor>}
*/
all(): IterableIterator<ConcernConstructor>;
/**
* Invoke a method with given arguments in concern instance
*
* @param {ConcernConstructor} concern
* @param {PropertyKey} method
* @param {...any} [args]
*
* @return {any}
*
* @throws {ConcernException}
* @throws {Error}
*/
call(concern: ConcernConstructor, method: PropertyKey, ...args: any[]): any;
/**
* Set the value of given property in concern instance
*
* @param {ConcernConstructor} concern
* @param {PropertyKey} property
* @param {any} value
*
* @throws {ConcernException}
* @throws {Error}
*/
setProperty(concern: ConcernConstructor, property: PropertyKey, value: any): void;
/**
* Get value of given property in concern instance
*
* @param {ConcernConstructor} concern
* @param {PropertyKey} property
*
* @return {any}
*
* @throws {ConcernException}
* @throws {Error}
*/
getProperty(concern: ConcernConstructor, property: PropertyKey): any;
}
/**
* Uses Concerns
*
* A target class that uses one or more concern classes.
*
* @template T = object
*/
interface UsesConcerns<T = object> {
/**
* Constructor
*
* @template T = object
*
* @param {...any} [args]
*
* @returns {ConstructorLike<T> & Owner}
*/
new (...args: any[]): ConstructorLike<T & Owner>;
/**
* A list of the concern classes to be used by this target class.
*
* **Note**: _If this class' parent class also uses concerns, then those
* concern classes are included recursively in this list, in the order
* that they have been registered._
*
* **Note**: _This property is usually automatically defined and populated
* by an {@link Injector}, and used to prevent duplicate concern injections._
*
* @static
*
* @type {ConcernConstructor[]}
*/
[CONCERN_CLASSES]: ConcernConstructor[];
/**
* List of aliases applied in this target class.
*
* **Note**: _This property is usually automatically defined and populated
* by an {@link Injector}, and used to prevent alias conflicts._
*
* @static
*
* @type {Alias[]}
*/
[ALIASES]: Alias[];
}
/**
* Descriptors Repository
*
* Utility for obtaining property descriptors for a target class or concern.
*/
interface DescriptorsRepository {
/**
* Returns property descriptors for given target class (recursively)
*
* @param {ConstructorLike | UsesConcerns | ConcernConstructor} target The target class, or concern class
* @param {boolean} [force=false] If `true` then method will not return evt. cached descriptors.
* @param {boolean} [cache=false] Caches the descriptors if `true`.
*
* @returns {Record<PropertyKey, PropertyDescriptor>}
*/
get(target: ConstructorLike | UsesConcerns | ConcernConstructor, force?: boolean, cache?: boolean): Record<PropertyKey, PropertyDescriptor>;
/**
* Caches property descriptors for target during the execution of callback.
*
* @param {ConstructorLike | UsesConcerns | ConcernConstructor} target The target class, or concern class
* @param {() => any} callback Callback to invoke
* @param {boolean} [forgetAfter=true] It `true`, cached descriptors are deleted after callback is invoked
*
* @return {any}
*/
rememberDuring(target: ConstructorLike | UsesConcerns | ConcernConstructor, callback: () => any, /* eslint-disable-line @typescript-eslint/no-explicit-any */ forgetAfter?: boolean): any;
/**
* Retrieves the property descriptors for given target and caches them
*
* @param {ConstructorLike | UsesConcerns | ConcernConstructor} target The target class, or concern class
* @param {boolean} [force=false] If `true` then evt. previous cached result is not used.
*
* @returns {Record<PropertyKey, PropertyDescriptor>}
*/
remember(target: ConstructorLike | UsesConcerns | ConcernConstructor, force?: boolean): Record<PropertyKey, PropertyDescriptor>;
/**
* Deletes cached descriptors for target
*
* @param {ConstructorLike | UsesConcerns | ConcernConstructor} target
*
* @return {boolean}
*/
forget(target: ConstructorLike | UsesConcerns | ConcernConstructor): boolean;
/**
* Clears all cached descriptors
*
* @return {this}
*/
clear(): this;
}
/**
* Concern Configuration Factory
*
* Able to make new {@link Configuration} from a given concern "entry".
*/
interface Factory {
/**
* Returns a new normalised concern configuration for given concern "entry"
*
* **Note**: _"normalised" in this context means:_
*
* _**A**: If a concern class is given, then a new concern configuration made._
*
* _**B**: If configuration is given, then a new concern configuration and given
* configuration is merged into the new configuration._
*
* _**C**: Configuration's `aliases` are automatically populated. When a concern
* configuration is provided, its evt. aliases merged with the default ones,
* unless `allowAliases` is set to `false`, in which case all aliases are removed._
*
* @param {object} target
* @param {ConcernConstructor | Configuration | ShorthandConfiguration} entry
*
* @returns {Configuration}
*
* @throws {InjectionException} If entry is unsupported or invalid
*/
make(target: object, entry: ConcernConstructor | Configuration | ShorthandConfiguration): Configuration;
}
/**
* Concerns Injector
*
* Able to inject concerns into a target class and create alias (proxy) properties
* and methods to the provided concerns' properties and methods, in the target class.
*
* @template T = object The target class that concern classes must be injected into
*
* @see {Concern}
*/
interface Injector<T = object> {
/**
* The target class
*
* @returns {T}
*/
get target(): T;
/**
* Injects concern classes into the target class and return the modified target.
*
* **Note**: _Method performs injection in the following way:_
*
* _**A**: Defines the concern classes in target class, via {@link defineConcerns}._
*
* _**B**: Defines a concerns container in target class' prototype, via {@link defineContainer}._
*
* _**C**: Defines "aliases" (proxy properties and methods) in target class' prototype, via {@link defineAliases}._
*
* @template T = object The target class that concern classes must be injected into
*
* @param {...ConcernConstructor | Configuration | ShorthandConfiguration} concerns List of concern classes / injection configurations
*
* @returns {UsesConcerns<T>} The modified target class
*
* @throws {InjectionException}
*/
inject(...concerns: (ConcernConstructor | Configuration | ShorthandConfiguration)[]): UsesConcerns<T>;
/**
* Defines the concern classes that must be used by the target class.
*
* **Note**: _Method changes the target class, such that it implements and respects the
* {@link UsesConcerns} interface._
*
* @template T = object
*
* @param {T} target The target class that must define the concern classes to be used
* @param {Constructor<Concern>[]} concerns List of concern classes
*
* @returns {UsesConcerns<T>} The modified target class
*
* @throws {AlreadyRegisteredException}
* @throws {InjectionException}
*/
defineConcerns<T = object>(target: T, concerns: ConcernConstructor[]): UsesConcerns<T>;
/**
* Defines a concerns {@link Container} in target class' prototype.
*
* **Note**: _Method changes the target class, such that it implements and respects the
* [Owner]{@link import('@aedart/contracts/support/concerns').Owner} interface!_
*
* @template T = object
*
* @param {UsesConcerns<T>} target The target in which a concerns container must be defined
*
* @returns {UsesConcerns<T>} The modified target class
*
* @throws {InjectionException} If unable to define concerns container in target class
*/
defineContainer<T = object>(target: UsesConcerns<T>): UsesConcerns<T>;
/**
* Defines "aliases" (proxy properties and methods) in target class' prototype, such that they
* point to the properties and methods available in the concern classes.
*
* **Note**: _Method defines each alias using the {@link defineAlias} method!_
*
* @template T = object
*
* @param {UsesConcerns<T>} target The target in which "aliases" must be defined in
* @param {Configuration[]} configurations List of concern injection configurations
*
* @returns {UsesConcerns<T>} The modified target class
*
* @throws {AliasConflictException} If case of alias naming conflicts.
* @throws {InjectionException} If unable to define aliases in target class.
*/
defineAliases<T = object>(target: UsesConcerns<T>, configurations: Configuration[]): UsesConcerns<T>;
/**
* Defines an "alias" (proxy property or method) in target class' prototype, which points to a property or method
* in the given concern.
*
* **Note**: _Method will do nothing, if a property or method already exists in the target class' prototype
* chain, with the same name as given "alias"._
*
* @template T = object
*
* @param {UsesConcerns<T>} target The target in which "alias" must be defined in
* @param {PropertyKey} alias Name of the "alias" in the target class (name of the proxy property or method)
* @param {PropertyKey} key Name of the property or method that the "alias" points to, in the concern class (`source`)
* @param {Constructor} source The source concern class that contains the property or methods that is pointed to (`key`)
*
* @returns {boolean} `true` if "alias" was in target class. `false` if a property or method already exists in the
* target, with the same name as the "alias".
*
* @throws {UnsafeAliasException} If an alias points to an "unsafe" property or method in the source concern class.
* @throws {InjectionException} If unable to define "alias" in target class.
*/
defineAlias<T = object>(target: UsesConcerns<T>, alias: PropertyKey, key: PropertyKey, source: ConcernConstructor): boolean;
}
/**
* Registration Aware
*
* Concern class is aware of when it is being registered by a target class
* and is able to performs pre-/post-registration logic.
*/
interface RegistrationAware {
/**
* Perform pre-registration logic.
*
* **Note**: _This hook method is intended to be invoked by an
* [Injector]{@link import('@aedart/contracts/support/concerns').Injector}, before
* the concern container and aliases are defined in the target class._
*
* @static
*
* @param {UsesConcerns} target Target class constructor
*
* @return {void}
*
* @throws {Error}
*/
[BEFORE](target: UsesConcerns): void;
/**
* Perform post-registration logic.
*
* **Note**: _This hook method is intended to be invoked by an
* [Injector]{@link import('@aedart/contracts/support/concerns').Injector}, after
* this concern class has been registered in a target class and aliases have been
* defined in target's prototype._
*
* @static
*
* @param {UsesConcerns} target Target class constructor, after concerns and aliases defined
*
* @return {void}
*
* @throws {Error}
*/
[AFTER](target: UsesConcerns): void;
}
/**
* Alias Descriptor Factory
*/
interface AliasDescriptorFactory {
/**
* Makes a property descriptor to be used for an "alias" (proxy) property or method
*
* @param {PropertyKey} key The property key in `source` concern
* @param {ConcernConstructor} source The concern that holds the property key
* @param {PropertyDescriptor} keyDescriptor Descriptor of `key` in `source` concern
*
* @returns {PropertyDescriptor} Descriptor to be used for defining alias in a target class
*/
make(key: PropertyKey, source: ConcernConstructor, keyDescriptor: PropertyDescriptor): PropertyDescriptor;
}
/**
* Concern Exception
*
* To be thrown when a concern class is the cause of an error.
*/
interface ConcernException extends Throwable {
/**
* The Concern class that caused this error or exception
*
* @readonly
*
* @type {ConcernConstructor | null}
*/
readonly concern: ConcernConstructor | null;
}
/**
* Concern Injection Exception
*
* To be thrown when a concern class cannot be injected into a target class.
*/
interface InjectionException extends ConcernException {
/**
* The target class
*
* @readonly
*
* @type {ConstructorLike | UsesConcerns}
*/
readonly target: ConstructorLike | UsesConcerns;
}
/**
* Alias Conflict Exception
*
* To be thrown if an alias conflicts with another alias.
*/
interface AliasConflictException extends InjectionException {
/**
* The requested alias that conflicts with another alias
* of the same name.
*
* @readonly
*
* @type {Alias}
*/
readonly alias: Alias;
/**
* the property key that the conflicting alias points to
*
* @readonly
*
* @type {Alias}
*/
readonly key: PropertyKey;
/**
* The source class (e.g. parent class) that defines that originally defined the alias
*
* @readonly
*
* @type {ConstructorLike | UsesConcerns}
*/
readonly source: ConstructorLike | UsesConcerns;
}
/**
* Already Registered Exception
*
* To be thrown when a concern class is attempted registered in a target, but
* was previously registered in the target or target's parent.
*/
interface AlreadyRegisteredException extends InjectionException$1 {
/**
* The source, e.g. a parent class, in which a concern class
* was already registered.
*
* @readonly
*
* @type {ConstructorLike | UsesConcerns}
*/
readonly source: ConstructorLike | UsesConcerns;
}
/**
* Concern Boot Exception
*
* To be thrown when a concern class is unable to boot.
*
* @see ConcernException
*/
interface BootException extends ConcernException {
}
/**
* Concern Not Registered Exception
*
* To be thrown when a given concern is expected registered in a [Container]{@link import('@aedart/contracts/support/concerns').Container},
* but isn't.
*
* @see ConcernException
*/
interface NotRegisteredException extends ConcernException {
}
/**
* Unsafe Alias Exception
*
* To be thrown when an alias points to an "unsafe" property or method inside a concern.
*/
interface UnsafeAliasException extends InjectionException {
/**
* The alias that points to an "unsafe" property or method
*
* @readonly
*
* @type {PropertyKey}
*/
readonly alias: PropertyKey;
/**
* The "unsafe" property or method that an alias points to
*
* @readonly
*
* @type {PropertyKey}
*/
readonly key: PropertyKey;
}
/**
* Support Concerns identifier
*
* @type {Symbol}
*/
declare const SUPPORT_CONCERNS: unique symbol;
/**
* Symbol used by a [concern class]{@link ConcernConstructor} to indicate what properties
* and methods can be aliased into a target class.
*
* **Note**: _Symbol MUST be used to as name for a "static" method in the desired Concern class._
*
* **Example**:
* ```ts
* class MyConcern implements Concern
* {
* static [PROVIDES](): PropertyKey[]
* {
* // ...not shown...
* }
*
* // ...remaining not shown...
* }
* ```
*
* @see ConcernConstructor
*
* @type {symbol}
*/
declare const PROVIDES: unique symbol;
/**
* Symbol that can be used by a [concern class]{@link ConcernConstructor} to perform
* pre-registration logic
*
* @see RegistrationAware
* @see ConcernConstructor
*
* @type {symbol}
*/
declare const BEFORE: unique symbol;
/**
* Symbol that can be used by a [concern class]{@link ConcernConstructor} to perform
* post-registration logic.
*
* @see RegistrationAware
* @see ConcernConstructor
*
* @type {symbol}
*/
declare const AFTER: unique symbol;
/**
* Symbol used to define a list of the concern classes to be used by a target class.
*
* @see {UsesConcerns}
*
* @type {Symbol}
*/
declare const CONCERN_CLASSES: unique symbol;
/**
* Symbol used to list the aliases applied in a target class
*
* @see {UsesConcerns}
*
* @type {Symbol}
*/
declare const ALIASES: unique symbol;
/**
* Symbol used to define a "concerns container" property inside a target class' prototype
*
* @see {Owner}
* @see {Container}
*
* @type {Symbol}
*/
declare const CONCERNS: unique symbol;
export { AFTER, ALIASES, type Alias, type AliasConflictException, type AliasDescriptorFactory, type Aliases, type AlreadyRegisteredException, BEFORE, type BootException, CONCERNS, CONCERN_CLASSES, type Concern, type ConcernConstructor, type ConcernException, type Configuration, type Container, type DescriptorsRepository, type Factory, type InjectionException, type Injector, type NotRegisteredException, type Owner, PROVIDES, type RegistrationAware, SUPPORT_CONCERNS, type ShorthandConfiguration, type UnsafeAliasException, type UsesConcerns };