Models rewrite with new API for better typings and extensibility.
This commit is contained in:
		
							parent
							
								
									498d25a909
								
							
						
					
					
						commit
						e43e27e2e1
					
				
					 17 changed files with 430 additions and 386 deletions
				
			
		|  | @ -1,6 +1,6 @@ | ||||||
| { | { | ||||||
| 	"name": "@sharkitek/core", | 	"name": "@sharkitek/core", | ||||||
| 	"version": "2.1.3", | 	"version": "3.0.0", | ||||||
| 	"description": "Sharkitek core models library.", | 	"description": "Sharkitek core models library.", | ||||||
| 	"keywords": [ | 	"keywords": [ | ||||||
| 		"sharkitek", | 		"sharkitek", | ||||||
|  |  | ||||||
|  | @ -1,34 +0,0 @@ | ||||||
| import {Type} from "./Types/Type"; |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Options of a definition. |  | ||||||
|  */ |  | ||||||
| export interface DefinitionOptions<SerializedType, SharkitekType> |  | ||||||
| { //TODO implement some options, like `mandatory`.
 |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * A Sharkitek model property definition. |  | ||||||
|  */ |  | ||||||
| export class Definition<SerializedType, SharkitekType> |  | ||||||
| { |  | ||||||
| 	/** |  | ||||||
| 	 * Initialize a property definition with the given type and options. |  | ||||||
| 	 * @param type - The model property type. |  | ||||||
| 	 * @param options - Property definition options. |  | ||||||
| 	 */ |  | ||||||
| 	constructor( |  | ||||||
| 		public type: Type<SerializedType, SharkitekType>, |  | ||||||
| 		public options: DefinitionOptions<SerializedType, SharkitekType> = {}, |  | ||||||
| 	) {} |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| /** |  | ||||||
|  * Initialize a property definition with the given type and options. |  | ||||||
|  * @param type - The model property type. |  | ||||||
|  * @param options - Property definition options. |  | ||||||
|  */ |  | ||||||
| export function SDefine<SerializedType, SharkitekType>(type: Type<SerializedType, SharkitekType>, options: DefinitionOptions<SerializedType, SharkitekType> = {}): Definition<SerializedType, SharkitekType> |  | ||||||
| { |  | ||||||
| 	return new Definition<SerializedType, SharkitekType>(type, options); |  | ||||||
| } |  | ||||||
|  | @ -1,85 +1,123 @@ | ||||||
| import {Definition} from "./Definition"; | import {Definition} from "./PropertyDefinition"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Model properties definition type. |  * Type definition of a model constructor. | ||||||
|  */ |  */ | ||||||
| export type ModelDefinition<T> = Partial<Record<keyof T, Definition<unknown, unknown>>>; | export type ConstructorOf<T extends object> = { new(): T; }; | ||||||
| /** |  | ||||||
|  * Model identifier type. |  | ||||||
|  */ |  | ||||||
| export type ModelIdentifier<T> = keyof T; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * A Sharkitek model. |  * Unknown property definition. | ||||||
|  */ |  */ | ||||||
| export abstract class Model<THIS> | export type UnknownDefinition = Definition<unknown, unknown>; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * A model shape. | ||||||
|  |  */ | ||||||
|  | export type ModelShape = Record<string, UnknownDefinition>; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Properties of a model based on its shape. | ||||||
|  |  */ | ||||||
|  | export type PropertiesModel<Shape extends ModelShape> = { | ||||||
|  | 	[k in keyof Shape]: Shape[k]["_sharkitek"]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Serialized object type based on model shape. | ||||||
|  |  */ | ||||||
|  | export type SerializedModel<Shape extends ModelShape> = { | ||||||
|  | 	[k in keyof Shape]: Shape[k]["_serialized"]; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Type of a model object. | ||||||
|  |  */ | ||||||
|  | export type Model<Shape extends ModelShape, IdentifierType = unknown> = ModelDefinition<Shape, IdentifierType> & PropertiesModel<Shape>; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Identifier type. | ||||||
|  |  */ | ||||||
|  | export type IdentifierType<Shape extends ModelShape, K extends keyof Shape> = Shape[K]["_sharkitek"]; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Interface of a Sharkitek model definition. | ||||||
|  |  */ | ||||||
|  | export interface ModelDefinition<Shape extends ModelShape, IdentifierType> | ||||||
| { | { | ||||||
| 	/** | 	/** | ||||||
| 	 * Model properties definition function. | 	 * Get model identifier. | ||||||
| 	 */ | 	 */ | ||||||
| 	protected abstract SDefinition(): ModelDefinition<THIS>; | 	getIdentifier(): IdentifierType; | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Return the name of the model identifier property. | 	 * Serialize the model. | ||||||
| 	 */ | 	 */ | ||||||
| 	protected SIdentifier(): ModelIdentifier<THIS> | 	serialize(): SerializedModel<Shape>; | ||||||
| 	{ | 	/** | ||||||
| 		return undefined; | 	 * Deserialize the model. | ||||||
| 	} | 	 * @param obj Serialized object. | ||||||
|  | 	 */ | ||||||
|  | 	deserialize(obj: SerializedModel<Shape>): Model<Shape, IdentifierType>; | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Get given property definition. | 	 * Find out if the model is new (never deserialized) or not. | ||||||
| 	 * @protected |  | ||||||
| 	 */ | 	 */ | ||||||
| 	protected getPropertyDefinition(propertyName: string): Definition<unknown, unknown> | 	isNew(): boolean; | ||||||
| 	{ | 	/** | ||||||
| 		return (this.SDefinition() as any)?.[propertyName]; | 	 * Find out if the model is dirty or not. | ||||||
| 	} | 	 */ | ||||||
|  | 	isDirty(): boolean; | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Get the list of the model properties. | 	 * Serialize the difference between current model state and the original one. | ||||||
| 	 * @protected |  | ||||||
| 	 */ | 	 */ | ||||||
| 	protected getProperties(): string[] | 	serializeDiff(): Partial<SerializedModel<Shape>>; | ||||||
|  | 	/** | ||||||
|  | 	 * Set current properties values as original values. | ||||||
|  | 	 */ | ||||||
|  | 	resetDiff(): void; | ||||||
|  | 	/** | ||||||
|  | 	 * Get difference between original values and current ones, then reset it. | ||||||
|  | 	 * Similar to call `serializeDiff()` then `resetDiff()`. | ||||||
|  | 	 */ | ||||||
|  | 	save(): Partial<SerializedModel<Shape>>; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Define a Sharkitek model. | ||||||
|  |  * @param shape Model shape definition. | ||||||
|  |  * @param identifier Identifier property name. | ||||||
|  |  */ | ||||||
|  | export function model<Shape extends ModelShape, Identifier extends keyof Shape = any>( | ||||||
|  | 	shape: Shape, | ||||||
|  | 	identifier?: Identifier, | ||||||
|  | ): ConstructorOf<Model<Shape, IdentifierType<Shape, Identifier>>> | ||||||
|  | { | ||||||
|  | 	// Get shape entries.
 | ||||||
|  | 	const shapeEntries = Object.entries(shape) as [keyof Shape, UnknownDefinition][]; | ||||||
|  | 
 | ||||||
|  | 	return class GenericModel implements ModelDefinition<Shape, IdentifierType<Shape, Identifier>> | ||||||
| 	{ | 	{ | ||||||
| 		return Object.keys(this.SDefinition()); | 		constructor() | ||||||
|  | 		{ | ||||||
|  | 			// Initialize properties to undefined.
 | ||||||
|  | 			Object.assign(this, | ||||||
|  | 				// Build empty properties model from shape entries.
 | ||||||
|  | 				Object.fromEntries(shapeEntries.map(([key]) => [key, undefined])) as PropertiesModel<Shape> | ||||||
|  | 			); | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Calling a function for a defined property. |  | ||||||
| 	 * @param propertyName - The property for which to check definition. |  | ||||||
| 	 * @param callback - The function called when the property is defined. |  | ||||||
| 	 * @param notProperty - The function called when the property is not defined. |  | ||||||
| 	 * @protected |  | ||||||
| 	 */ |  | ||||||
| 	protected propertyWithDefinition(propertyName: string, callback: (propertyDefinition: Definition<unknown, unknown>) => void, notProperty: () => void = () => {}): unknown |  | ||||||
| 	{ |  | ||||||
| 		// Getting the current property definition.
 |  | ||||||
| 		const propertyDefinition = this.getPropertyDefinition(propertyName); |  | ||||||
| 		if (propertyDefinition) |  | ||||||
| 			// There is a definition for the current property, calling the right callback.
 |  | ||||||
| 			return callback(propertyDefinition); |  | ||||||
| 		else |  | ||||||
| 			// No definition for the given property, calling the right callback.
 |  | ||||||
| 			return notProperty(); |  | ||||||
| 	} |  | ||||||
| 		/** | 		/** | ||||||
| 		 * Calling a function for each defined property. | 		 * Calling a function for each defined property. | ||||||
| 		 * @param callback - The function to call. | 		 * @param callback - The function to call. | ||||||
| 		 * @protected | 		 * @protected | ||||||
| 		 */ | 		 */ | ||||||
| 	protected forEachModelProperty(callback: (propertyName: string, propertyDefinition: Definition<unknown, unknown>) => unknown): any|void | 		protected forEachModelProperty<ReturnType>(callback: (propertyName: keyof Shape, propertyDefinition: UnknownDefinition) => ReturnType): ReturnType | ||||||
| 		{ | 		{ | ||||||
| 		for (const propertyName of this.getProperties()) | 			for (const [propertyName, propertyDefinition] of shapeEntries) | ||||||
| 			{ // For each property, checking that its type is defined and calling the callback with its type.
 | 			{ // For each property, checking that its type is defined and calling the callback with its type.
 | ||||||
| 			const result = this.propertyWithDefinition(propertyName, (propertyDefinition) => { |  | ||||||
| 				// If the property is defined, calling the function with the property name and definition.
 | 				// If the property is defined, calling the function with the property name and definition.
 | ||||||
| 				const result = callback(propertyName, propertyDefinition); | 				const result = callback(propertyName, propertyDefinition); | ||||||
| 
 |  | ||||||
| 				// If there is a return value, returning it directly (loop is broken).
 |  | ||||||
| 				if (typeof result !== "undefined") return result; |  | ||||||
| 			}); |  | ||||||
| 
 |  | ||||||
| 				// If there is a return value, returning it directly (loop is broken).
 | 				// If there is a return value, returning it directly (loop is broken).
 | ||||||
| 				if (typeof result !== "undefined") return result; | 				if (typeof result !== "undefined") return result; | ||||||
| 			} | 			} | ||||||
|  | @ -90,30 +128,60 @@ export abstract class Model<THIS> | ||||||
| 		 * The original properties values. | 		 * The original properties values. | ||||||
| 		 * @protected | 		 * @protected | ||||||
| 		 */ | 		 */ | ||||||
| 	protected _originalProperties: Record<string, any> = {}; | 		protected _originalProperties: Partial<PropertiesModel<Shape>> = {}; | ||||||
| 
 | 
 | ||||||
| 		/** | 		/** | ||||||
| 		 * The original (serialized) object. | 		 * The original (serialized) object. | ||||||
| 		 * @protected | 		 * @protected | ||||||
| 		 */ | 		 */ | ||||||
| 	protected _originalObject: any = null; | 		protected _originalObject: SerializedModel<Shape>|null = null; | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 		getIdentifier(): IdentifierType<Shape, Identifier> | ||||||
|  | 		{ | ||||||
|  | 			return (this as PropertiesModel<Shape>)?.[identifier]; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		serialize(): SerializedModel<Shape> | ||||||
|  | 		{ | ||||||
|  | 			// Creating an empty (=> partial) serialized object.
 | ||||||
|  | 			const serializedObject: Partial<SerializedModel<Shape>> = {}; | ||||||
|  | 
 | ||||||
|  | 			this.forEachModelProperty((propertyName, propertyDefinition) => { | ||||||
|  | 				// For each defined model property, adding it to the serialized object.
 | ||||||
|  | 				serializedObject[propertyName] = propertyDefinition.type.serialize((this as PropertiesModel<Shape>)?.[propertyName]); | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			return serializedObject as SerializedModel<Shape>; // Returning the serialized object.
 | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		deserialize(obj: SerializedModel<Shape>): Model<Shape, IdentifierType<Shape, Identifier>> | ||||||
|  | 		{ | ||||||
|  | 			this.forEachModelProperty((propertyName, propertyDefinition) => { | ||||||
|  | 				// For each defined model property, assigning its deserialized value.
 | ||||||
|  | 				(this as PropertiesModel<Shape>)[propertyName] = propertyDefinition.type.deserialize(obj[propertyName]); | ||||||
|  | 			}); | ||||||
|  | 
 | ||||||
|  | 			// Reset original property values.
 | ||||||
|  | 			this.resetDiff(); | ||||||
|  | 
 | ||||||
|  | 			this._originalObject = obj; // The model is not a new one, but loaded from a deserialized one. Storing it.
 | ||||||
|  | 
 | ||||||
|  | 			return this as Model<Shape, IdentifierType<Shape, Identifier>>; | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Determine if the model is new or not. |  | ||||||
| 	 */ |  | ||||||
| 		isNew(): boolean | 		isNew(): boolean | ||||||
| 		{ | 		{ | ||||||
| 			return !this._originalObject; | 			return !this._originalObject; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Determine if the model is dirty or not. |  | ||||||
| 	 */ |  | ||||||
| 		isDirty(): boolean | 		isDirty(): boolean | ||||||
| 		{ | 		{ | ||||||
| 			return this.forEachModelProperty((propertyName, propertyDefinition) => ( | 			return this.forEachModelProperty((propertyName, propertyDefinition) => ( | ||||||
| 				// For each property, checking if it is different.
 | 				// For each property, checking if it is different.
 | ||||||
| 			propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as any)[propertyName]) | 				propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as PropertiesModel<Shape>)[propertyName]) | ||||||
| 					// There is a difference, we should return false.
 | 					// There is a difference, we should return false.
 | ||||||
| 					? true | 					? true | ||||||
| 					// There is no difference, returning nothing.
 | 					// There is no difference, returning nothing.
 | ||||||
|  | @ -121,97 +189,43 @@ export abstract class Model<THIS> | ||||||
| 			)) === true; | 			)) === true; | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	/** |  | ||||||
| 	 * Get model identifier. |  | ||||||
| 	 */ |  | ||||||
| 	getIdentifier(): unknown |  | ||||||
| 	{ |  | ||||||
| 		return (this as any)[this.SIdentifier()]; |  | ||||||
| 	} |  | ||||||
| 
 | 
 | ||||||
| 	/** | 		serializeDiff(): Partial<SerializedModel<Shape>> | ||||||
| 	 * Set current properties values as original values. |  | ||||||
| 	 */ |  | ||||||
| 	resetDiff() |  | ||||||
| 		{ | 		{ | ||||||
| 		this.forEachModelProperty((propertyName, propertyDefinition) => { | 			// Creating an empty (=> partial) serialized object.
 | ||||||
| 			// For each property, set its original value to its current property value.
 | 			const serializedObject: Partial<SerializedModel<Shape>> = {}; | ||||||
| 			this._originalProperties[propertyName] = (this as any)[propertyName]; |  | ||||||
| 			propertyDefinition.type.resetDiff((this as any)[propertyName]); |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
| 	/** |  | ||||||
| 	 * Serialize the difference between current model state and original one. |  | ||||||
| 	 */ |  | ||||||
| 	serializeDiff(): any |  | ||||||
| 	{ |  | ||||||
| 		// Creating a serialized object.
 |  | ||||||
| 		const serializedDiff: any = {}; |  | ||||||
| 
 | 
 | ||||||
| 			this.forEachModelProperty((propertyName, propertyDefinition) => { | 			this.forEachModelProperty((propertyName, propertyDefinition) => { | ||||||
| 			// For each defined model property, adding it to the serialized object if it has changed.
 | 				// For each defined model property, adding it to the serialized object if it has changed or if it is the identifier.
 | ||||||
| 			if (this.SIdentifier() == propertyName | 				if ( | ||||||
| 				|| propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as any)[propertyName])) | 					identifier == propertyName || | ||||||
| 				// Adding the current property to the serialized object if it is the identifier or its value has changed.
 | 					propertyDefinition.type.propertyHasChanged(this._originalProperties[propertyName], (this as PropertiesModel<Shape>)[propertyName]) | ||||||
| 				serializedDiff[propertyName] = propertyDefinition.type.serializeDiff((this as any)[propertyName]); | 				) // Adding the current property to the serialized object if it is the identifier or its value has changed.
 | ||||||
| 		}) | 					serializedObject[propertyName] = propertyDefinition.type.serializeDiff((this as PropertiesModel<Shape>)?.[propertyName]); | ||||||
| 
 |  | ||||||
| 		return serializedDiff; // Returning the serialized object.
 |  | ||||||
| 	} |  | ||||||
| 	/** |  | ||||||
| 	 * Get difference between original values and current ones, then reset it. |  | ||||||
| 	 * Similar to call `serializeDiff()` then `resetDiff()`. |  | ||||||
| 	 */ |  | ||||||
| 	save(): any |  | ||||||
| 	{ |  | ||||||
| 		// Get the difference.
 |  | ||||||
| 		const diff = this.serializeDiff(); |  | ||||||
| 
 |  | ||||||
| 		// Once the difference has been gotten, reset it.
 |  | ||||||
| 		this.resetDiff(); |  | ||||||
| 
 |  | ||||||
| 		return diff; // Return the difference.
 |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * Serialize the model. |  | ||||||
| 	 */ |  | ||||||
| 	serialize(): any |  | ||||||
| 	{ |  | ||||||
| 		// Creating a serialized object.
 |  | ||||||
| 		const serializedObject: any = {}; |  | ||||||
| 
 |  | ||||||
| 		this.forEachModelProperty((propertyName, propertyDefinition) => { |  | ||||||
| 			// For each defined model property, adding it to the serialized object.
 |  | ||||||
| 			serializedObject[propertyName] = propertyDefinition.type.serialize((this as any)[propertyName]); |  | ||||||
| 			}); | 			}); | ||||||
| 
 | 
 | ||||||
| 			return serializedObject; // Returning the serialized object.
 | 			return serializedObject; // Returning the serialized object.
 | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
| 	/** | 		resetDiff(): void | ||||||
| 	 * Special operations on parse. |  | ||||||
| 	 * @protected |  | ||||||
| 	 */ |  | ||||||
| 	protected parse(): void |  | ||||||
| 	{} // Nothing by default. TODO: create an event system to create functions like "beforeDeserialization" or "afterDeserialization".
 |  | ||||||
| 
 |  | ||||||
| 	/** |  | ||||||
| 	 * Deserialize the model. |  | ||||||
| 	 */ |  | ||||||
| 	deserialize(serializedObject: any): THIS |  | ||||||
| 		{ | 		{ | ||||||
| 			this.forEachModelProperty((propertyName, propertyDefinition) => { | 			this.forEachModelProperty((propertyName, propertyDefinition) => { | ||||||
| 			// For each defined model property, assigning its deserialized value to the model.
 | 				// For each property, set its original value to its current property value.
 | ||||||
| 			(this as any)[propertyName] = propertyDefinition.type.deserialize(serializedObject[propertyName]); | 				this._originalProperties[propertyName] = (this as PropertiesModel<Shape>)[propertyName]; | ||||||
|  | 				propertyDefinition.type.resetDiff((this as PropertiesModel<Shape>)[propertyName]); | ||||||
| 			}); | 			}); | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 		// Reset original property values.
 | 		save(): Partial<SerializedModel<Shape>> | ||||||
|  | 		{ | ||||||
|  | 			// Get the difference.
 | ||||||
|  | 			const diff = this.serializeDiff(); | ||||||
|  | 
 | ||||||
|  | 			// Once the difference has been obtained, reset it.
 | ||||||
| 			this.resetDiff(); | 			this.resetDiff(); | ||||||
| 
 | 
 | ||||||
| 		this._originalObject = serializedObject; // The model is not a new one, but loaded from a deserialized one.
 | 			return diff; // Return the difference.
 | ||||||
| 
 |  | ||||||
| 		return this as unknown as THIS; // Returning this, after deserialization.
 |  | ||||||
| 		} | 		} | ||||||
|  | 
 | ||||||
|  | 	} as unknown as ConstructorOf<Model<Shape, IdentifierType<Shape, Identifier>>>; | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										10
									
								
								src/Model/Properties.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/Model/Properties.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | ||||||
|  | export {define} from "./PropertyDefinition"; | ||||||
|  | 
 | ||||||
|  | export {array} from "./Types/ArrayType"; | ||||||
|  | export {bool, boolean} from "./Types/BoolType"; | ||||||
|  | export {date} from "./Types/DateType"; | ||||||
|  | export {decimal} from "./Types/DecimalType"; | ||||||
|  | export {model} from "./Types/ModelType"; | ||||||
|  | export {numeric} from "./Types/NumericType"; | ||||||
|  | export {object} from "./Types/ObjectType"; | ||||||
|  | export {string} from "./Types/StringType"; | ||||||
							
								
								
									
										26
									
								
								src/Model/PropertyDefinition.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/Model/PropertyDefinition.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | ||||||
|  | import {Type} from "./Types/Type"; | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * Property definition class. | ||||||
|  |  */ | ||||||
|  | export class Definition<SerializedType, ModelType> | ||||||
|  | { | ||||||
|  | 	readonly _sharkitek: ModelType; | ||||||
|  | 	readonly _serialized: SerializedType; | ||||||
|  | 
 | ||||||
|  | 	/** | ||||||
|  | 	 * Create a property definer instance. | ||||||
|  | 	 * @param type Property type. | ||||||
|  | 	 */ | ||||||
|  | 	constructor(public readonly type: Type<SerializedType, ModelType>) | ||||||
|  | 	{} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | /** | ||||||
|  |  * New definition of a property of the given type. | ||||||
|  |  * @param type Type of the property to define. | ||||||
|  |  */ | ||||||
|  | export function define<SerializedType, ModelType>(type: Type<SerializedType, ModelType>): Definition<SerializedType, ModelType> | ||||||
|  | { | ||||||
|  | 	return new Definition(type); | ||||||
|  | } | ||||||
|  | @ -1,4 +1,5 @@ | ||||||
| import {Type} from "./Type"; | import {Type} from "./Type"; | ||||||
|  | import {define, Definition} from "../PropertyDefinition"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of an array of values. |  * Type of an array of values. | ||||||
|  | @ -6,60 +7,60 @@ import {Type} from "./Type"; | ||||||
| export class ArrayType<SerializedValueType, SharkitekValueType> extends Type<SerializedValueType[], SharkitekValueType[]> | export class ArrayType<SerializedValueType, SharkitekValueType> extends Type<SerializedValueType[], SharkitekValueType[]> | ||||||
| { | { | ||||||
| 	/** | 	/** | ||||||
| 	 * Constructs a new array type of a Sharkitek model property. | 	 * Initialize a new array type of a Sharkitek model property. | ||||||
| 	 * @param valueType - Type of the array values. | 	 * @param valueDefinition Definition the array values. | ||||||
| 	 */ | 	 */ | ||||||
| 	constructor(protected valueType: Type<SerializedValueType, SharkitekValueType>) | 	constructor(protected valueDefinition: Definition<SerializedValueType, SharkitekValueType>) | ||||||
| 	{ | 	{ | ||||||
| 		super(); | 		super(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serialize(value: SharkitekValueType[]): SerializedValueType[] | 	serialize(value: SharkitekValueType[]|null|undefined): SerializedValueType[]|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		if (value === undefined) return undefined; | 		if (value === undefined) return undefined; | ||||||
| 		if (value === null) return null; | 		if (value === null) return null; | ||||||
| 
 | 
 | ||||||
| 		return value.map((value) => ( | 		return value.map((value) => ( | ||||||
| 			// Serializing each value of the array.
 | 			// Serializing each value of the array.
 | ||||||
| 			this.valueType.serialize(value) | 			this.valueDefinition.type.serialize(value) | ||||||
| 		)); | 		)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	deserialize(value: SerializedValueType[]): SharkitekValueType[] | 	deserialize(value: SerializedValueType[]|null|undefined): SharkitekValueType[]|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		if (value === undefined) return undefined; | 		if (value === undefined) return undefined; | ||||||
| 		if (value === null) return null; | 		if (value === null) return null; | ||||||
| 
 | 
 | ||||||
| 		return value.map((serializedValue) => ( | 		return value.map((serializedValue) => ( | ||||||
| 			// Deserializing each value of the array.
 | 			// Deserializing each value of the array.
 | ||||||
| 			this.valueType.deserialize(serializedValue) | 			this.valueDefinition.type.deserialize(serializedValue) | ||||||
| 		)); | 		)); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serializeDiff(value: SharkitekValueType[]): any | 	serializeDiff(value: SharkitekValueType[]|null|undefined): SerializedValueType[]|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		if (value === undefined) return undefined; | 		if (value === undefined) return undefined; | ||||||
| 		if (value === null) return null; | 		if (value === null) return null; | ||||||
| 
 | 
 | ||||||
| 		// Serializing diff of all elements.
 | 		// Serializing diff of all elements.
 | ||||||
| 		return value.map((value) => this.valueType.serializeDiff(value)); | 		return value.map((value) => this.valueDefinition.type.serializeDiff(value) as SerializedValueType); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	resetDiff(value: SharkitekValueType[]): void | 	resetDiff(value: SharkitekValueType[]|null|undefined): void | ||||||
| 	{ | 	{ | ||||||
| 		// Do nothing if it is not an array.
 | 		// Do nothing if it is not an array.
 | ||||||
| 		if (!Array.isArray(value)) return; | 		if (!Array.isArray(value)) return; | ||||||
| 
 | 
 | ||||||
| 		// Reset diff of all elements.
 | 		// Reset diff of all elements.
 | ||||||
| 		value.forEach((value) => this.valueType.resetDiff(value)); | 		value.forEach((value) => this.valueDefinition.type.resetDiff(value)); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of an array of values. |  * New array property definition. | ||||||
|  * @param valueType - Type of the array values. |  * @param valueDefinition Array values type definition. | ||||||
|  */ |  */ | ||||||
| export function SArray<SerializedValueType, SharkitekValueType>(valueType: Type<SerializedValueType, SharkitekValueType>) | export function array<SerializedValueType, SharkitekValueType>(valueDefinition: Definition<SerializedValueType, SharkitekValueType>): Definition<SerializedValueType[], SharkitekValueType[]> | ||||||
| { | { | ||||||
| 	return new ArrayType<SerializedValueType, SharkitekValueType>(valueType); | 	return define(new ArrayType(valueDefinition)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,22 +1,42 @@ | ||||||
| import {Type} from "./Type"; | import {Type} from "./Type"; | ||||||
|  | import {define, Definition} from "../PropertyDefinition"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of any boolean value. |  * Type of any boolean value. | ||||||
|  */ |  */ | ||||||
| export class BoolType extends Type<boolean, boolean> | export class BoolType extends Type<boolean, boolean> | ||||||
| { | { | ||||||
| 	deserialize(value: boolean): boolean | 	deserialize(value: boolean|null|undefined): boolean|null|undefined | ||||||
| 	{ | 	{ | ||||||
|  | 		// Keep NULL and undefined values.
 | ||||||
|  | 		if (value === undefined) return undefined; | ||||||
|  | 		if (value === null) return null; | ||||||
|  | 
 | ||||||
| 		return !!value; // ensure bool type.
 | 		return !!value; // ensure bool type.
 | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serialize(value: boolean): boolean | 	serialize(value: boolean|null|undefined): boolean|null|undefined | ||||||
| 	{ | 	{ | ||||||
|  | 		// Keep NULL and undefined values.
 | ||||||
|  | 		if (value === undefined) return undefined; | ||||||
|  | 		if (value === null) return null; | ||||||
|  | 
 | ||||||
| 		return !!value; // ensure bool type.
 | 		return !!value; // ensure bool type.
 | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of any boolean value. |  * New boolean property definition. | ||||||
|  */ |  */ | ||||||
| export const SBool = new BoolType(); | export function bool(): Definition<boolean, boolean> | ||||||
|  | { | ||||||
|  | 	return define(new BoolType()); | ||||||
|  | } | ||||||
|  | /** | ||||||
|  |  * New boolean property definition. | ||||||
|  |  * Alias of bool. | ||||||
|  |  */ | ||||||
|  | export function boolean(): ReturnType<typeof bool> | ||||||
|  | { | ||||||
|  | 	return bool(); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,22 +1,32 @@ | ||||||
| import {Type} from "./Type"; | import {Type} from "./Type"; | ||||||
|  | import {define, Definition} from "../PropertyDefinition"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of dates. |  * Type of dates. | ||||||
|  */ |  */ | ||||||
| export class DateType extends Type<string, Date> | export class DateType extends Type<string, Date> | ||||||
| { | { | ||||||
| 	deserialize(value: string): Date | 	deserialize(value: string|null|undefined): Date|null|undefined | ||||||
| 	{ | 	{ | ||||||
|  | 		if (value === undefined) return undefined; | ||||||
|  | 		if (value === null) return null; | ||||||
|  | 
 | ||||||
| 		return new Date(value); | 		return new Date(value); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serialize(value: Date): string | 	serialize(value: Date|null|undefined): string|null|undefined | ||||||
| 	{ | 	{ | ||||||
|  | 		if (value === undefined) return undefined; | ||||||
|  | 		if (value === null) return null; | ||||||
|  | 
 | ||||||
| 		return value?.toISOString(); | 		return value?.toISOString(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of dates. |  * New date property definition. | ||||||
|  */ |  */ | ||||||
| export const SDate = new DateType(); | export function date(): Definition<string, Date> | ||||||
|  | { | ||||||
|  | 	return define(new DateType()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,22 +1,32 @@ | ||||||
| import {Type} from "./Type"; | import {Type} from "./Type"; | ||||||
|  | import {define, Definition} from "../PropertyDefinition"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of decimal numbers. |  * Type of decimal numbers. | ||||||
|  */ |  */ | ||||||
| export class DecimalType extends Type<string, number> | export class DecimalType extends Type<string, number> | ||||||
| { | { | ||||||
| 	deserialize(value: string): number | 	deserialize(value: string|null|undefined): number|null|undefined | ||||||
| 	{ | 	{ | ||||||
|  | 		if (value === undefined) return undefined; | ||||||
|  | 		if (value === null) return null; | ||||||
|  | 
 | ||||||
| 		return parseFloat(value); | 		return parseFloat(value); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serialize(value: number): string | 	serialize(value: number|null|undefined): string|null|undefined | ||||||
| 	{ | 	{ | ||||||
|  | 		if (value === undefined) return undefined; | ||||||
|  | 		if (value === null) return null; | ||||||
|  | 
 | ||||||
| 		return value?.toString(); | 		return value?.toString(); | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of decimal numbers. |  * New decimal property definition. | ||||||
|  */ |  */ | ||||||
| export const SDecimal = new DecimalType(); | export function decimal(): Definition<string, number> | ||||||
|  | { | ||||||
|  | 	return define(new DecimalType()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,44 +1,49 @@ | ||||||
| import {Type} from "./Type"; | import {Type} from "./Type"; | ||||||
| import {Model} from "../Model"; | import {define, Definition} from "../PropertyDefinition"; | ||||||
| 
 | import {ConstructorOf, Model, ModelShape, SerializedModel} from "../Model"; | ||||||
| /** |  | ||||||
|  * Type definition of the constructor of a specific type. |  | ||||||
|  */ |  | ||||||
| export type ConstructorOf<T> = { new(): T; } |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of a Sharkitek model value. |  * Type of a Sharkitek model value. | ||||||
|  */ |  */ | ||||||
| export class ModelType<M extends Model<M>> extends Type<any, M> | export class ModelType<Shape extends ModelShape> extends Type<SerializedModel<Shape>, Model<Shape>> | ||||||
| { | { | ||||||
| 	/** | 	/** | ||||||
| 	 * Constructs a new model type of a Sharkitek model property. | 	 * Initialize a new model type of a Sharkitek model property. | ||||||
| 	 * @param modelConstructor - Constructor of the model. | 	 * @param modelConstructor Model constructor. | ||||||
| 	 */ | 	 */ | ||||||
| 	constructor(protected modelConstructor: ConstructorOf<M>) | 	constructor(protected modelConstructor: ConstructorOf<Model<Shape>>) | ||||||
| 	{ | 	{ | ||||||
| 		super(); | 		super(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serialize(value: M|null): any | 	serialize(value: Model<Shape>|null|undefined): SerializedModel<Shape>|null|undefined | ||||||
| 	{ | 	{ | ||||||
|  | 		if (value === undefined) return undefined; | ||||||
|  | 		if (value === null) return null; | ||||||
|  | 
 | ||||||
| 		// Serializing the given model.
 | 		// Serializing the given model.
 | ||||||
| 		return value ? value.serialize() : null; | 		return value?.serialize(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	deserialize(value: any): M|null | 	deserialize(value: SerializedModel<Shape>|null|undefined): Model<Shape>|null|undefined | ||||||
| 	{ | 	{ | ||||||
|  | 		if (value === undefined) return undefined; | ||||||
|  | 		if (value === null) return null; | ||||||
|  | 
 | ||||||
| 		// Deserializing the given object in the new model.
 | 		// Deserializing the given object in the new model.
 | ||||||
| 		return value ? (new this.modelConstructor()).deserialize(value) : null; | 		return (new this.modelConstructor()).deserialize(value) as Model<Shape>; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serializeDiff(value: M): any | 	serializeDiff(value: Model<Shape>|null|undefined): Partial<SerializedModel<Shape>>|null|undefined | ||||||
| 	{ | 	{ | ||||||
|  | 		if (value === undefined) return undefined; | ||||||
|  | 		if (value === null) return null; | ||||||
|  | 
 | ||||||
| 		// Serializing the given model.
 | 		// Serializing the given model.
 | ||||||
| 		return value ? value.serializeDiff() : null; | 		return value?.serializeDiff(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	resetDiff(value: M): void | 	resetDiff(value: Model<Shape>|null|undefined): void | ||||||
| 	{ | 	{ | ||||||
| 		// Reset diff of the given model.
 | 		// Reset diff of the given model.
 | ||||||
| 		value?.resetDiff(); | 		value?.resetDiff(); | ||||||
|  | @ -46,10 +51,10 @@ export class ModelType<M extends Model<M>> extends Type<any, M> | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of a Sharkitek model value. |  * New model property definition. | ||||||
|  * @param modelConstructor - Constructor of the model. |  * @param modelConstructor Model constructor. | ||||||
|  */ |  */ | ||||||
| export function SModel<M extends Model<M>>(modelConstructor: ConstructorOf<M>) | export function model<Shape extends ModelShape>(modelConstructor: ConstructorOf<Model<Shape>>): Definition<SerializedModel<Shape>, Model<Shape>> | ||||||
| { | { | ||||||
| 	return new ModelType(modelConstructor); | 	return define(new ModelType(modelConstructor)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,22 +1,26 @@ | ||||||
| import {Type} from "./Type"; | import {Type} from "./Type"; | ||||||
|  | import {define, Definition} from "../PropertyDefinition"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of any numeric value. |  * Type of any numeric value. | ||||||
|  */ |  */ | ||||||
| export class NumericType extends Type<number, number> | export class NumericType extends Type<number, number> | ||||||
| { | { | ||||||
| 	deserialize(value: number): number | 	deserialize(value: number|null|undefined): number|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		return value; | 		return value; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serialize(value: number): number | 	serialize(value: number|null|undefined): number|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		return value; | 		return value; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of any numeric value. |  * New numeric property definition. | ||||||
|  */ |  */ | ||||||
| export const SNumeric = new NumericType(); | export function numeric(): Definition<number, number> | ||||||
|  | { | ||||||
|  | 	return define(new NumericType()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,66 +1,67 @@ | ||||||
| import {Type} from "./Type"; | import {Type} from "./Type"; | ||||||
| import {Definition} from "../Definition"; | import {define, Definition} from "../PropertyDefinition"; | ||||||
|  | import {ModelShape, PropertiesModel, SerializedModel, UnknownDefinition} from "../Model"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of a simple object. |  * Type of a custom object. | ||||||
|  */ |  */ | ||||||
| export class ObjectType<Keys extends symbol|string> extends Type<Record<Keys, any>, Record<Keys, any>> | export class ObjectType<Shape extends ModelShape> extends Type<SerializedModel<Shape>, PropertiesModel<Shape>> | ||||||
| { | { | ||||||
| 	/** | 	/** | ||||||
| 	 * Constructs a new object type of a Sharkitek model property. | 	 * Initialize a new object type of a Sharkitek model property. | ||||||
| 	 * @param fieldsTypes Object fields types. | 	 * @param shape | ||||||
| 	 */ | 	 */ | ||||||
| 	constructor(protected fieldsTypes: Record<Keys, Definition<unknown, unknown>>) | 	constructor(protected readonly shape: Shape) | ||||||
| 	{ | 	{ | ||||||
| 		super(); | 		super(); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	deserialize(value: Record<Keys, any>): Record<Keys, any> | 	deserialize(value: SerializedModel<Shape>|null|undefined): PropertiesModel<Shape>|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		if (value === undefined) return undefined; | 		if (value === undefined) return undefined; | ||||||
| 		if (value === null) return null; | 		if (value === null) return null; | ||||||
| 
 | 
 | ||||||
| 		return Object.fromEntries( | 		return Object.fromEntries( | ||||||
| 			// For each defined field, deserialize its value according to its type.
 | 			// For each defined field, deserialize its value according to its type.
 | ||||||
| 			(Object.entries(this.fieldsTypes) as [Keys, Definition<any, any>][]).map(([fieldName, fieldDefinition]) => ( | 			(Object.entries(this.shape) as [keyof Shape, UnknownDefinition][]).map(([fieldName, fieldDefinition]) => ( | ||||||
| 				// Return an entry with the current field name and the deserialized value.
 | 				// Return an entry with the current field name and the deserialized value.
 | ||||||
| 				[fieldName, fieldDefinition.type.deserialize(value?.[fieldName])] | 				[fieldName, fieldDefinition.type.deserialize(value?.[fieldName])] | ||||||
| 			)) | 			)) | ||||||
| 		) as Record<Keys, any>; | 		) as PropertiesModel<Shape>; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serialize(value: Record<Keys, any>): Record<Keys, any> | 	serialize(value: PropertiesModel<Shape>|null|undefined): SerializedModel<Shape>|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		if (value === undefined) return undefined; | 		if (value === undefined) return undefined; | ||||||
| 		if (value === null) return null; | 		if (value === null) return null; | ||||||
| 
 | 
 | ||||||
| 		return Object.fromEntries( | 		return Object.fromEntries( | ||||||
| 			// For each defined field, serialize its value according to its type.
 | 			// For each defined field, serialize its value according to its type.
 | ||||||
| 			(Object.entries(this.fieldsTypes) as [Keys, Definition<any, any>][]).map(([fieldName, fieldDefinition]) => ( | 			(Object.entries(this.shape) as [keyof Shape, UnknownDefinition][]).map(([fieldName, fieldDefinition]) => ( | ||||||
| 				// Return an entry with the current field name and the serialized value.
 | 				// Return an entry with the current field name and the serialized value.
 | ||||||
| 				[fieldName, fieldDefinition.type.serialize(value?.[fieldName])] | 				[fieldName, fieldDefinition.type.serialize(value?.[fieldName])] | ||||||
| 			)) | 			)) | ||||||
| 		) as Record<Keys, any>; | 		) as PropertiesModel<Shape>; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serializeDiff(value: Record<Keys, any>): Record<Keys, any> | 	serializeDiff(value: PropertiesModel<Shape>|null|undefined): Partial<SerializedModel<Shape>>|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		if (value === undefined) return undefined; | 		if (value === undefined) return undefined; | ||||||
| 		if (value === null) return null; | 		if (value === null) return null; | ||||||
| 
 | 
 | ||||||
| 		return Object.fromEntries( | 		return Object.fromEntries( | ||||||
| 			// For each defined field, serialize its diff value according to its type.
 | 			// For each defined field, serialize its diff value according to its type.
 | ||||||
| 			(Object.entries(this.fieldsTypes) as [Keys, Definition<any, any>][]).map(([fieldName, fieldDefinition]) => ( | 			(Object.entries(this.shape) as [keyof Shape, UnknownDefinition][]).map(([fieldName, fieldDefinition]) => ( | ||||||
| 				// Return an entry with the current field name and the serialized diff value.
 | 				// Return an entry with the current field name and the serialized diff value.
 | ||||||
| 				[fieldName, fieldDefinition.type.serializeDiff(value?.[fieldName])] | 				[fieldName, fieldDefinition.type.serializeDiff(value?.[fieldName])] | ||||||
| 			)) | 			)) | ||||||
| 		) as Record<Keys, any>; | 		) as PropertiesModel<Shape>; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	resetDiff(value: Record<Keys, any>): void | 	resetDiff(value: PropertiesModel<Shape>|null|undefined) | ||||||
| 	{ | 	{ | ||||||
| 		// For each field, reset its diff.
 | 		// For each field, reset its diff.
 | ||||||
| 		(Object.entries(this.fieldsTypes) as [Keys, Definition<any, any>][]).forEach(([fieldName, fieldDefinition]) => { | 		(Object.entries(this.shape) as [keyof Shape, UnknownDefinition][]).forEach(([fieldName, fieldDefinition]) => { | ||||||
| 			// Reset diff of the current field.
 | 			// Reset diff of the current field.
 | ||||||
| 			fieldDefinition.type.resetDiff(value?.[fieldName]); | 			fieldDefinition.type.resetDiff(value?.[fieldName]); | ||||||
| 		}); | 		}); | ||||||
|  | @ -68,10 +69,10 @@ export class ObjectType<Keys extends symbol|string> extends Type<Record<Keys, an | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of a simple object. |  * New object property definition. | ||||||
|  * @param fieldsTypes Object fields types. |  * @param shape Shape of the object. | ||||||
|  */ |  */ | ||||||
| export function SObject<Keys extends symbol|string>(fieldsTypes: Record<Keys, Definition<unknown, unknown>>): ObjectType<Keys> | export function object<Shape extends ModelShape>(shape: Shape): Definition<SerializedModel<Shape>, PropertiesModel<Shape>> | ||||||
| { | { | ||||||
| 	return new ObjectType<Keys>(fieldsTypes); | 	return define(new ObjectType(shape)); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,22 +1,26 @@ | ||||||
| import {Type} from "./Type"; | import {Type} from "./Type"; | ||||||
|  | import {define, Definition} from "../PropertyDefinition"; | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of any string value. |  * Type of any string value. | ||||||
|  */ |  */ | ||||||
| export class StringType extends Type<string, string> | export class StringType extends Type<string, string> | ||||||
| { | { | ||||||
| 	deserialize(value: string): string | 	deserialize(value: string|null|undefined): string|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		return value; | 		return value; | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	serialize(value: string): string | 	serialize(value: string|null|undefined): string|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		return value; | 		return value; | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Type of any string value. |  * New string property definition. | ||||||
|  */ |  */ | ||||||
| export const SString = new StringType(); | export function string(): Definition<string, string> | ||||||
|  | { | ||||||
|  | 	return define(new StringType()); | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -1,25 +1,25 @@ | ||||||
| /** | /** | ||||||
|  * Abstract class of a Sharkitek model property type. |  * Abstract class of a Sharkitek model property type. | ||||||
|  */ |  */ | ||||||
| export abstract class Type<SerializedType, SharkitekType> | export abstract class Type<SerializedType, ModelType> | ||||||
| { | { | ||||||
| 	/** | 	/** | ||||||
| 	 * Serialize the given value of a Sharkitek model property. | 	 * Serialize the given value of a Sharkitek model property. | ||||||
| 	 * @param value - Value to serialize. | 	 * @param value Value to serialize. | ||||||
| 	 */ | 	 */ | ||||||
| 	abstract serialize(value: SharkitekType): SerializedType; | 	abstract serialize(value: ModelType|null|undefined): SerializedType|null|undefined; | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Deserialize the given value of a serialized Sharkitek model. | 	 * Deserialize the given value of a serialized Sharkitek model. | ||||||
| 	 * @param value - Value to deserialize. | 	 * @param value - Value to deserialize. | ||||||
| 	 */ | 	 */ | ||||||
| 	abstract deserialize(value: SerializedType): SharkitekType; | 	abstract deserialize(value: SerializedType|null|undefined): ModelType|null|undefined; | ||||||
| 
 | 
 | ||||||
| 	/** | 	/** | ||||||
| 	 * Serialize the given value only if it has changed. | 	 * Serialize the given value only if it has changed. | ||||||
| 	 * @param value - Value to deserialize. | 	 * @param value - Value to deserialize. | ||||||
| 	 */ | 	 */ | ||||||
| 	serializeDiff(value: SharkitekType): SerializedType|null | 	serializeDiff(value: ModelType|null|undefined): Partial<SerializedType>|null|undefined | ||||||
| 	{ | 	{ | ||||||
| 		return this.serialize(value); // By default, nothing changes.
 | 		return this.serialize(value); // By default, nothing changes.
 | ||||||
| 	} | 	} | ||||||
|  | @ -28,7 +28,7 @@ export abstract class Type<SerializedType, SharkitekType> | ||||||
| 	 * Reset the difference between the original value and the current one. | 	 * Reset the difference between the original value and the current one. | ||||||
| 	 * @param value - Value for which reset diff data. | 	 * @param value - Value for which reset diff data. | ||||||
| 	 */ | 	 */ | ||||||
| 	resetDiff(value: SharkitekType): void | 	resetDiff(value: ModelType|null|undefined): void | ||||||
| 	{ | 	{ | ||||||
| 		// By default, nothing to do.
 | 		// By default, nothing to do.
 | ||||||
| 	} | 	} | ||||||
|  | @ -38,7 +38,7 @@ export abstract class Type<SerializedType, SharkitekType> | ||||||
| 	 * @param originalValue - Original property value. | 	 * @param originalValue - Original property value. | ||||||
| 	 * @param currentValue - Current property value. | 	 * @param currentValue - Current property value. | ||||||
| 	 */ | 	 */ | ||||||
| 	propertyHasChanged(originalValue: SharkitekType, currentValue: SharkitekType): boolean | 	propertyHasChanged(originalValue: ModelType|null|undefined, currentValue: ModelType|null|undefined): boolean | ||||||
| 	{ | 	{ | ||||||
| 		return originalValue != currentValue; | 		return originalValue != currentValue; | ||||||
| 	} | 	} | ||||||
|  | @ -48,7 +48,7 @@ export abstract class Type<SerializedType, SharkitekType> | ||||||
| 	 * @param originalValue - Original serialized property value. | 	 * @param originalValue - Original serialized property value. | ||||||
| 	 * @param currentValue - Current serialized property value. | 	 * @param currentValue - Current serialized property value. | ||||||
| 	 */ | 	 */ | ||||||
| 	serializedPropertyHasChanged(originalValue: SerializedType, currentValue: SerializedType): boolean | 	serializedPropertyHasChanged(originalValue: SerializedType|null|undefined, currentValue: SerializedType|null|undefined): boolean | ||||||
| 	{ | 	{ | ||||||
| 		return originalValue != currentValue; | 		return originalValue != currentValue; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										14
									
								
								src/Model/index.ts
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										14
									
								
								src/Model/index.ts
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,14 @@ | ||||||
|  | import * as property from "./Properties"; | ||||||
|  | export { property }; | ||||||
|  | 
 | ||||||
|  | export * from "./Model"; | ||||||
|  | export {Definition} from "./PropertyDefinition"; | ||||||
|  | 
 | ||||||
|  | export {ArrayType} from "./Types/ArrayType"; | ||||||
|  | export {BoolType} from "./Types/BoolType"; | ||||||
|  | export {DateType} from "./Types/DateType"; | ||||||
|  | export {DecimalType} from "./Types/DecimalType"; | ||||||
|  | export {ModelType} from "./Types/ModelType"; | ||||||
|  | export {NumericType} from "./Types/NumericType"; | ||||||
|  | export {ObjectType} from "./Types/ObjectType"; | ||||||
|  | export {StringType} from "./Types/StringType"; | ||||||
							
								
								
									
										18
									
								
								src/index.ts
									
										
									
									
									
								
							
							
						
						
									
										18
									
								
								src/index.ts
									
										
									
									
									
								
							|  | @ -1,14 +1,4 @@ | ||||||
| 
 | import * as s from "./Model"; | ||||||
| export * from "./Model/Model"; | export * from "./Model"; | ||||||
| 
 | export { s }; | ||||||
| export * from "./Model/Definition"; | export default s; | ||||||
| 
 |  | ||||||
| export * from "./Model/Types/Type"; |  | ||||||
| export * from "./Model/Types/ArrayType"; |  | ||||||
| export * from "./Model/Types/BoolType"; |  | ||||||
| export * from "./Model/Types/DateType"; |  | ||||||
| export * from "./Model/Types/DecimalType"; |  | ||||||
| export * from "./Model/Types/ModelType"; |  | ||||||
| export * from "./Model/Types/NumericType"; |  | ||||||
| export * from "./Model/Types/ObjectType"; |  | ||||||
| export * from "./Model/Types/StringType"; |  | ||||||
|  |  | ||||||
|  | @ -1,39 +1,18 @@ | ||||||
| import { | import {s} from "../src"; | ||||||
| 	SArray, |  | ||||||
| 	SDecimal, |  | ||||||
| 	SModel, |  | ||||||
| 	SNumeric, |  | ||||||
| 	SString, |  | ||||||
| 	SDate, |  | ||||||
| 	SBool, |  | ||||||
| 	Model, |  | ||||||
| 	ModelDefinition, |  | ||||||
| 	SDefine, ModelIdentifier |  | ||||||
| } from "../src"; |  | ||||||
| import {SObject} from "../src/Model/Types/ObjectType"; |  | ||||||
| 
 | 
 | ||||||
| /** | /** | ||||||
|  * Another test model. |  * Another test model. | ||||||
|  */ |  */ | ||||||
| class Author extends Model<Author> | class Author extends s.model({ | ||||||
|  | 	name: s.property.string(), | ||||||
|  | 	firstName: s.property.string(), | ||||||
|  | 	email: s.property.string(), | ||||||
|  | 	createdAt: s.property.date(), | ||||||
|  | 	active: s.property.bool(), | ||||||
|  | }) | ||||||
| { | { | ||||||
| 	name: string; |  | ||||||
| 	firstName: string; |  | ||||||
| 	email: string; |  | ||||||
| 	createdAt: Date; |  | ||||||
| 	active: boolean = true; | 	active: boolean = true; | ||||||
| 
 | 
 | ||||||
| 	protected SDefinition(): ModelDefinition<Author> |  | ||||||
| 	{ |  | ||||||
| 		return { |  | ||||||
| 			name: SDefine(SString), |  | ||||||
| 			firstName: SDefine(SString), |  | ||||||
| 			email: SDefine(SString), |  | ||||||
| 			createdAt: SDefine(SDate), |  | ||||||
| 			active: SDefine(SBool), |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	constructor(name: string = "", firstName: string = "", email: string = "", createdAt: Date = new Date()) | 	constructor(name: string = "", firstName: string = "", email: string = "", createdAt: Date = new Date()) | ||||||
| 	{ | 	{ | ||||||
| 		super(); | 		super(); | ||||||
|  | @ -48,7 +27,18 @@ class Author extends Model<Author> | ||||||
| /** | /** | ||||||
|  * A test model. |  * A test model. | ||||||
|  */ |  */ | ||||||
| class Article extends Model<Article> | class Article extends s.model({ | ||||||
|  | 	id: s.property.numeric(), | ||||||
|  | 	title: s.property.string(), | ||||||
|  | 	authors: s.property.array(s.property.model(Author)), | ||||||
|  | 	text: s.property.string(), | ||||||
|  | 	evaluation: s.property.decimal(), | ||||||
|  | 	tags: s.property.array( | ||||||
|  | 		s.property.object({ | ||||||
|  | 			name: s.property.string(), | ||||||
|  | 		}) | ||||||
|  | 	), | ||||||
|  | }, "id") | ||||||
| { | { | ||||||
| 	id: number; | 	id: number; | ||||||
| 	title: string; | 	title: string; | ||||||
|  | @ -58,27 +48,6 @@ class Article extends Model<Article> | ||||||
| 	tags: { | 	tags: { | ||||||
| 		name: string; | 		name: string; | ||||||
| 	}[]; | 	}[]; | ||||||
| 
 |  | ||||||
| 	protected SIdentifier(): ModelIdentifier<Article> |  | ||||||
| 	{ |  | ||||||
| 		return "id"; |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	protected SDefinition(): ModelDefinition<Article> |  | ||||||
| 	{ |  | ||||||
| 		return { |  | ||||||
| 			id: SDefine(SNumeric), |  | ||||||
| 			title: SDefine(SString), |  | ||||||
| 			authors: SDefine(SArray(SModel(Author))), |  | ||||||
| 			text: SDefine(SString), |  | ||||||
| 			evaluation: SDefine(SDecimal), |  | ||||||
| 			tags: SDefine(SArray( |  | ||||||
| 				SObject({ |  | ||||||
| 					name: SDefine(SString), |  | ||||||
| 				}) |  | ||||||
| 			)), |  | ||||||
| 		}; |  | ||||||
| 	} |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| it("deserialize", () => { | it("deserialize", () => { | ||||||
|  | @ -140,8 +109,8 @@ it("deserialize then save", () => { | ||||||
| 		id: 1, | 		id: 1, | ||||||
| 		title: "this is a test", | 		title: "this is a test", | ||||||
| 		authors: [ | 		authors: [ | ||||||
| 			{ name: "DOE", firstName: "John", email: "test@test.test", createdAt: new Date(), active: true, }, | 			{ name: "DOE", firstName: "John", email: "test@test.test", createdAt: (new Date()).toISOString(), active: true, }, | ||||||
| 			{ name: "TEST", firstName: "Another", email: "another@test.test", createdAt: new Date(), active: false, }, | 			{ name: "TEST", firstName: "Another", email: "another@test.test", createdAt: (new Date()).toISOString(), active: false, }, | ||||||
| 		], | 		], | ||||||
| 		text: "this is a long test.", | 		text: "this is a long test.", | ||||||
| 		evaluation: "25.23", | 		evaluation: "25.23", | ||||||
|  | @ -167,8 +136,8 @@ it("save with modified submodels", () => { | ||||||
| 		id: 1, | 		id: 1, | ||||||
| 		title: "this is a test", | 		title: "this is a test", | ||||||
| 		authors: [ | 		authors: [ | ||||||
| 			{ name: "DOE", firstName: "John", email: "test@test.test", createdAt: new Date(), active: true, }, | 			{ name: "DOE", firstName: "John", email: "test@test.test", createdAt: (new Date()).toISOString(), active: true, }, | ||||||
| 			{ name: "TEST", firstName: "Another", email: "another@test.test", createdAt: new Date(), active: false, }, | 			{ name: "TEST", firstName: "Another", email: "another@test.test", createdAt: (new Date()).toISOString(), active: false, }, | ||||||
| 		], | 		], | ||||||
| 		text: "this is a long test.", | 		text: "this is a long test.", | ||||||
| 		evaluation: "25.23", | 		evaluation: "25.23", | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		
		Reference in a new issue