import { PropertyValidatorError } from "./validation"; import { ClassType } from '@marcj/estdlib'; /** * Registry of all registered entity that used the @Entity('name') decorator. */ export declare const RegisteredEntities: { [name: string]: ClassType; }; export declare const MarshalGlobal: { unpopulatedCheckActive: boolean; }; export declare type Types = 'objectId' | 'uuid' | 'class' | 'union' | 'moment' | 'date' | 'string' | 'boolean' | 'Int8Array' | 'Uint8Array' | 'Uint8ClampedArray' | 'Int16Array' | 'Uint16Array' | 'Int32Array' | 'Uint32Array' | 'Float32Array' | 'Float64Array' | 'arrayBuffer' | 'number' | 'enum' | 'any'; /** * Type for @f.partial(). * * Differs to standard Partial<> in a way that it supports sub class fields using dot based paths (like mongoDB) */ export declare type PartialField = { [P in keyof T]?: T[P]; } & { [path: string]: any; }; export declare const typedArrayMap: Map; export declare const typedArrayNamesMap: Map; export declare function isTypedArray(type: Types): boolean; export interface PropertyValidator { validate(value: any, propertySchema: PropertyCompilerSchema): PropertyValidatorError | void; } export declare function isPropertyValidator(object: any): object is ClassType; declare type IndexOptions = Partial<{ unique: boolean; spatial: boolean; sparse: boolean; synchronize: boolean; fulltext: boolean; where: string; }>; export interface PropertySchemaSerialized { name: string; type: Types; isArray?: true; isMap?: true; isDecorated?: true; isParentReference?: true; isOptional?: true; isId?: true; isPartial?: true; typeSet?: true; allowLabelsAsValue?: true; methodName?: string; templateArgs?: PropertySchemaSerialized[]; classType?: string; } /** * Contains all resolved information from PropertySchema necessary to feed compiler functions. * * Internal note: It's on purpose aligned with PropertySchema. */ export declare class PropertyCompilerSchema { name: string; protected classType?: ClassType | ClassType[] | undefined; type: Types; isArray: boolean; isMap: boolean; /** * Whether given classType can be populated partially (for example in patch mechanisms). */ isPartial: boolean; isOptional: boolean; isDiscriminant: boolean; allowLabelsAsValue: boolean; isParentReference: boolean; validators: ClassType[]; /** * Whether its a owning reference. */ isReference: boolean; hasDefaultValue: boolean; defaultValue: any; constructor(name: string, classType?: ClassType | ClassType[] | undefined); get resolveClassType(): ClassType | undefined; get resolveUnionTypes(): ClassType[]; isActualOptional(): boolean; static createFromPropertySchema(propertySchema: PropertySchema, isArray?: boolean, isMap?: boolean, isPartial?: boolean): PropertyCompilerSchema; } /** * Represents a class property or method argument definition. */ export declare class PropertySchema extends PropertyCompilerSchema { /** * Whether its a back reference. */ backReference?: BackReferenceOptions; /** * Whether its a foreign key from a owning reference. */ isReferenceKey: boolean; index?: IndexOptions; /** * Used in decorator to check whether type has been set already. */ typeSet: boolean; /** * Whether this property is decorated. */ isDecorated: boolean; isId: boolean; /** * When this property belongs to method as argument then this contains the name of the method. */ methodName?: string; exclude?: 'all' | 'plain' | string; templateArgs?: PropertySchema[]; protected classTypeForwardRef?: ForwardedRef; protected classTypeResolved?: ClassType; protected unionTypes?: (ClassType | ForwardedRef)[]; protected unionResolved?: ClassType[]; constructor(name: string); setClassType(classType?: ClassType): void; toJSON(): PropertySchemaSerialized; static fromJSON(props: PropertySchemaSerialized): PropertySchema; static getTypeFromJSType(type: any): Types; setFromJSValue(value: any): void; setUnionTypes(types: (ClassType | ForwardedRef)[]): void; setFromJSType(type: any): void; /** * Internal note: for multi pk support, this will return a string[] in the future. */ getForeignKeyName(): string; /** * When this.classType is referenced, this returns the fields necessary to instantiate a proxy object. */ getRequiredForeignFieldsForProxy(): PropertySchema[]; getResolvedClassSchema(): ClassSchema; clone(): PropertySchema; getTemplateArg(position: number): PropertySchema | undefined; getForeignClassDecorator(): PropertySchema | undefined; get resolveClassType(): ClassType | undefined; get resolveUnionTypes(): ClassType[]; getResolvedClassTypeForValidType(): ClassType[] | ClassType | undefined; isResolvedClassTypeIsDecorated(): boolean; getResolvedUnionTypes(): ClassType[]; getResolvedClassType(): ClassType; } export interface EntityIndex { name: string; fields: string[]; options: IndexOptions; } export declare class ClassSchema { classType: ClassType; name?: string; collectionName?: string; databaseName?: string; /** * Name of the property which this class is decorating. * As soon as someone use this class, the actual value of this property is used to serialize. */ decorator?: string; /** * Name of the property that is a discriminant of this class. * This is automatically set when at least one property has @f.discriminant. */ discriminant?: string; /** * Each method can have its own PropertySchema definition for each argument, where map key = method name. */ protected methodProperties: Map; methods: { [name: string]: PropertySchema; }; /** * @internal */ protected initializedMethods: { [name: string]: true; }; classProperties: Map; idField?: keyof T & string; propertyNames: string[]; protected methodsParamNames: Map; protected methodsParamNamesAutoResolved: Map; indices: EntityIndex[]; /** * Contains all references, owning reference and back references. */ readonly references: Set; protected referenceInitialized: boolean; protected hasDefaultsInitialized: boolean; onLoad: { methodName: string; options: { fullLoad?: boolean; }; }[]; protected hasFullLoadHooksCheck: boolean; constructor(classType: ClassType); resetInitialized(): void; loadDefaults(): void; getClassName(): string; hasFullLoadHooks(): boolean; addIndex(name: string, options?: IndexOptions): void; clone(classType: ClassType): ClassSchema; /** * Adds dynamically new properties to the class schema definition. * Use the `f` decorator as you already do at the class directly. * * Note: Once a transform method is called like plainToClass/classToPlain etc * this method has no effect since compiler templates are then already built. * So make sure to call this addProperty() before calling transform methods. * * @example * ```typescript * const schema = getClassSchema(MyClass); * //or * const schema = createClassSchema(MyClass); * * schema.addProperty('anotherOne', f.type(String)); * ``` */ addProperty(name: string, decorator: FieldDecoratorResult): void; /** * Returns all annotated arguments as PropertSchema for given method name. */ getMethodProperties(name: string): PropertySchema[]; getMethod(name: string): PropertySchema; /** * Returns a perfect hash from the primary key(s). */ getPrimaryFieldRepresentation(item: T): any; /** * Internal note: for multi pk support, this will return a PropertySchema[] in the future. */ getPrimaryField(): PropertySchema; /** * Returns true if the method got a @f decorator. */ hasMethod(name: string): boolean; registerReference(property: PropertySchema): void; protected initializeMethod(name: string): void; /** * @internal */ getOrCreateMethodProperties(name: string): PropertySchema[]; /** * Before accessing `classProperties`, its necessary to call this method. */ initializeProperties(): void; getClassProperties(): Map; /** * @internal */ getMethodsParamNames(methodName: string): string[]; /** * @internal */ getMethodsParamNamesAutoResolved(methodName: string): string[]; getDiscriminantPropertySchema(): PropertySchema; getDecoratedPropertySchema(): PropertySchema; getIndex(name: string): EntityIndex | undefined; getPropertyOrUndefined(name: string): PropertySchema | undefined; hasProperty(name: string): boolean; /** * All references have a counter-part. This methods finds it and errors if not possible. * * If the given reference is a owning reference it finds the correct backReference, * which can be found by checking all reference options.mappedBy. * * If the given reference is a back reference it finds the owning reference, * which can be found by using its options.mappedBy. * * Alternatively we simply check for resolvedClassType to be given `classType`, and if only one * found, we return it. When more than one found, we throw an error saying the user he * should make its relation mapping not ambiguous. */ findReverseReference(toClassType: ClassType, fromReference: PropertySchema): PropertySchema; getProperty(name: string): PropertySchema; } /** * @hidden */ export declare const ClassSchemas: Map>; /** * @hidden */ export declare function getOrCreateEntitySchema(target: Object | ClassType): ClassSchema; /** * Returns meta information / schema about given entity class. */ export declare function getClassSchema(classTypeIn: ClassType | Object): ClassSchema; /** * Creates a new ClassSchema for a given external class (you might have no write access to), * which can be used to transform data for the given class. You can dynamically add properties * and use then the external class as usual with plainToClass, classToPlain, etc. * * @example * ```typescript * class ExternalClass { * id!: string; * version!: number; * lists!: number[]; * } * * const schema = createClassSchema(ExternalClass); * schema.addProperty('id', f.type(String)); * schema.addProperty('version', f.type(Number)); * schema.addProperty('lists', f.array(Number)); * * const obj = plainToClass(ExternalClass, { * id: '23', * version: 1, * lists: [12, 23] * }); * ``` */ export declare function createClassSchema(clazz?: ClassType, name?: string): ClassSchema; /** * Returns the ClassType for a given instance. */ export declare function getClassTypeFromInstance(target: T): ClassType; /** * Returns true when target is a class instance. */ export declare function isClassInstance(target: any): boolean; /** * Returns true if given class has an @Entity() or @f defined, and thus became * a Marshal entity. */ export declare function isRegisteredEntity(classType: ClassType): boolean; /** * Used to define a entity name for an entity. * * The name is used for an internal registry, so ot should be a unique one. * * Marshal's database abstraction uses this name to generate the collection name / table name. * * @category Decorator */ export declare function Entity(name: string, collectionName?: string): (target: ClassType) => void; /** * Used to define a database name for an entity. Per default marshal's database abstraction * uses the default database, but you can change that using this decorator. * * @category Decorator */ export declare function DatabaseName(name: string): (target: ClassType) => void; export interface BackReferenceOptions { /** * Necessary for normalised many-to-many relations. This defines the class of the pivot table/collection. */ via?: ClassType | ForwardRefFn>; /** * A reference/backReference can define which reference on the other side * reference back. This is necessary when multiple outgoing references * to the same */ mappedBy?: T extends ClassType ? keyof K : ''; } export declare function resolveClassTypeOrForward(type: ClassType | ForwardRefFn>): ClassType; export interface FieldDecoratorResult { (target: Object, property?: string, parameterIndexOrDescriptor?: any): void; /** * Sets the name of this property. Important for cases where the actual name is lost during compilation. */ asName(name: string): this; /** * Marks this field as owning reference to the foreign class. * * Its actual value is not written into the document itself, but stored * in its own collection/table and a reference is established using its primary field. * Without reference() field values are embedded into the main document. * * Owning reference means: Additional foreign key fields are automatically added if not already explicitly done. * Those additional fields are used to store the primary key of the foreign class. */ reference(): this; /** * Marks this reference as not-owning side. * * options.via: If the foreign class is not directly accessible, you can use a pivot collection/table * using the `via` option. Make sure that the given class in `via` contains both reference * (one back to this class and one to the actual foreign class). * * options.mappedBy: Explicitly set the name of the @f.reference() of the foreign class to which this backReference belongs to. * Per default it is automatically detected, but will fail if you the foreign class contains more * than one @f.reference() to this class. * @param options */ backReference(options?: BackReferenceOptions): this; /** * Marks this field as optional. The validation requires field values per default, this makes it optional. */ optional(): this; /** * Marks this field as discriminant for the discriminator in union types. * See @f.union() */ discriminant(): this; /** * Used to define a field as excluded when serialized from class to different targets (currently to Mongo or JSON). * PlainToClass or mongoToClass is not effected by this. */ exclude(t?: 'all' | 'database' | 'plain' | string): this; /** * Marks this field as an ID aka primary. * This is important if you interact with the database abstraction. * * Only one field in a class can be the ID. */ primary(): this; /** * @see primary * @deprecated */ asId(): this; /** * Defines template arguments of a tempalted class. Very handy for types like Observables. * * ```typescript * class Stuff { * } * * class Page { * @f.t(Stuff) * downloadStuff(): Observable { * return new Observable((observer) => { * observer.next(new Stuff()); * }) * } * * //or more verbose way if the type is more complex. * @f.t(f.type(Stuff).optional()) * downloadStuffWrapper(): Observable { * return new Observable((observer) => { * observer.next(new Stuff()); * }) * } * } * ``` */ template(...templateArgs: any[]): this; /** * Used to define an index on a field. */ index(options?: IndexOptions, name?: string): this; /** * Used to define a field as MongoDB ObjectId. This decorator is necessary if you want to use Mongo's _id. * * ```typescript * class Page { * @f.mongoId() * referenceToSomething?: string; * * constructor( * @f.id().mongoId() * public readonly _id: string * ) { * * } * } * ``` */ mongoId(): this; /** * Used to define a field as UUID (v4). */ uuid(): this; /** * Used to define a field as decorated. * This is necessary if you want to wrap a field value in the class instance using * a own class, like for example for Array manipulations, but keep the JSON and Database value * as primitive as possible. * * Only one field per class can be the decorated one. * * @category Decorator * * Example * ```typescript * export class PageCollection { * @f.forward(() => PageClass).decorated() * private readonly pages: PageClass[] = []; * * constructor(pages: PageClass[] = []) { * this.pages = pages; * } * * public count(): number { * return this.pages.length; * } * * public add(name: string): number { * return this.pages.push(new PageClass(name)); * } * } * * export class PageClass { * @f.uuid() * id: string = uuid(); * * @f * name: string; * * @f.forward(() => PageCollection) * children: PageCollection = new PageCollection; * * constructor(name: string) { * this.name = name; * } * } * ``` * * If you use classToPlain(PageClass, ...) or classToMongo(PageClass, ...) the field value of `children` will be the type of * `PageCollection.pages` (always the field where @Decorated() is applied to), here a array of PagesClass `PageClass[]`. */ decorated(): this; /** * Marks a field as array. You should prefer `@f.array(T)` syntax. * * ```typescript * class User { * @f.type(String).asArray() * tags: strings[] = []; * } * ``` */ asArray(): this; /** * @internal */ asPartial(): this; /** * Marks a field as map. You should prefer `@f.map(T)` syntax. * * ```typescript * class User { * @f.type(String).asMap() * tags: {[name: string]: string} = []; * } * ``` */ asMap(): this; /** * Uses an additional modifier to change the PropertySchema. */ use(decorator: (target: Object, property: PropertySchema) => void): this; /** * Adds a custom validator class or validator callback. * * @example * ```typescript * import {PropertyValidator, PropertyValidatorError} from '@marcj/marshal'; * * class MyCustomValidator implements PropertyValidator { * async validate(value: any, target: ClassType, propertyName: string): PropertyValidatorError | void { * if (value.length > 10) { * return new PropertyValidatorError('too_long', 'Too long :()'); * } * }; * } * * class Entity { * @f.validator(MyCustomValidator) * name: string; * * @f.validator(MyCustomValidator) * name: string; * * @f.validator((value: any, target: ClassType, propertyName: string) => { * if (value.length > 255) { * return new PropertyValidatorError('too_long', 'Too long :()'); * } * }) * title: string; * } * * ``` */ validator(validator: ClassType | ((value: any, target: ClassType, propertyName: string) => PropertyValidatorError | void)): this; } /** * Helper for decorators that are allowed to be placed in property declaration and constructor property declaration. * We detect the name by reading the constructor' signature, which would be otherwise lost. */ export declare function FieldDecoratorWrapper(cb: (target: Object, property: PropertySchema, returnType: any, modifiedOptions: FieldOptions) => void, root?: boolean): FieldDecoratorResult; /** * Used to define a field as a reference to a parent. * * @category Decorator * * Example one direction. * ```typescript * class JobConfig { * @f.forward(() => Job) //forward necessary since circular dependency * @ParentReference() * job: Job; * * } * * class Job { * @f config: JobConfig; * } * ``` * * Example circular parent-child setup. * ```typescript * export class PageClass { * @f.uuid() * id: string = uuid(); * * @f * name: string; * * @f.forwardArray(() => PageClass) //forward necessary since circular dependency * children: PageClass[] = []; * * @f.forward(() => PageClass).optional() //forward necessary since circular dependency * @ParentReference() * parent?: PageClass; * * constructor(name: string) { * this.name = name; * } * ``` * * todo: Move this to @f api. */ export declare function ParentReference(): FieldDecoratorResult; /** * Used to define a method as callback which will be called when the object has been completely serialized. * When fullLoad is true the callback is called when all references are loaded as well. This is particularly useful * when you have @ParentReference() properties, which will be undefined in regular OnLoad callback. * * Example * ```typescript * class User { * @OnLoad() * onLoad() { * console.log('self loaded!'); * } * * @OnLoad({fullLoad: true}) * onFullLoad() { * console.log('fully loaded, including parent references'); * } * } * * ``` * * @category Decorator */ export declare function OnLoad(options?: { fullLoad?: boolean; }): (target: T, property: string) => void; declare type FieldTypes = String | Number | Date | ClassType | ForwardedRef; declare type ForwardRefFn = () => T; declare class ForwardedRef { readonly forward: ForwardRefFn; constructor(forward: ForwardRefFn); } /** * Allows to refer to references which are not yet defined. * * For instance, if you reference a circular dependency or a not yet defined variable. * * ```typescript * class User { * @f.forward(() => Config) * config: Config; * * @f.forwardArray(() => Config) * configArray: Config[] = []; * * @f.forwardMap(() => Config) * configMap: {[k: string]: Config} = {}; * * ``` */ export declare function forwardRef(forward: ForwardRefFn): ForwardedRef; /** * @internal */ interface FieldOptions { template?: any[]; map?: boolean; array?: boolean; partial?: boolean; reference?: boolean; backReference?: BackReferenceOptions; } export interface MainDecorator { /** * Defines a type for a certain field. This is only necessary for custom classes * if the Typescript compiler does not include the reflection type in the build output. * * ```typescript * class User { * @f.type(MyClass) * tags: MyClass = new MyClass; * } * ``` */ type(type: T): FieldDecoratorResult; /** * Defines discriminated union types. * * ```typescript * class ConfigA { * @f.discriminator() * kind: string = 'a'; * * @f * myValue: string = ''; * } * * class ConfigB { * @f.discriminator() * kind: string = 'b'; * * @f * myValue2: string = ''; * } * * class User { * @f.types(ConfigA, ConfigB) * config: ConfigA | ConfigB = new ConfigA; * } * ``` */ union, K extends ForwardedRef>(...type: T[] | K[]): FieldDecoratorResult; /** * Marks a field as discriminant. This field MUST have a default value. * The default value is used to discriminate this class type when used in a union type. See @f.union. */ discriminant(): FieldDecoratorResult; /** * Marks a field as array. * * ```typescript * class User { * @f.array(String) * tags: string[] = []; * } * ``` */ array(...type: [T] | FieldTypes[]): FieldDecoratorResult; /** * Marks a field as enum. * * ```typescript * enum MyEnum { * low; * medium; * hight; * } * * class User { * @f.enum(MyEnum) * level: MyEnum = MyEnum.low; * } * ``` * * If allowLabelsAsValue is set, you can use the enum labels as well for setting the property value using plainToClass(). */ enum(type: T, allowLabelsAsValue?: boolean): FieldDecoratorResult; /** * Marks a field as partial of a class entity. It differs in a way to standard Partial<> that * it allows path based sub values, like you know from JSON patch. * * ```typescript * class Config { * @f.optional() * name?: string; * * @f.optional() * sub?: Config; * * @f * prio: number = 0; * } * * class User { * @f.partial(Config) * config: PartialField = {}; * } * ``` */ partial>(type: T): FieldDecoratorResult; /** * Marks a field as Moment.js value. Mongo and JSON transparent uses its toJSON() result. * In MongoDB its stored as Date. * * You have to install moment npm package in order to use it. */ moment(): FieldDecoratorResult; /** * Marks a field as type any. It does not transform the value and directly uses JSON.parse/stringify. */ any(): FieldDecoratorResult; /** * Marks a field as map. * * ```typescript * class User { * @f.map(String) * tags: {[k: string]: string}; * * @f.forwardMap(() => MyClass) * tags: {[k: string]: MyClass}; * } * ``` */ map(...type: [T] | FieldTypes[]): FieldDecoratorResult; /** * Forward references a type, required for circular reference. * * ```typescript * class User { * @f.forward(() => User)).optional() * parent: User; * } * ``` */ forward>(f: () => T): FieldDecoratorResult; /** * Forward references a type in an array, required for circular reference. * * ```typescript * class User { * @f.forwardArray(() => User)).optional() * parents: User[] = []; * } * ``` */ forwardArray>(f: () => T): FieldDecoratorResult; /** * Forward references a type in a map, required for circular reference. * * ```typescript * class User { * @f.forwardRef(() => User)).optional() * parents: {[name: string]: User} = {}}; * } * ``` */ forwardMap>(f: () => T): FieldDecoratorResult; /** * Marks a field as partial of a class entity. * * ```typescript * class Config { * @f.optional() * name?: string; * * @f * prio: number = 0; * } * * class User { * @f.forwardPartial(() => Config) * config: PartialField = {}; * } * ``` */ forwardPartial>(type: () => T): FieldDecoratorResult; } /** * This is the main decorator to define a properties on class or arguments on methods. * * ```typescript * class SubModel { * @f label: string; * } * * export enum Plan { * DEFAULT, * PRO, * ENTERPRISE, * } * * class SimpleModel { * @f.primary().uuid() * id: string = uuid(); * * @f.array(String) * tags: string[] = []; * * @f.type(ArrayBuffer).optional() //binary * picture?: ArrayBuffer; * * @f * type: number = 0; * * @f.enum(Plan) * plan: Plan = Plan.DEFAULT; * * @f * created: Date = new Date; * * @f.array(SubModel) * children: SubModel[] = []; * * @f.map(SubModel) * childrenMap: {[key: string]: SubModel} = {}; * * constructor( * @f.index().asName('name') //asName is required for minimized code * public name: string * ) {} * } * ``` * * @category Decorator */ export declare const f: MainDecorator & FieldDecoratorResult; /** * Alias for `f`. */ export declare const field: MainDecorator & FieldDecoratorResult; /** * Alias for `f`. */ export declare const type: MainDecorator & FieldDecoratorResult; /** * Used to define an index on a class. * * @category Decorator */ export declare function MultiIndex(fields: string[], options: IndexOptions, name?: string): (target: Object, property?: string | undefined, parameterIndexOrDescriptor?: any) => void; export {};