Compare commits

..

No commits in common. "develop" and "main" have entirely different histories.

35 changed files with 1287 additions and 2889 deletions

View file

@ -1,5 +0,0 @@
{
"useTabs": true,
"trailingComma": "all",
"bracketSpacing": false
}

View file

@ -44,7 +44,8 @@ With Sharkitek, you define the architecture of your models by specifying their p
Then, you can use the defined methods like `serialize`, `parse`, `patch` or `serializeDiff`. Then, you can use the defined methods like `serialize`, `parse`, `patch` or `serializeDiff`.
```typescript ```typescript
class Example { class Example
{
static model = defineModel({ static model = defineModel({
Class: Example, Class: Example,
properties: { properties: {
@ -69,7 +70,8 @@ class Example {
/** /**
* A person. * A person.
*/ */
class Person { class Person
{
static model = defineModel({ static model = defineModel({
Class: Person, Class: Person,
properties: { properties: {
@ -94,7 +96,8 @@ class Person {
/** /**
* An article. * An article.
*/ */
class Article { class Article
{
static model = defineModel({ static model = defineModel({
Class: Article, Class: Article,
properties: { properties: {
@ -106,7 +109,7 @@ class Article {
tags: s.property.array( tags: s.property.array(
s.property.object({ s.property.object({
name: s.property.string(), name: s.property.string(),
}), })
), ),
}, },
identifier: "id", identifier: "id",
@ -127,7 +130,8 @@ class Article {
/** /**
* A model with composite keys. * A model with composite keys.
*/ */
class CompositeKeys { class CompositeKeys
{
static model = defineModel({ static model = defineModel({
Class: CompositeKeys, Class: CompositeKeys,
properties: { properties: {
@ -221,7 +225,8 @@ Sharkitek defines some basic types by default, in these classes:
When you are defining a property of a Sharkitek model, you must provide its type by instantiating one of these classes. When you are defining a property of a Sharkitek model, you must provide its type by instantiating one of these classes.
```typescript ```typescript
class Example { class Example
{
static model = defineModel({ static model = defineModel({
Class: Example, Class: Example,
properties: { properties: {
@ -248,7 +253,8 @@ To ease the use of these classes and reduce read complexity, properties of each
Type implementers should provide a corresponding function for each defined type. They can even provide multiple functions or constants with predefined parameters. For example, we could define `s.property.stringArray()` which would be similar to `s.property.array(s.property.string())`. Type implementers should provide a corresponding function for each defined type. They can even provide multiple functions or constants with predefined parameters. For example, we could define `s.property.stringArray()` which would be similar to `s.property.array(s.property.string())`.
```typescript ```typescript
class Example { class Example
{
static model = defineModel({ static model = defineModel({
Class: Example, Class: Example,
properties: { properties: {

View file

@ -25,8 +25,7 @@
"scripts": { "scripts": {
"build": "tsc && vite build", "build": "tsc && vite build",
"test": "vitest", "test": "vitest",
"coverage": "vitest run --coverage", "coverage": "vitest run --coverage"
"format": "prettier . --write"
}, },
"type": "module", "type": "module",
"source": "src/library.ts", "source": "src/library.ts",
@ -36,14 +35,13 @@
"lib/**/*" "lib/**/*"
], ],
"devDependencies": { "devDependencies": {
"@types/node": "^24.0.3", "@types/node": "^22.13.14",
"@vitest/coverage-v8": "^3.2.4", "@vitest/coverage-v8": "^3.0.9",
"prettier": "^3.6.0",
"ts-node": "^10.9.2", "ts-node": "^10.9.2",
"typescript": "^5.8.3", "typescript": "^5.8.2",
"vite": "^6.3.5", "vite": "^6.2.3",
"vite-plugin-dts": "^4.5.4", "vite-plugin-dts": "^4.5.3",
"vitest": "^3.2.4" "vitest": "^3.0.9"
}, },
"packageManager": "yarn@4.6.0" "packageManager": "yarn@4.6.0"
} }

View file

@ -1,3 +1,4 @@
export * from "./sharkitek-error"; export * from "./sharkitek-error";
export * from "./type-error"; export * from "./type-error";
export * from "./invalid-type-value-error"; export * from "./invalid-type-value-error";

View file

@ -4,15 +4,10 @@ import {Type} from "../model/types/type";
/** /**
* A Sharkitek type error when the passed value is invalid. * A Sharkitek type error when the passed value is invalid.
*/ */
export class InvalidTypeValueError<SerializedType, ModelType> extends TypeError< export class InvalidTypeValueError<SerializedType, ModelType> extends TypeError<SerializedType, ModelType>
SerializedType, {
ModelType constructor(public type: Type<SerializedType, ModelType>, public value: any, message?: string)
> { {
constructor( super(type, message ?? `${JSON.stringify(value)} is an invalid value`)
public type: Type<SerializedType, ModelType>,
public value: any,
message?: string,
) {
super(type, message ?? `${JSON.stringify(value)} is an invalid value`);
} }
} }

View file

@ -1,4 +1,6 @@
/** /**
* A Sharkitek error. * A Sharkitek error.
*/ */
export class SharkitekError extends Error {} export class SharkitekError extends Error
{
}

View file

@ -4,13 +4,10 @@ import {Type} from "../model/types/type";
/** /**
* A Sharkitek type error. * A Sharkitek type error.
*/ */
export class TypeError<SerializedType, ModelType> extends SharkitekError { export class TypeError<SerializedType, ModelType> extends SharkitekError
constructor( {
public type: Type<SerializedType, ModelType>, constructor(public type: Type<SerializedType, ModelType>, message?: string)
message?: string, {
) { super(`Error in type ${type.constructor.name}${message ? `: ${message}` : ""}`);
super(
`Error in type ${type.constructor.name}${message ? `: ${message}` : ""}`,
);
} }
} }

View file

@ -1,5 +1,5 @@
import * as s from "./model"; import * as s from "./model";
export * from "./model"; export * from "./model";
export * from "./errors"; export * from "./errors";
export {s}; export { s };
export default s; export default s;

View file

@ -11,10 +11,7 @@ export type ModelShape<T extends object> = Partial<{
/** /**
* Properties values of a model based on its shape. * Properties values of a model based on its shape.
*/ */
export type ModelPropertiesValues< export type ModelPropertiesValues<T extends object, Shape extends ModelShape<T>> = {
T extends object,
Shape extends ModelShape<T>,
> = {
[k in keyof Shape]: Shape[k]["_sharkitek"]; [k in keyof Shape]: Shape[k]["_sharkitek"];
}; };
@ -29,38 +26,20 @@ export type SerializedModel<T extends object, Shape extends ModelShape<T>> = {
* This is an experimental serialized model type declaration. * This is an experimental serialized model type declaration.
* @deprecated * @deprecated
*/ */
type ExperimentalSerializedModel< type ExperimentalSerializedModel<T extends object, Shape extends ModelShape<T>>
T extends object, = Omit<ExperimentalSerializedModelBase<T, Shape>, ExperimentalSerializedModelOptionalKeys<T, Shape>>
Shape extends ModelShape<T>, & Pick<Partial<ExperimentalSerializedModelBase<T, Shape>>, ExperimentalSerializedModelOptionalKeys<T, Shape>>;
> = Omit< type ExperimentalSerializedModelBase<T extends object, Shape extends ModelShape<T>> = {
ExperimentalSerializedModelBase<T, Shape>,
ExperimentalSerializedModelOptionalKeys<T, Shape>
> &
Pick<
Partial<ExperimentalSerializedModelBase<T, Shape>>,
ExperimentalSerializedModelOptionalKeys<T, Shape>
>;
type ExperimentalSerializedModelBase<
T extends object,
Shape extends ModelShape<T>,
> = {
[k in keyof Shape]: Shape[k]["_serialized"]; [k in keyof Shape]: Shape[k]["_serialized"];
}; };
type ExperimentalSerializedModelOptionalKeys< type ExperimentalSerializedModelOptionalKeys<T extends object, Shape extends ModelShape<T>> = {
T extends object, [k in keyof Shape]: Shape[k]["_serialized"] extends undefined ? k : never
Shape extends ModelShape<T>,
> = {
[k in keyof Shape]: Shape[k]["_serialized"] extends undefined ? k : never;
}[keyof Shape]; }[keyof Shape];
/** /**
* A sharkitek model instance, with internal model state. * A sharkitek model instance, with internal model state.
*/ */
export type ModelInstance< export type ModelInstance<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>> = T & {
T extends object,
Shape extends ModelShape<T>,
Identifier extends IdentifierDefinition<T, Shape>,
> = T & {
/** /**
* The Sharkitek model state. * The Sharkitek model state.
*/ */
@ -70,34 +49,19 @@ export type ModelInstance<
/** /**
* Identifier definition type. * Identifier definition type.
*/ */
export type IdentifierDefinition< export type IdentifierDefinition<T extends object, Shape extends ModelShape<T>> = (keyof Shape)|((keyof Shape)[]);
T extends object,
Shape extends ModelShape<T>,
> = keyof Shape | (keyof Shape)[];
/** /**
* Identifier type. * Identifier type.
*/ */
export type IdentifierType< export type IdentifierType<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>>
T extends object, = Identifier extends keyof Shape ? Shape[Identifier]["_sharkitek"] : { [K in keyof Identifier]: Identifier[K] extends keyof Shape ? Shape[Identifier[K]]["_sharkitek"] : unknown };
Shape extends ModelShape<T>,
Identifier extends IdentifierDefinition<T, Shape>,
> = Identifier extends keyof Shape
? Shape[Identifier]["_sharkitek"]
: {
[K in keyof Identifier]: Identifier[K] extends keyof Shape
? Shape[Identifier[K]]["_sharkitek"]
: unknown;
};
/** /**
* A model definition object. * A model definition object.
*/ */
export interface ModelDefinition< export interface ModelDefinition<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>>
T extends object, {
Shape extends ModelShape<T>,
Identifier extends IdentifierDefinition<T, Shape>,
> {
/** /**
* Model class. * Model class.
*/ */
@ -119,7 +83,8 @@ export interface ModelDefinition<
/** /**
* A model property. * A model property.
*/ */
export interface ModelProperty<T extends object, Shape extends ModelShape<T>> { export interface ModelProperty<T extends object, Shape extends ModelShape<T>>
{
/** /**
* Property name. * Property name.
*/ */
@ -139,19 +104,13 @@ export interface ModelProperty<T extends object, Shape extends ModelShape<T>> {
/** /**
* Model properties iterator object. * Model properties iterator object.
*/ */
export type ModelProperties< export type ModelProperties<T extends object, Shape extends ModelShape<T>> = ModelProperty<T, Shape>[];
T extends object,
Shape extends ModelShape<T>,
> = ModelProperty<T, Shape>[];
/** /**
* A Sharkitek model state. * A Sharkitek model state.
*/ */
export class Model< export class Model<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>>
T extends object, {
Shape extends ModelShape<T>,
Identifier extends IdentifierDefinition<T, Shape>,
> {
/** /**
* The model manager instance. * The model manager instance.
*/ */
@ -185,14 +144,15 @@ export class Model<
/** /**
* The original serialized object, if there is one. * The original serialized object, if there is one.
*/ */
serialized: SerializedModel<T, Shape> | null; serialized: SerializedModel<T, Shape>|null;
}; };
/** /**
* Initialize a new model state with the defined properties. * Initialize a new model state with the defined properties.
* @param manager The model manager. * @param manager The model manager.
*/ */
constructor(manager: ModelManager<T, Shape, Identifier>) { constructor(manager: ModelManager<T, Shape, Identifier>)
{
this.manager = manager; this.manager = manager;
this.definition = manager.definition; this.definition = manager.definition;
this.properties = manager.properties; this.properties = manager.properties;
@ -201,10 +161,11 @@ export class Model<
/** /**
* Initialize the Sharkitek model state for a new instance. * Initialize the Sharkitek model state for a new instance.
*/ */
initInstance(): this { initInstance(): this
{
return this.fromInstance( return this.fromInstance(
// Initialize a new model instance. // Initialize a new model instance.
new this.definition.Class(), new (this.definition.Class)()
); );
} }
@ -212,7 +173,8 @@ export class Model<
* Initialize the Sharkitek model state for the provided instance. * Initialize the Sharkitek model state for the provided instance.
* @param instance The model instance. * @param instance The model instance.
*/ */
fromInstance(instance: T): this { fromInstance(instance: T): this
{
// Initialize the sharkitek model instance. // Initialize the sharkitek model instance.
const sharkitekInstance = instance as ModelInstance<T, Shape, Identifier>; const sharkitekInstance = instance as ModelInstance<T, Shape, Identifier>;
@ -226,8 +188,8 @@ export class Model<
if (originalInstance) if (originalInstance)
// Share the same original values object. // Share the same original values object.
this.original = originalInstance.original; this.original = originalInstance.original;
else { else
// Initialize a new original values object, based on the current values of the instance. { // Initialize a new original values object, based on the current values of the instance.
this.original = { this.original = {
properties: undefined, properties: undefined,
serialized: null, serialized: null,
@ -242,14 +204,14 @@ export class Model<
* Deserialize provided data to a new model instance. * Deserialize provided data to a new model instance.
* @param serialized Serialized model. * @param serialized Serialized model.
*/ */
deserialize(serialized: SerializedModel<T, Shape>): this { deserialize(serialized: SerializedModel<T, Shape>): this
{
// Initialize a new model instance. // Initialize a new model instance.
this.initInstance(); this.initInstance();
for (const property of this.properties) { for (const property of this.properties)
// For each defined model property, assigning its deserialized value. { // For each defined model property, assigning its deserialized value.
(this.instance[property.name as keyof T] as any) = (this.instance[property.name as keyof T] as any) = property.definition.type.deserialize(serialized[property.name]);
property.definition.type.deserialize(serialized[property.name]);
} }
// Reset original property values. // Reset original property values.
@ -263,31 +225,29 @@ export class Model<
/** /**
* Get current model instance identifier. * Get current model instance identifier.
*/ */
getIdentifier(): IdentifierType<T, Shape, Identifier> { getIdentifier(): IdentifierType<T, Shape, Identifier>
if (Array.isArray(this.definition.identifier)) { {
// The identifier is composite, make an array of properties values. if (Array.isArray(this.definition.identifier))
return this.definition.identifier.map( { // The identifier is composite, make an array of properties values.
(identifier) => this.instance?.[identifier as keyof T], return this.definition.identifier.map(identifier => this.instance?.[identifier as keyof T]) as IdentifierType<T, Shape, Identifier>;
) as IdentifierType<T, Shape, Identifier>; }
} else { else
// The identifier is a simple property, get its value. { // The identifier is a simple property, get its value.
return this.instance?.[ return this.instance?.[this.definition.identifier as keyof Shape as keyof T] as IdentifierType<T, Shape, Identifier>;
this.definition.identifier as keyof Shape as keyof T
] as IdentifierType<T, Shape, Identifier>;
} }
} }
/** /**
* Get current model instance properties. * Get current model instance properties.
*/ */
getInstanceProperties(): ModelPropertiesValues<T, Shape> { getInstanceProperties(): ModelPropertiesValues<T, Shape>
{
// Initialize an empty model properties object. // Initialize an empty model properties object.
const instanceProperties: Partial<ModelPropertiesValues<T, Shape>> = {}; const instanceProperties: Partial<ModelPropertiesValues<T, Shape>> = {};
for (const property of this.properties) { for (const property of this.properties)
// For each defined model property, adding it to the properties object. { // For each defined model property, adding it to the properties object.
instanceProperties[property.name] = instanceProperties[property.name] = this.instance?.[property.name as keyof T]; // keyof Shape is a subset of keyof T.
this.instance?.[property.name as keyof T]; // keyof Shape is a subset of keyof T.
} }
return instanceProperties as ModelPropertiesValues<T, Shape>; // Returning the properties object. return instanceProperties as ModelPropertiesValues<T, Shape>; // Returning the properties object.
@ -296,12 +256,13 @@ export class Model<
/** /**
* Serialize the model instance. * Serialize the model instance.
*/ */
serialize(): SerializedModel<T, Shape> { serialize(): SerializedModel<T, Shape>
{
// Creating an empty serialized object. // Creating an empty serialized object.
const serializedObject: Partial<SerializedModel<T, Shape>> = {}; const serializedObject: Partial<SerializedModel<T, Shape>> = {};
for (const property of this.properties) { for (const property of this.properties)
// For each defined model property, adding it to the serialized object. { // For each defined model property, adding it to the serialized object.
serializedObject[property.name] = property.definition.type.serialize( serializedObject[property.name] = property.definition.type.serialize(
// keyof Shape is a subset of keyof T. // keyof Shape is a subset of keyof T.
this.instance?.[property.name as keyof T], this.instance?.[property.name as keyof T],
@ -314,22 +275,19 @@ export class Model<
/** /**
* Examine if the model is new (never deserialized) or not. * Examine if the model is new (never deserialized) or not.
*/ */
isNew(): boolean { isNew(): boolean
{
return !this.original.serialized; return !this.original.serialized;
} }
/** /**
* Examine if the model is dirty or not. * Examine if the model is dirty or not.
*/ */
isDirty(): boolean { isDirty(): boolean
for (const property of this.properties) { {
// For each property, check if it is different. for (const property of this.properties)
if ( { // For each property, check if it is different.
property.definition.type.hasChanged( if (property.definition.type.hasChanged(this.original.properties?.[property.name], this.instance?.[property.name as keyof T]))
this.original.properties?.[property.name],
this.instance?.[property.name as keyof T],
)
)
// There is a difference: the model is dirty. // There is a difference: the model is dirty.
return true; return true;
} }
@ -341,23 +299,19 @@ export class Model<
/** /**
* Serialize the difference between current model state and the original one. * Serialize the difference between current model state and the original one.
*/ */
serializeDiff(): Partial<SerializedModel<T, Shape>> { serializeDiff(): Partial<SerializedModel<T, Shape>>
{
// Creating an empty serialized object. // Creating an empty serialized object.
const serializedObject: Partial<SerializedModel<T, Shape>> = {}; const serializedObject: Partial<SerializedModel<T, Shape>> = {};
for (const property of this.properties) { for (const property of this.properties)
// For each defined model property, adding it to the serialized object if it has changed or if it is in the identifier. { // For each defined model property, adding it to the serialized object if it has changed or if it is in the identifier.
const instancePropValue = this.instance?.[property.name as keyof T]; // keyof Shape is a subset of keyof T. const instancePropValue = this.instance?.[property.name as keyof T]; // keyof Shape is a subset of keyof T.
if ( if (
property.identifier || property.identifier ||
property.definition.type.hasChanged( property.definition.type.hasChanged(this.original.properties?.[property.name], instancePropValue)
this.original.properties?.[property.name], ) // The property is part of the identifier or its value has changed.
instancePropValue, serializedObject[property.name] = property.definition.type.serializeDiff(instancePropValue);
)
)
// The property is part of the identifier or its value has changed.
serializedObject[property.name] =
property.definition.type.serializeDiff(instancePropValue);
} }
return serializedObject; // Returning the serialized object. return serializedObject; // Returning the serialized object.
@ -366,13 +320,13 @@ export class Model<
/** /**
* Set current model properties as original values. * Set current model properties as original values.
*/ */
resetDiff(): void { resetDiff(): void
{
this.original.properties = {}; this.original.properties = {};
for (const property of this.properties) { for (const property of this.properties)
// For each property, set its original value to the current property value. { // For each property, set its original value to the current property value.
const instancePropValue = this.instance?.[property.name as keyof T]; const instancePropValue = this.instance?.[property.name as keyof T];
this.original.properties[property.name] = this.original.properties[property.name] = property.definition.type.clone(instancePropValue);
property.definition.type.clone(instancePropValue);
property.definition.type.resetDiff(instancePropValue); property.definition.type.resetDiff(instancePropValue);
} }
} }
@ -381,7 +335,8 @@ export class Model<
* Get difference between original values and current ones, then reset it. * Get difference between original values and current ones, then reset it.
* Similar to call `serializeDiff()` then `resetDiff()`. * Similar to call `serializeDiff()` then `resetDiff()`.
*/ */
patch(): Partial<SerializedModel<T, Shape>> { patch(): Partial<SerializedModel<T, Shape>>
{
// Get the difference. // Get the difference.
const diff = this.serializeDiff(); const diff = this.serializeDiff();
@ -394,36 +349,32 @@ export class Model<
/** /**
* Clone the model instance. * Clone the model instance.
*/ */
clone(): ModelInstance<T, Shape, Identifier> { clone(): ModelInstance<T, Shape, Identifier>
{
// Initialize a new instance for the clone. // Initialize a new instance for the clone.
const cloned = this.manager.model(); const cloned = this.manager.model();
// Clone every value of the model instance. // Clone every value of the model instance.
for (const [key, value] of Object.entries(this.instance) as [ for (const [key, value] of Object.entries(this.instance) as [keyof T, unknown][])
keyof T, { // For each [key, value], clone the value and put it in the cloned instance.
unknown,
][]) {
// For each [key, value], clone the value and put it in the cloned instance.
// Do not clone ourselves. // Do not clone ourselves.
if (key == "_sharkitek") continue; if (key == "_sharkitek") continue;
if (this.definition.properties[key]) { if (this.definition.properties[key])
// The current key is a defined property, clone using the defined type. { // The current key is a defined property, clone using the defined type.
(cloned.instance[key] as any) = ( (cloned.instance[key] as any) = (this.definition.properties[key] as UnknownDefinition).type.clone(value);
this.definition.properties[key] as UnknownDefinition }
).type.clone(value); else
} else { { // Not a property, cloning the raw value.
// Not a property, cloning the raw value.
(cloned.instance[key] as any) = structuredClone(value); (cloned.instance[key] as any) = structuredClone(value);
} }
} }
// Clone original properties. // Clone original properties.
for (const property of this.properties) { for (const property of this.properties)
// For each property, clone its original value. { // For each property, clone its original value.
cloned.original.properties[property.name] = cloned.original.properties[property.name] = property.definition.type.clone(this.original.properties[property.name]);
property.definition.type.clone(this.original.properties[property.name]);
} }
// Clone original serialized. // Clone original serialized.
@ -437,11 +388,10 @@ export class Model<
* Fields that cannot be matched to existing properties are silently ignored. * Fields that cannot be matched to existing properties are silently ignored.
* @param fields The fields to assign to the model. * @param fields The fields to assign to the model.
*/ */
assign( assign(fields: Partial<ModelPropertiesValues<T, Shape>> & {[field: string]: any}): ModelInstance<T, Shape, Identifier>
fields: Partial<ModelPropertiesValues<T, Shape>> & {[field: string]: any}, {
): ModelInstance<T, Shape, Identifier> { for (const field in fields)
for (const field in fields) { { // For each field, if it's a property, assign its value.
// For each field, if it's a property, assign its value.
if ((this.definition.properties as any)?.[field]) if ((this.definition.properties as any)?.[field])
// Set the instance value. // Set the instance value.
this.instance[field as keyof T] = fields[field]; this.instance[field as keyof T] = fields[field];
@ -454,38 +404,26 @@ export class Model<
* @param patch The patch object to apply. * @param patch The patch object to apply.
* @param updateOriginals Indicates if the original properties values must be updated or not. By default, they are reset. * @param updateOriginals Indicates if the original properties values must be updated or not. By default, they are reset.
*/ */
applyPatch( applyPatch(patch: SerializedModel<T, Shape>, updateOriginals: boolean = true): ModelInstance<T, Shape, Identifier>
patch: SerializedModel<T, Shape>, {
updateOriginals: boolean = true, if (updateOriginals)
): ModelInstance<T, Shape, Identifier> { { // If serialized original is null and we need to update it, initialize it.
if (updateOriginals) {
// If serialized original is null and we need to update it, initialize it.
this.original.serialized = this.serialize(); this.original.serialized = this.serialize();
} }
for (const serializedField in patch) { for (const serializedField in patch)
// For each field, if it's a property, assign its value. { // For each field, if it's a property, assign its value.
// Get the property definition. // Get the property definition.
const property = const property = this.definition.properties[serializedField as keyof Shape];
this.definition.properties[serializedField as keyof Shape]; if (property)
if (property) { { // Found a matching model property, assigning its deserialized value.
// Found a matching model property, assigning its deserialized value. (this.instance[serializedField as keyof Shape as keyof T] as any) =
(this.instance[serializedField as keyof Shape as keyof T] as any) = ( (property as UnknownDefinition).type.applyPatch(this.instance[serializedField as keyof Shape as keyof T], patch[serializedField], updateOriginals);
property as UnknownDefinition
).type.applyPatch(
this.instance[serializedField as keyof Shape as keyof T],
patch[serializedField],
updateOriginals,
);
if (updateOriginals) { if (updateOriginals)
// Update original values. { // Update original values.
// Set original property value. // Set original property value.
(this.original.properties[serializedField] as any) = ( (this.original.properties[serializedField] as any) = (property as UnknownDefinition).type.clone(this.instance[serializedField as keyof Shape as keyof T]);
property as UnknownDefinition
).type.clone(
this.instance[serializedField as keyof Shape as keyof T],
);
// Set original serialized value. // Set original serialized value.
this.original.serialized[serializedField] = patch[serializedField]; this.original.serialized[serializedField] = patch[serializedField];
} }
@ -495,14 +433,13 @@ export class Model<
} }
} }
/** /**
* A model manager, created from a model definition. * A model manager, created from a model definition.
*/ */
export class ModelManager< export class ModelManager<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>>
T extends object, {
Shape extends ModelShape<T>,
Identifier extends IdentifierDefinition<T, Shape>,
> {
/** /**
* Defined properties. * Defined properties.
*/ */
@ -512,9 +449,8 @@ export class ModelManager<
* Initialize a model manager from a model definition. * Initialize a model manager from a model definition.
* @param definition The model definition. * @param definition The model definition.
*/ */
constructor( constructor(public readonly definition: ModelDefinition<T, Shape, Identifier>)
public readonly definition: ModelDefinition<T, Shape, Identifier>, {
) {
this.initProperties(); this.initProperties();
} }
@ -522,22 +458,23 @@ export class ModelManager<
* Initialize properties iterator from current definition. * Initialize properties iterator from current definition.
* @protected * @protected
*/ */
protected initProperties(): void { protected initProperties(): void
{
// Build an array of model properties from the definition. // Build an array of model properties from the definition.
this.properties = []; this.properties = [];
for (const propertyName in this.definition.properties) { for (const propertyName in this.definition.properties)
// For each property, build a model property object. { // For each property, build a model property object.
this.properties.push({ this.properties.push({
name: propertyName, name: propertyName,
definition: this.definition.properties[propertyName], definition: this.definition.properties[propertyName],
// Find out if the current property is part of the identifier. // Find out if the current property is part of the identifier.
identifier: Array.isArray(this.definition.identifier) identifier: (
? // The identifier is an array, the property must be in the array. Array.isArray(this.definition.identifier)
this.definition.identifier.includes( // The identifier is an array, the property must be in the array.
propertyName as keyof Shape as keyof T, ? this.definition.identifier.includes(propertyName as keyof Shape as keyof T)
) // The identifier is a single string, the property must be the defined identifier.
: // The identifier is a single string, the property must be the defined identifier. : (this.definition.identifier == propertyName as keyof Shape)
this.definition.identifier == (propertyName as keyof Shape), ),
} as ModelProperty<T, Shape>); } as ModelProperty<T, Shape>);
} }
} }
@ -546,19 +483,14 @@ export class ModelManager<
* Get the model state of the provided model instance. * Get the model state of the provided model instance.
* @param instance The model instance for which to get its state. NULL or undefined to create a new one. * @param instance The model instance for which to get its state. NULL or undefined to create a new one.
*/ */
model( model(instance: T|ModelInstance<T, Shape, Identifier>|null = null): Model<T, Shape, Identifier>
instance: T | ModelInstance<T, Shape, Identifier> | null = null, { // Get the instance model state if there is one, or initialize a new one.
): Model<T, Shape, Identifier> {
// Get the instance model state if there is one, or initialize a new one.
if (instance) if (instance)
// There is an instance, create a model from it. // There is an instance, create a model from it.
return ( return ((instance as ModelInstance<T, Shape, Identifier>)?._sharkitek ?? (new Model<T, Shape, Identifier>(this))).fromInstance(instance);
(instance as ModelInstance<T, Shape, Identifier>)?._sharkitek ??
new Model<T, Shape, Identifier>(this)
).fromInstance(instance);
else else
// No instance, initialize a new one. // No instance, initialize a new one.
return new Model<T, Shape, Identifier>(this).initInstance(); return (new Model<T, Shape, Identifier>(this)).initInstance();
} }
/** /**
@ -566,9 +498,8 @@ export class ModelManager<
* Fields that cannot be matched to existing properties are silently ignored. * Fields that cannot be matched to existing properties are silently ignored.
* @param fields * @param fields
*/ */
from( from(fields: Partial<ModelPropertiesValues<T, Shape>> & {[field: string]: any}): ModelInstance<T, Shape, Identifier>
fields: Partial<ModelPropertiesValues<T, Shape>> & {[field: string]: any}, {
): ModelInstance<T, Shape, Identifier> {
return this.model().assign(fields); return this.model().assign(fields);
} }
@ -576,9 +507,8 @@ export class ModelManager<
* Parse the serialized model object to a new model instance. * Parse the serialized model object to a new model instance.
* @param serialized The serialized model object. * @param serialized The serialized model object.
*/ */
parse( parse(serialized: SerializedModel<T, Shape>): ModelInstance<T, Shape, Identifier>
serialized: SerializedModel<T, Shape>, {
): ModelInstance<T, Shape, Identifier> {
return this.model().deserialize(serialized).instance; return this.model().deserialize(serialized).instance;
} }
} }
@ -587,11 +517,10 @@ export class ModelManager<
* Define a new model. * Define a new model.
* @param definition The model definition object. * @param definition The model definition object.
*/ */
export function defineModel< export function defineModel<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>>(
T extends object, definition: ModelDefinition<T, Shape, Identifier>
Shape extends ModelShape<T>, )
Identifier extends IdentifierDefinition<T, Shape>, {
>(definition: ModelDefinition<T, Shape, Identifier>) {
return new ModelManager<T, Shape, Identifier>(definition); return new ModelManager<T, Shape, Identifier>(definition);
} }
@ -601,21 +530,16 @@ export function defineModel<
* @param definition The extension of the model definition object. * @param definition The extension of the model definition object.
*/ */
export function extend< export function extend<
ExtT extends object, ExtT extends object, ExtShape extends ModelShape<ExtT>, ExtIdentifier extends IdentifierDefinition<ExtT, ExtShape>,
ExtShape extends ModelShape<ExtT>, T extends ExtT, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>,
ExtIdentifier extends IdentifierDefinition<ExtT, ExtShape>, ResIdentifier extends IdentifierDefinition<T, ResShape>, ResShape extends ModelShape<T> = Modify<ExtShape, Shape>
T extends ExtT,
Shape extends ModelShape<T>,
Identifier extends IdentifierDefinition<T, Shape>,
ResIdentifier extends IdentifierDefinition<T, ResShape>,
ResShape extends ModelShape<T> = Modify<ExtShape, Shape>,
>( >(
extendedModel: ModelManager<ExtT, ExtShape, ExtIdentifier>, extendedModel: ModelManager<ExtT, ExtShape, ExtIdentifier>,
definition: ModelDefinition<T, Shape, Identifier>, definition: ModelDefinition<T, Shape, Identifier>,
) { )
const {properties: extendedProperties, ...overridableDefinition} = {
extendedModel.definition; const { properties: extendedProperties, ...overridableDefinition } = extendedModel.definition;
const {properties: propertiesExtension, ...definitionExtension} = definition; const { properties: propertiesExtension, ...definitionExtension } = definition;
return new ModelManager({ return new ModelManager({
...overridableDefinition, ...overridableDefinition,
...definitionExtension, ...definitionExtension,
@ -629,25 +553,15 @@ export function extend<
/** /**
* A generic model manager for a provided model type, to use in circular dependencies. * A generic model manager for a provided model type, to use in circular dependencies.
*/ */
export type GenericModelManager<T extends object> = ModelManager< export type GenericModelManager<T extends object> = ModelManager<T, ModelShape<T>, IdentifierDefinition<T, ModelShape<T>>>;
T,
ModelShape<T>,
IdentifierDefinition<T, ModelShape<T>>
>;
/** /**
* Function to get a model manager lazily. * Function to get a model manager lazily.
*/ */
export type LazyModelManager< export type LazyModelManager<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>>
T extends object, = (() => ModelManager<T, Shape, Identifier>);
Shape extends ModelShape<T>,
Identifier extends IdentifierDefinition<T, Shape>,
> = () => ModelManager<T, Shape, Identifier>;
/** /**
* A model manager definition that can be lazy. * A model manager definition that can be lazy.
*/ */
export type DeclaredModelManager< export type DeclaredModelManager<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>>
T extends object, = ModelManager<T, Shape, Identifier>|LazyModelManager<T, Shape, Identifier>;
Shape extends ModelShape<T>,
Identifier extends IdentifierDefinition<T, Shape>,
> = ModelManager<T, Shape, Identifier> | LazyModelManager<T, Shape, Identifier>;

View file

@ -3,7 +3,8 @@ import {Type} from "./types/type";
/** /**
* Property definition class. * Property definition class.
*/ */
export class Definition<SerializedType, ModelType> { export class Definition<SerializedType, ModelType>
{
readonly _sharkitek: ModelType; readonly _sharkitek: ModelType;
readonly _serialized: SerializedType; readonly _serialized: SerializedType;
@ -11,7 +12,8 @@ export class Definition<SerializedType, ModelType> {
* Create a property definer instance. * Create a property definer instance.
* @param type Property type. * @param type Property type.
*/ */
constructor(public readonly type: Type<SerializedType, ModelType>) {} constructor(public readonly type: Type<SerializedType, ModelType>)
{}
} }
/** /**
@ -28,8 +30,7 @@ export type AnyDefinition = Definition<any, any>;
* New definition of a property of the given type. * New definition of a property of the given type.
* @param type Type of the property to define. * @param type Type of the property to define.
*/ */
export function define<SerializedType, ModelType>( export function define<SerializedType, ModelType>(type: Type<SerializedType, ModelType>): Definition<SerializedType, ModelType>
type: Type<SerializedType, ModelType>, {
): Definition<SerializedType, ModelType> {
return new Definition(type); return new Definition(type);
} }

View file

@ -5,70 +5,56 @@ import {InvalidTypeValueError} from "../../errors";
/** /**
* Type of an array of values. * Type of an array of values.
*/ */
export class ArrayType<SerializedValueType, SharkitekValueType> extends Type< export class ArrayType<SerializedValueType, SharkitekValueType> extends Type<SerializedValueType[], SharkitekValueType[]>
SerializedValueType[], {
SharkitekValueType[]
> {
/** /**
* Initialize a new array type of a Sharkitek model property. * Initialize a new array type of a Sharkitek model property.
* @param valueDefinition Definition the array values. * @param valueDefinition Definition the array values.
*/ */
constructor( constructor(protected valueDefinition: Definition<SerializedValueType, SharkitekValueType>)
protected valueDefinition: Definition< {
SerializedValueType,
SharkitekValueType
>,
) {
super(); super();
} }
serialize( serialize(value: SharkitekValueType[]|null|undefined): SerializedValueType[]|null|undefined
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;
if (!Array.isArray(value)) if (!Array.isArray(value)) throw new InvalidTypeValueError(this, value, "value must be an array");
throw new InvalidTypeValueError(this, value, "value must be an array");
return value.map((value) => return value.map((value) => (
// Serializing each value of the array. // Serializing each value of the array.
this.valueDefinition.type.serialize(value), this.valueDefinition.type.serialize(value)
); ));
} }
deserialize( deserialize(value: SerializedValueType[]|null|undefined): SharkitekValueType[]|null|undefined
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;
if (!Array.isArray(value)) if (!Array.isArray(value)) throw new InvalidTypeValueError(this, value, "value must be an array");
throw new InvalidTypeValueError(this, value, "value must be an array");
return value.map((serializedValue) => return value.map((serializedValue) => (
// Deserializing each value of the array. // Deserializing each value of the array.
this.valueDefinition.type.deserialize(serializedValue), this.valueDefinition.type.deserialize(serializedValue)
); ));
} }
serializeDiff( serializeDiff(value: SharkitekValueType[]|null|undefined): SerializedValueType[]|null|undefined
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;
if (!Array.isArray(value)) if (!Array.isArray(value)) throw new InvalidTypeValueError(this, value, "value must be an array");
throw new InvalidTypeValueError(this, value, "value must be an array");
// Serializing diff of all elements. // Serializing diff of all elements.
return value.map( return value.map((value) => this.valueDefinition.type.serializeDiff(value) as SerializedValueType);
(value) =>
this.valueDefinition.type.serializeDiff(value) as SerializedValueType,
);
} }
resetDiff(value: SharkitekValueType[] | null | undefined): 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;
@ -76,24 +62,16 @@ export class ArrayType<SerializedValueType, SharkitekValueType> extends Type<
value.forEach((value) => this.valueDefinition.type.resetDiff(value)); value.forEach((value) => this.valueDefinition.type.resetDiff(value));
} }
hasChanged( hasChanged(originalValue: SharkitekValueType[]|null|undefined, currentValue: SharkitekValueType[]|null|undefined): boolean
originalValue: SharkitekValueType[] | null | undefined, {
currentValue: SharkitekValueType[] | null | undefined,
): boolean {
// If any array length is different, arrays are different. // If any array length is different, arrays are different.
if (originalValue?.length != currentValue?.length) return true; if (originalValue?.length != currentValue?.length) return true;
// If length is undefined, values are probably not arrays. // If length is undefined, values are probably not arrays.
if (originalValue?.length == undefined) if (originalValue?.length == undefined) return super.hasChanged(originalValue, currentValue);
return super.hasChanged(originalValue, currentValue);
for (const key of originalValue.keys()) { for (const key of originalValue.keys())
// Check for any change for each value in the array. { // Check for any change for each value in the array.
if ( if (this.valueDefinition.type.hasChanged(originalValue[key], currentValue[key]))
this.valueDefinition.type.hasChanged(
originalValue[key],
currentValue[key],
)
)
// The value has changed, the array is different. // The value has changed, the array is different.
return true; return true;
} }
@ -101,24 +79,16 @@ export class ArrayType<SerializedValueType, SharkitekValueType> extends Type<
return false; // No change detected. return false; // No change detected.
} }
serializedHasChanged( serializedHasChanged(originalValue: SerializedValueType[] | null | undefined, currentValue: SerializedValueType[] | null | undefined): boolean
originalValue: SerializedValueType[] | null | undefined, {
currentValue: SerializedValueType[] | null | undefined,
): boolean {
// If any array length is different, arrays are different. // If any array length is different, arrays are different.
if (originalValue?.length != currentValue?.length) return true; if (originalValue?.length != currentValue?.length) return true;
// If length is undefined, values are probably not arrays. // If length is undefined, values are probably not arrays.
if (originalValue?.length == undefined) if (originalValue?.length == undefined) return super.serializedHasChanged(originalValue, currentValue);
return super.serializedHasChanged(originalValue, currentValue);
for (const key of originalValue.keys()) { for (const key of originalValue.keys())
// Check for any change for each value in the array. { // Check for any change for each value in the array.
if ( if (this.valueDefinition.type.serializedHasChanged(originalValue[key], currentValue[key]))
this.valueDefinition.type.serializedHasChanged(
originalValue[key],
currentValue[key],
)
)
// The value has changed, the array is different. // The value has changed, the array is different.
return true; return true;
} }
@ -126,50 +96,41 @@ export class ArrayType<SerializedValueType, SharkitekValueType> extends Type<
return false; // No change detected. return false; // No change detected.
} }
clone<T extends SharkitekValueType[]>(array: T | null | undefined): T { clone<T extends SharkitekValueType[]>(array: T|null|undefined): T
{
// Handle NULL / undefined array. // Handle NULL / undefined array.
if (!array) return super.clone(array); if (!array) return super.clone(array);
if (!Array.isArray(array)) if (!Array.isArray(array)) throw new InvalidTypeValueError(this, array, "value must be an array");
throw new InvalidTypeValueError(this, array, "value must be an array");
// Initialize an empty array. // Initialize an empty array.
const cloned = [] as T; const cloned = [] as T;
for (const value of array) { for (const value of array)
// Clone each value of the array. { // Clone each value of the array.
cloned.push(this.valueDefinition.type.clone(value)); cloned.push(this.valueDefinition.type.clone(value));
} }
return cloned; // Returning cloned array. return cloned; // Returning cloned array.
} }
applyPatch<T extends SharkitekValueType[]>( applyPatch<T extends SharkitekValueType[]>(currentValue: T|null|undefined, patchValue: SerializedValueType[]|null|undefined, updateOriginals: boolean): T|null|undefined
currentValue: T | null | undefined, {
patchValue: SerializedValueType[] | null | undefined,
updateOriginals: boolean,
): T | null | undefined {
if (patchValue === undefined) return undefined; if (patchValue === undefined) return undefined;
if (patchValue === null) return null; if (patchValue === null) return null;
if (!Array.isArray(patchValue)) if (!Array.isArray(patchValue))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, patchValue, "value must be an array");
this,
patchValue,
"value must be an array",
);
currentValue = Array.isArray(currentValue) ? currentValue : ([] as T); currentValue = Array.isArray(currentValue) ? currentValue : [] as T;
for (let i = 0; i < patchValue.length; i++) { for (let i = 0; i < patchValue.length; i++)
// Apply the patch to all values of the array. { // Apply the patch to all values of the array.
const patchedElement = this.valueDefinition.type.applyPatch( const patchedElement = this.valueDefinition.type.applyPatch(currentValue?.[i], patchValue[i], updateOriginals);
currentValue?.[i], if (i < currentValue.length)
patchValue[i], currentValue[i] = patchedElement;
updateOriginals, else
); currentValue.push(patchedElement);
if (i < currentValue.length) currentValue[i] = patchedElement;
else currentValue.push(patchedElement);
} }
return currentValue; return currentValue;
@ -180,8 +141,7 @@ export class ArrayType<SerializedValueType, SharkitekValueType> extends Type<
* New array property definition. * New array property definition.
* @param valueDefinition Array values type definition. * @param valueDefinition Array values type definition.
*/ */
export function array<SerializedValueType, SharkitekValueType>( export function array<SerializedValueType, SharkitekValueType>(valueDefinition: Definition<SerializedValueType, SharkitekValueType>): Definition<SerializedValueType[], SharkitekValueType[]>
valueDefinition: Definition<SerializedValueType, SharkitekValueType>, {
): Definition<SerializedValueType[], SharkitekValueType[]> {
return define(new ArrayType(valueDefinition)); return define(new ArrayType(valueDefinition));
} }

View file

@ -4,8 +4,10 @@ import {define, Definition} from "../property-definition";
/** /**
* Type of any boolean value. * Type of any boolean value.
*/ */
export class BooleanType extends Type<boolean, boolean> { export class BooleanType extends Type<boolean, boolean>
deserialize(value: boolean | null | undefined): boolean | null | undefined { {
deserialize(value: boolean|null|undefined): boolean|null|undefined
{
// Keep NULL and undefined values. // Keep NULL and undefined values.
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
@ -13,7 +15,8 @@ export class BooleanType extends Type<boolean, boolean> {
return !!value; // ensure bool type. return !!value; // ensure bool type.
} }
serialize(value: boolean | null | undefined): boolean | null | undefined { serialize(value: boolean|null|undefined): boolean|null|undefined
{
// Keep NULL and undefined values. // Keep NULL and undefined values.
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
@ -25,13 +28,15 @@ export class BooleanType extends Type<boolean, boolean> {
/** /**
* New boolean property definition. * New boolean property definition.
*/ */
export function boolean(): Definition<boolean, boolean> { export function boolean(): Definition<boolean, boolean>
{
return define(new BooleanType()); return define(new BooleanType());
} }
/** /**
* New boolean property definition. * New boolean property definition.
* Alias of boolean. * Alias of boolean.
*/ */
export function bool(): ReturnType<typeof boolean> { export function bool(): ReturnType<typeof boolean>
{
return boolean(); return boolean();
} }

View file

@ -5,30 +5,30 @@ import {InvalidTypeValueError} from "../../errors";
/** /**
* Type of dates. * Type of dates.
*/ */
export class DateType extends Type<string, Date> { export class DateType extends Type<string, Date>
deserialize(value: string | null | undefined): Date | null | undefined { {
deserialize(value: string|null|undefined): Date|null|undefined
{
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
return new Date(value); return new Date(value);
} }
serialize(value: Date | null | undefined): string | null | undefined { serialize(value: Date|null|undefined): string|null|undefined
{
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
if (!(value instanceof Date)) if (!(value instanceof Date)) throw new InvalidTypeValueError(this, value, "value must be a date");
throw new InvalidTypeValueError(this, value, "value must be a date");
if (isNaN(value?.valueOf())) return value?.toString(); if (isNaN(value?.valueOf())) return value?.toString();
return value?.toISOString(); return value?.toISOString();
} }
hasChanged( hasChanged(originalValue: Date|null|undefined, currentValue: Date|null|undefined): boolean
originalValue: Date | null | undefined, {
currentValue: Date | null | undefined, if (originalValue instanceof Date && currentValue instanceof Date)
): boolean { { // Compare dates.
if (originalValue instanceof Date && currentValue instanceof Date) {
// Compare dates.
const originalTime = originalValue.getTime(); const originalTime = originalValue.getTime();
const currentTime = currentValue.getTime(); const currentTime = currentValue.getTime();
@ -37,7 +37,8 @@ export class DateType extends Type<string, Date> {
// Timestamps need to be exactly the same. // Timestamps need to be exactly the same.
return originalValue.getTime() !== currentValue.getTime(); return originalValue.getTime() !== currentValue.getTime();
} else }
else
// Compare undefined or null values. // Compare undefined or null values.
return originalValue !== currentValue; return originalValue !== currentValue;
} }
@ -46,6 +47,7 @@ export class DateType extends Type<string, Date> {
/** /**
* New date property definition. * New date property definition.
*/ */
export function date(): Definition<string, Date> { export function date(): Definition<string, Date>
{
return define(new DateType()); return define(new DateType());
} }

View file

@ -5,19 +5,21 @@ import {InvalidTypeValueError} from "../../errors";
/** /**
* 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 | null | undefined): number | null | undefined { {
deserialize(value: string|null|undefined): number|null|undefined
{
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
return parseFloat(value); return parseFloat(value);
} }
serialize(value: number | null | undefined): string | null | undefined { serialize(value: number|null|undefined): string|null|undefined
{
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
if (typeof value !== "number" && typeof value !== "string") if (typeof value !== "number" && typeof value !== "string") throw new InvalidTypeValueError(this, value, "value must be a number");
throw new InvalidTypeValueError(this, value, "value must be a number");
return value?.toString(); return value?.toString();
} }
@ -26,6 +28,7 @@ export class DecimalType extends Type<string, number> {
/** /**
* New decimal property definition. * New decimal property definition.
*/ */
export function decimal(): Definition<string, number> { export function decimal(): Definition<string, number>
{
return define(new DecimalType()); return define(new DecimalType());
} }

View file

@ -1,219 +1,155 @@
import {Type} from "./type"; import {Type} from "./type";
import {define, Definition} from "../property-definition"; import {define, Definition} from "../property-definition";
import {InvalidTypeValueError} from "../../errors"; import {InvalidTypeValueError} from "../../errors";
import {type} from "node:os";
import {string} from "./string"; import {string} from "./string";
/** /**
* Type of a key-value map. * Type of a key-value map.
*/ */
export class MapType< export class MapType<KeyType, ValueType, SerializedValueType, SerializedMapType extends Record<string, SerializedValueType> = Record<string, SerializedValueType>> extends Type<SerializedMapType, Map<KeyType, ValueType>>
KeyType, {
ValueType, /**
SerializedValueType, * Initialize a new map type of a Sharkitek model property.
SerializedMapType extends Record<string, SerializedValueType> = Record< * @param keyDefinition Definition of the map keys.
string, * @param valueDefinition Definition of the map values.
SerializedValueType */
>, constructor(
> extends Type<SerializedMapType, Map<KeyType, ValueType>> { protected keyDefinition: Definition<string, KeyType>,
/** protected valueDefinition: Definition<SerializedValueType, ValueType>,
* Initialize a new map type of a Sharkitek model property. )
* @param keyDefinition Definition of the map keys. {
* @param valueDefinition Definition of the map values. super();
*/ }
constructor(
protected keyDefinition: Definition<string, KeyType>,
protected valueDefinition: Definition<SerializedValueType, ValueType>,
) {
super();
}
serialize( serialize(value: Map<KeyType, ValueType>|null|undefined): SerializedMapType|null|undefined
value: Map<KeyType, ValueType> | null | undefined, {
): SerializedMapType | null | undefined { if (value === undefined) return undefined;
if (value === undefined) return undefined; if (value === null) return null;
if (value === null) return null;
if (!(value instanceof Map)) if (!(value instanceof Map)) throw new InvalidTypeValueError(this, value, "value must be an instance of map");
throw new InvalidTypeValueError(
this,
value,
"value must be an instance of map",
);
return Object.fromEntries( return Object.fromEntries(
// Serializing each key-value pair of the map. // Serializing each key-value pair of the map.
value value.entries().map(([key, value]) =>
.entries() ([this.keyDefinition.type.serialize(key), this.valueDefinition.type.serialize(value)]))
.map(([key, value]) => [ ) as SerializedMapType;
this.keyDefinition.type.serialize(key), }
this.valueDefinition.type.serialize(value),
]),
) as SerializedMapType;
}
deserialize( deserialize(value: SerializedMapType|null|undefined): Map<KeyType, ValueType>|null|undefined
value: SerializedMapType | null | undefined, {
): Map<KeyType, ValueType> | null | undefined { if (value === undefined) return undefined;
if (value === undefined) return undefined; if (value === null) return null;
if (value === null) return null;
if (typeof value !== "object" || Array.isArray(value)) if (typeof value !== "object" || Array.isArray(value)) throw new InvalidTypeValueError(this, value, "value must be an object");
throw new InvalidTypeValueError(this, value, "value must be an object");
const map = new Map<KeyType, ValueType>(); const map = new Map<KeyType, ValueType>;
for (const [serializedKey, serializedValue] of Object.entries(value)) { for (const [serializedKey, serializedValue] of Object.entries(value))
// Deserializing each key-value pair of the map. { // Deserializing each key-value pair of the map.
map.set( map.set(this.keyDefinition.type.deserialize(serializedKey), this.valueDefinition.type.deserialize(serializedValue));
this.keyDefinition.type.deserialize(serializedKey), }
this.valueDefinition.type.deserialize(serializedValue),
);
}
return map; return map;
} }
serializeDiff( serializeDiff(value: Map<KeyType, ValueType>|null|undefined): SerializedMapType|null|undefined
value: Map<KeyType, ValueType> | null | undefined, {
): SerializedMapType | null | undefined { if (value === undefined) return undefined;
if (value === undefined) return undefined; if (value === null) return null;
if (value === null) return null;
if (!(value instanceof Map)) if (!(value instanceof Map)) throw new InvalidTypeValueError(this, value, "value must be an instance of map");
throw new InvalidTypeValueError(
this,
value,
"value must be an instance of map",
);
return Object.fromEntries( return Object.fromEntries(
// Serializing the diff of each key-value pair of the map. // Serializing the diff of each key-value pair of the map.
value value.entries().map(([key, value]) =>
.entries() ([this.keyDefinition.type.serializeDiff(key), this.valueDefinition.type.serializeDiff(value)]))
.map(([key, value]) => [ ) as SerializedMapType;
this.keyDefinition.type.serializeDiff(key), }
this.valueDefinition.type.serializeDiff(value),
]),
) as SerializedMapType;
}
resetDiff(value: Map<KeyType, ValueType> | null | undefined): void { resetDiff(value: Map<KeyType, ValueType>|null|undefined): void
// Do nothing if it is not a map. {
if (!(value instanceof Map)) return; // Do nothing if it is not a map.
if (!(value instanceof Map)) return;
// Reset diff of all key-value pairs. // Reset diff of all key-value pairs.
value.forEach((value, key) => { value.forEach((value, key) => {
this.keyDefinition.type.resetDiff(key); this.keyDefinition.type.resetDiff(key);
this.valueDefinition.type.resetDiff(value); this.valueDefinition.type.resetDiff(value);
}); });
} }
hasChanged( hasChanged(originalValue: Map<KeyType, ValueType>|null|undefined, currentValue: Map<KeyType, ValueType>|null|undefined): boolean
originalValue: Map<KeyType, ValueType> | null | undefined, {
currentValue: Map<KeyType, ValueType> | null | undefined, // If any map size is different, maps are different.
): boolean { if (originalValue?.size != currentValue?.size) return true;
// If any map size is different, maps are different. // If size is undefined, values are probably not maps.
if (originalValue?.size != currentValue?.size) return true; if (originalValue?.size == undefined) return super.hasChanged(originalValue, currentValue);
// If size is undefined, values are probably not maps.
if (originalValue?.size == undefined)
return super.hasChanged(originalValue, currentValue);
for (const [key, value] of originalValue.entries()) { for (const [key, value] of originalValue.entries())
// Check for any change for each key-value in the map. { // Check for any change for each key-value in the map.
if (this.valueDefinition.type.hasChanged(value, currentValue.get(key))) if (this.valueDefinition.type.hasChanged(value, currentValue.get(key)))
// The value has changed, the map is different. // The value has changed, the map is different.
return true; return true;
} }
return false; // No change detected. return false; // No change detected.
} }
serializedHasChanged( serializedHasChanged(originalValue: SerializedMapType | null | undefined, currentValue: SerializedMapType | null | undefined): boolean
originalValue: SerializedMapType | null | undefined, {
currentValue: SerializedMapType | null | undefined, // If any value is not a defined object, use the default comparison function.
): boolean { if (!originalValue || !currentValue || typeof originalValue !== "object" || typeof currentValue !== "object") return super.serializedHasChanged(originalValue, currentValue);
// If any value is not a defined object, use the default comparison function.
if (
!originalValue ||
!currentValue ||
typeof originalValue !== "object" ||
typeof currentValue !== "object"
)
return super.serializedHasChanged(originalValue, currentValue);
// If any object size is different, objects are different. // If any object size is different, objects are different.
if (Object.keys(originalValue)?.length != Object.keys(currentValue)?.length) if (Object.keys(originalValue)?.length != Object.keys(currentValue)?.length) return true;
return true;
for (const [key, value] of Object.entries(originalValue)) { for (const [key, value] of Object.entries(originalValue))
// Check for any change for each key-value pair in the object. { // Check for any change for each key-value pair in the object.
if ( if (this.valueDefinition.type.serializedHasChanged(value, currentValue[key]))
this.valueDefinition.type.serializedHasChanged(value, currentValue[key]) // The value has changed, the object is different.
) return true;
// The value has changed, the object is different. }
return true;
}
return false; // No change detected. return false; // No change detected.
} }
clone<T extends Map<KeyType, ValueType>>(map: T | null | undefined): T { clone<T extends Map<KeyType, ValueType>>(map: T|null|undefined): T
// Handle NULL / undefined map. {
if (!map) return super.clone(map); // Handle NULL / undefined map.
if (!map) return super.clone(map);
if (!(map instanceof Map)) if (!(map instanceof Map)) throw new InvalidTypeValueError(this, map, "value must be an instance of map");
throw new InvalidTypeValueError(
this,
map,
"value must be an instance of map",
);
// Initialize an empty map. // Initialize an empty map.
const cloned = new Map<KeyType, ValueType>() as T; const cloned = new Map<KeyType, ValueType>() as T;
for (const [key, value] of map.entries()) { for (const [key, value] of map.entries())
// Clone each value of the map. { // Clone each value of the map.
cloned.set( cloned.set(this.keyDefinition.type.clone(key), this.valueDefinition.type.clone(value));
this.keyDefinition.type.clone(key), }
this.valueDefinition.type.clone(value),
);
}
return cloned; // Returning cloned map. return cloned; // Returning cloned map.
} }
applyPatch<T extends Map<KeyType, ValueType>>( applyPatch<T extends Map<KeyType, ValueType>>(currentValue: T|null|undefined, patchValue: SerializedMapType|null|undefined, updateOriginals: boolean): T|null|undefined
currentValue: T | null | undefined, {
patchValue: SerializedMapType | null | undefined, if (patchValue === undefined) return undefined;
updateOriginals: boolean, if (patchValue === null) return null;
): T | null | undefined {
if (patchValue === undefined) return undefined;
if (patchValue === null) return null;
if (typeof patchValue !== "object") if (typeof patchValue !== "object")
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, patchValue, "value must be an object");
this,
patchValue,
"value must be an object",
);
currentValue = currentValue = currentValue instanceof Map ? currentValue : new Map<KeyType, ValueType>() as T;
currentValue instanceof Map
? currentValue
: (new Map<KeyType, ValueType>() as T);
for (const [key, value] of Object.entries(patchValue)) { for (const [key, value] of Object.entries(patchValue))
// Apply the patch to all values of the map. { // Apply the patch to all values of the map.
const patchedKey = this.keyDefinition.type.deserialize(key); const patchedKey = this.keyDefinition.type.deserialize(key);
const patchedElement = this.valueDefinition.type.applyPatch( const patchedElement = this.valueDefinition.type.applyPatch(currentValue.get(patchedKey), value, updateOriginals);
currentValue.get(patchedKey), currentValue.set(patchedKey, patchedElement);
value, }
updateOriginals,
);
currentValue.set(patchedKey, patchedElement);
}
return currentValue; return currentValue;
} }
} }
/** /**
@ -221,44 +157,21 @@ export class MapType<
* @param keyDefinition Definition of the map keys. * @param keyDefinition Definition of the map keys.
* @param valueDefinition Definition of the map values. * @param valueDefinition Definition of the map values.
*/ */
export function map< export function map<KeyType, ValueType, SerializedValueType, SerializedMapType extends Record<string, SerializedValueType> = Record<string, SerializedValueType>>(
KeyType, keyDefinition: Definition<string, KeyType>,
ValueType, valueDefinition: Definition<SerializedValueType, ValueType>,
SerializedValueType, ): Definition<SerializedMapType, Map<KeyType, ValueType>>
SerializedMapType extends Record<string, SerializedValueType> = Record< {
string, return define(new MapType<KeyType, ValueType, SerializedValueType, SerializedMapType>(keyDefinition, valueDefinition));
SerializedValueType
>,
>(
keyDefinition: Definition<string, KeyType>,
valueDefinition: Definition<SerializedValueType, ValueType>,
): Definition<SerializedMapType, Map<KeyType, ValueType>> {
return define(
new MapType<KeyType, ValueType, SerializedValueType, SerializedMapType>(
keyDefinition,
valueDefinition,
),
);
} }
/** /**
* New map property definition, with string as index. * New map property definition, with string as index.
* @param valueDefinition Definition of the map values. * @param valueDefinition Definition of the map values.
*/ */
export function stringMap< export function stringMap<ValueType, SerializedValueType, SerializedMapType extends Record<string, SerializedValueType> = Record<string, SerializedValueType>>(
ValueType, valueDefinition: Definition<SerializedValueType, ValueType>,
SerializedValueType, ): Definition<SerializedMapType, Map<string, ValueType>>
SerializedMapType extends Record<string, SerializedValueType> = Record< {
string, return define(new MapType<string, ValueType, SerializedValueType, SerializedMapType>(string(), valueDefinition));
SerializedValueType
>,
>(
valueDefinition: Definition<SerializedValueType, ValueType>,
): Definition<SerializedMapType, Map<string, ValueType>> {
return define(
new MapType<string, ValueType, SerializedValueType, SerializedMapType>(
string(),
valueDefinition,
),
);
} }

View file

@ -2,62 +2,50 @@ import {Type} from "./type";
import {define, Definition} from "../property-definition"; import {define, Definition} from "../property-definition";
import { import {
GenericModelManager, GenericModelManager,
IdentifierDefinition, IdentifierDefinition, DeclaredModelManager,
DeclaredModelManager,
ModelInstance, ModelInstance,
ModelManager, ModelManager,
ModelShape, ModelShape,
SerializedModel, SerializedModel
} from "../model"; } from "../model";
import {InvalidTypeValueError} from "../../errors"; import {InvalidTypeValueError} from "../../errors";
/** /**
* Type of a Sharkitek model value. * Type of a Sharkitek model value.
*/ */
export class ModelType< export class ModelType<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>> extends Type<SerializedModel<T, Shape>, ModelInstance<T, Shape, Identifier>>
T extends object, {
Shape extends ModelShape<T>,
Identifier extends IdentifierDefinition<T, Shape>,
> extends Type<SerializedModel<T, Shape>, ModelInstance<T, Shape, Identifier>> {
/** /**
* Initialize a new model type of a Sharkitek model property. * Initialize a new model type of a Sharkitek model property.
* @param declaredModelManager Model manager. * @param declaredModelManager Model manager.
*/ */
constructor( constructor(protected declaredModelManager: DeclaredModelManager<T, Shape, Identifier>)
protected declaredModelManager: DeclaredModelManager<T, Shape, Identifier>, {
) {
super(); super();
} }
/** /**
* Resolve the defined model using the declared model, that can be defined lazily. * Resolve the defined model using the declared model, that can be defined lazily.
*/ */
get definedModel(): ModelManager<T, Shape, Identifier> { get definedModel(): ModelManager<T, Shape, Identifier>
return typeof this.declaredModelManager == "object" {
? this.declaredModelManager return typeof this.declaredModelManager == "object" ? this.declaredModelManager : this.declaredModelManager();
: this.declaredModelManager();
} }
serialize( serialize(value: ModelInstance<T, Shape, Identifier>|null|undefined): SerializedModel<T, Shape>|null|undefined
value: ModelInstance<T, Shape, Identifier> | null | undefined, {
): SerializedModel<T, Shape> | null | undefined {
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
if (!(value instanceof this.definedModel.definition.Class)) if (!(value instanceof this.definedModel.definition.Class))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, value, `value must be a compatible model (given ${value.constructor.name}, expected ${this.definedModel.definition.Class.name})`);
this,
value,
`value must be a compatible model (given ${value.constructor.name}, expected ${this.definedModel.definition.Class.name})`,
);
// Serializing the given model. // Serializing the given model.
return this.definedModel.model(value).serialize(); return this.definedModel.model(value).serialize();
} }
deserialize( deserialize(value: SerializedModel<T, Shape>|null|undefined): ModelInstance<T, Shape, Identifier>|null|undefined
value: SerializedModel<T, Shape> | null | undefined, {
): ModelInstance<T, Shape, Identifier> | null | undefined {
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
@ -68,135 +56,86 @@ export class ModelType<
return this.definedModel.parse(value); return this.definedModel.parse(value);
} }
serializeDiff( serializeDiff(value: ModelInstance<T, Shape, Identifier>|null|undefined): Partial<SerializedModel<T, Shape>>|null|undefined
value: ModelInstance<T, Shape, Identifier> | null | undefined, {
): Partial<SerializedModel<T, Shape>> | null | undefined {
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
if (!(value instanceof this.definedModel.definition.Class)) if (!(value instanceof this.definedModel.definition.Class))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, value, `value must be a compatible model (given ${value.constructor.name}, expected ${this.definedModel.definition.Class.name})`);
this,
value,
`value must be a compatible model (given ${value.constructor.name}, expected ${this.definedModel.definition.Class.name})`,
);
// Serializing the given model. // Serializing the given model.
return this.definedModel.model(value).serializeDiff(); return this.definedModel.model(value).serializeDiff();
} }
resetDiff( resetDiff(value: ModelInstance<T, Shape, Identifier>|null|undefined): void
value: ModelInstance<T, Shape, Identifier> | null | undefined, {
): void {
if (value === undefined) return; if (value === undefined) return;
if (value === null) return; if (value === null) return;
if (!(value instanceof this.definedModel.definition.Class)) if (!(value instanceof this.definedModel.definition.Class))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, value, `value must be a compatible model (given ${value.constructor.name}, expected ${this.definedModel.definition.Class.name})`);
this,
value,
`value must be a compatible model (given ${value.constructor.name}, expected ${this.definedModel.definition.Class.name})`,
);
// Reset diff of the given model. // Reset diff of the given model.
this.definedModel.model(value).resetDiff(); this.definedModel.model(value).resetDiff();
} }
hasChanged( hasChanged(originalValue: ModelInstance<T, Shape, Identifier>|null|undefined, currentValue: ModelInstance<T, Shape, Identifier>|null|undefined): boolean
originalValue: ModelInstance<T, Shape, Identifier> | null | undefined, {
currentValue: ModelInstance<T, Shape, Identifier> | null | undefined,
): boolean {
if (originalValue === undefined) return currentValue !== undefined; if (originalValue === undefined) return currentValue !== undefined;
if (originalValue === null) return currentValue !== null; if (originalValue === null) return currentValue !== null;
if (currentValue === undefined) return true; // Original value is not undefined. if (currentValue === undefined) return true; // Original value is not undefined.
if (currentValue === null) return true; // Original value is not null. if (currentValue === null) return true; // Original value is not null.
if (!(originalValue instanceof this.definedModel.definition.Class)) if (!(originalValue instanceof this.definedModel.definition.Class))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, originalValue, `value must be a compatible model (given ${originalValue.constructor.name}, expected ${this.definedModel.definition.Class.name})`);
this,
originalValue,
`value must be a compatible model (given ${originalValue.constructor.name}, expected ${this.definedModel.definition.Class.name})`,
);
if (!(currentValue instanceof this.definedModel.definition.Class)) if (!(currentValue instanceof this.definedModel.definition.Class))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, currentValue, `value must be a compatible model (given ${currentValue.constructor.name}, expected ${this.definedModel.definition.Class.name})`);
this,
currentValue,
`value must be a compatible model (given ${currentValue.constructor.name}, expected ${this.definedModel.definition.Class.name})`,
);
// If the current value is dirty, it has changed. // If the current value is dirty, it has changed.
return this.definedModel.model(currentValue).isDirty(); return this.definedModel.model(currentValue).isDirty();
} }
serializedHasChanged( serializedHasChanged(originalValue: SerializedModel<T, Shape> | null | undefined, currentValue: SerializedModel<T, Shape> | null | undefined): boolean
originalValue: SerializedModel<T, Shape> | null | undefined, {
currentValue: SerializedModel<T, Shape> | null | undefined,
): boolean {
if (originalValue === undefined) return currentValue !== undefined; if (originalValue === undefined) return currentValue !== undefined;
if (originalValue === null) return currentValue !== null; if (originalValue === null) return currentValue !== null;
if (currentValue === undefined) return true; // Original value is not undefined. if (currentValue === undefined) return true; // Original value is not undefined.
if (currentValue === null) return true; // Original value is not null. if (currentValue === null) return true; // Original value is not null.
if (typeof originalValue !== "object" || Array.isArray(originalValue)) if (typeof originalValue !== "object" || Array.isArray(originalValue))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, originalValue, "value must be an object");
this,
originalValue,
"value must be an object",
);
if (typeof currentValue !== "object" || Array.isArray(currentValue)) if (typeof currentValue !== "object" || Array.isArray(currentValue))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, currentValue, "value must be an object");
this,
currentValue,
"value must be an object",
);
// If any property has changed, the value has changed. // If any property has changed, the value has changed.
for (const property of this.definedModel.properties) for (const property of this.definedModel.properties)
if ( if (property.definition.type.serializedHasChanged(originalValue?.[property.name], currentValue?.[property.name]))
property.definition.type.serializedHasChanged(
originalValue?.[property.name],
currentValue?.[property.name],
)
)
return true; return true;
return false; // No change detected. return false; // No change detected.
} }
clone<Type extends ModelInstance<T, Shape, Identifier>>( clone<Type extends ModelInstance<T, Shape, Identifier>>(value: Type|null|undefined): Type
value: Type | null | undefined, {
): Type {
// Handle NULL / undefined values. // Handle NULL / undefined values.
if (!value) return super.clone(value); if (!value) return super.clone(value);
if (!(value instanceof this.definedModel.definition.Class)) if (!(value instanceof this.definedModel.definition.Class))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, value, `value must be a compatible model (given ${value.constructor.name}, expected ${this.definedModel.definition.Class.name})`);
this,
value,
`value must be a compatible model (given ${value.constructor.name}, expected ${this.definedModel.definition.Class.name})`,
);
return this.definedModel.model(value).clone() as Type; return this.definedModel.model(value).clone() as Type;
} }
applyPatch<Type extends ModelInstance<T, Shape, Identifier>>( applyPatch<Type extends ModelInstance<T, Shape, Identifier>>(currentValue: Type|null|undefined, patchValue: SerializedModel<T, Shape>|null|undefined, updateOriginals: boolean): Type|null|undefined
currentValue: Type | null | undefined, {
patchValue: SerializedModel<T, Shape> | null | undefined,
updateOriginals: boolean,
): Type | null | undefined {
if (patchValue === undefined) return undefined; if (patchValue === undefined) return undefined;
if (patchValue === null) return null; if (patchValue === null) return null;
if (typeof patchValue !== "object" || Array.isArray(patchValue)) if (typeof patchValue !== "object" || Array.isArray(patchValue))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, patchValue, "value must be an object");
this,
patchValue,
"value must be an object",
);
return this.definedModel return this.definedModel.model(currentValue).applyPatch(patchValue, updateOriginals) as Type;
.model(currentValue)
.applyPatch(patchValue, updateOriginals) as Type;
} }
} }
@ -204,18 +143,14 @@ export class ModelType<
* New model property definition. * New model property definition.
* @param definedModel Model manager. * @param definedModel Model manager.
*/ */
export function model< export function model<T extends object, Shape extends ModelShape<T>, Identifier extends IdentifierDefinition<T, Shape>>(
T extends object, definedModel: DeclaredModelManager<T, Shape, Identifier>
Shape extends ModelShape<T>, ): Definition<SerializedModel<T, Shape>, ModelInstance<T, Shape, Identifier>>
Identifier extends IdentifierDefinition<T, Shape>, {
>(
definedModel: DeclaredModelManager<T, Shape, Identifier>,
): Definition<SerializedModel<T, Shape>, ModelInstance<T, Shape, Identifier>> {
return define(new ModelType(definedModel)); return define(new ModelType(definedModel));
} }
export function circular<T extends object>( export function circular<T extends object>(definedModel: () => any): () => GenericModelManager<T>
definedModel: () => any, {
): () => GenericModelManager<T> {
return definedModel; return definedModel;
} }

View file

@ -5,23 +5,24 @@ import {InvalidTypeValueError} from "../../errors";
/** /**
* 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 | null | undefined): number | null | undefined { {
deserialize(value: number|null|undefined): number|null|undefined
{
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
if (typeof value !== "number") if (typeof value !== "number") throw new InvalidTypeValueError(this, value, "value must be a number");
throw new InvalidTypeValueError(this, value, "value must be a number");
return value; return value;
} }
serialize(value: number | null | undefined): number | null | undefined { serialize(value: number|null|undefined): number|null|undefined
{
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
if (typeof value !== "number") if (typeof value !== "number") throw new InvalidTypeValueError(this, value, "value must be a number");
throw new InvalidTypeValueError(this, value, "value must be a number");
return value; return value;
} }
@ -30,6 +31,7 @@ export class NumericType extends Type<number, number> {
/** /**
* New numeric property definition. * New numeric property definition.
*/ */
export function numeric(): Definition<number, number> { export function numeric(): Definition<number, number>
{
return define(new NumericType()); return define(new NumericType());
} }

View file

@ -1,21 +1,13 @@
import {Type} from "./type"; import {Type} from "./type";
import {define, Definition, UnknownDefinition} from "../property-definition"; import {define, Definition, UnknownDefinition} from "../property-definition";
import { import {ModelProperties, ModelPropertiesValues, ModelProperty, ModelShape, SerializedModel} from "../model";
ModelProperties,
ModelPropertiesValues,
ModelProperty,
ModelShape,
SerializedModel,
} from "../model";
import {InvalidTypeValueError} from "../../errors"; import {InvalidTypeValueError} from "../../errors";
/** /**
* Type of a custom object. * Type of a custom object.
*/ */
export class ObjectType< export class ObjectType<Shape extends ModelShape<T>, T extends object> extends Type<SerializedModel<T, Shape>, ModelPropertiesValues<T, Shape>>
Shape extends ModelShape<T>, {
T extends object,
> extends Type<SerializedModel<T, Shape>, ModelPropertiesValues<T, Shape>> {
/** /**
* Defined properties. * Defined properties.
*/ */
@ -25,7 +17,8 @@ export class ObjectType<
* Initialize a new object type of a Sharkitek model property. * Initialize a new object type of a Sharkitek model property.
* @param shape * @param shape
*/ */
constructor(readonly shape: Shape) { constructor(readonly shape: Shape)
{
super(); super();
this.initProperties(); this.initProperties();
} }
@ -34,11 +27,12 @@ export class ObjectType<
* Initialize properties iterator from the object shape. * Initialize properties iterator from the object shape.
* @protected * @protected
*/ */
protected initProperties(): void { protected initProperties(): void
{
// Build an array of model properties from the object shape. // Build an array of model properties from the object shape.
this.properties = []; this.properties = [];
for (const propertyName in this.shape) { for (const propertyName in this.shape)
// For each property, build a model property object. { // For each property, build a model property object.
this.properties.push({ this.properties.push({
name: propertyName, name: propertyName,
definition: this.shape[propertyName], definition: this.shape[propertyName],
@ -47,9 +41,8 @@ export class ObjectType<
} }
} }
deserialize( deserialize(value: SerializedModel<T, Shape>|null|undefined): ModelPropertiesValues<T, Shape>|null|undefined
value: SerializedModel<T, Shape> | null | undefined, {
): ModelPropertiesValues<T, Shape> | null | undefined {
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
@ -59,18 +52,16 @@ export class ObjectType<
// Initialize an empty object. // Initialize an empty object.
const obj: Partial<ModelPropertiesValues<T, Shape>> = {}; const obj: Partial<ModelPropertiesValues<T, Shape>> = {};
for (const property of this.properties) { for (const property of this.properties)
// For each defined property, deserialize its value according to its type. { // For each defined property, deserialize its value according to its type.
(obj[property.name as keyof T] as any) = (obj[property.name as keyof T] as any) = property.definition.type.deserialize(value?.[property.name]);
property.definition.type.deserialize(value?.[property.name]);
} }
return obj as ModelPropertiesValues<T, Shape>; // Returning serialized object. return obj as ModelPropertiesValues<T, Shape>; // Returning serialized object.
} }
serialize( serialize(value: ModelPropertiesValues<T, Shape>|null|undefined): SerializedModel<T, Shape>|null|undefined
value: ModelPropertiesValues<T, Shape> | null | undefined, {
): SerializedModel<T, Shape> | null | undefined {
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
@ -80,8 +71,8 @@ export class ObjectType<
// Creating an empty serialized object. // Creating an empty serialized object.
const serializedObject: Partial<SerializedModel<T, Shape>> = {}; const serializedObject: Partial<SerializedModel<T, Shape>> = {};
for (const property of this.properties) { for (const property of this.properties)
// For each property, adding it to the serialized object. { // For each property, adding it to the serialized object.
serializedObject[property.name] = property.definition.type.serialize( serializedObject[property.name] = property.definition.type.serialize(
// keyof Shape is a subset of keyof T. // keyof Shape is a subset of keyof T.
value?.[property.name as keyof T], value?.[property.name as keyof T],
@ -91,9 +82,8 @@ export class ObjectType<
return serializedObject as SerializedModel<T, Shape>; // Returning the serialized object. return serializedObject as SerializedModel<T, Shape>; // Returning the serialized object.
} }
serializeDiff( serializeDiff(value: ModelPropertiesValues<T, Shape>|null|undefined): Partial<SerializedModel<T, Shape>>|null|undefined
value: ModelPropertiesValues<T, Shape> | null | undefined, {
): Partial<SerializedModel<T, Shape>> | null | undefined {
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
@ -103,8 +93,8 @@ export class ObjectType<
// Creating an empty serialized object. // Creating an empty serialized object.
const serializedObject: Partial<SerializedModel<T, Shape>> = {}; const serializedObject: Partial<SerializedModel<T, Shape>> = {};
for (const property of this.properties) { for (const property of this.properties)
// For each property, adding it to the serialized object. { // For each property, adding it to the serialized object.
serializedObject[property.name] = property.definition.type.serializeDiff( serializedObject[property.name] = property.definition.type.serializeDiff(
// keyof Shape is a subset of keyof T. // keyof Shape is a subset of keyof T.
value?.[property.name as keyof T], value?.[property.name as keyof T],
@ -114,7 +104,8 @@ export class ObjectType<
return serializedObject as SerializedModel<T, Shape>; // Returning the serialized object. return serializedObject as SerializedModel<T, Shape>; // Returning the serialized object.
} }
resetDiff(value: ModelPropertiesValues<T, Shape> | null | undefined) { resetDiff(value: ModelPropertiesValues<T, Shape>|null|undefined)
{
if (value === undefined) return; if (value === undefined) return;
if (value === null) return; if (value === null) return;
@ -122,84 +113,53 @@ export class ObjectType<
throw new InvalidTypeValueError(this, value, "value must be an object"); throw new InvalidTypeValueError(this, value, "value must be an object");
// For each property, reset its diff. // For each property, reset its diff.
// keyof Shape is a subset of keyof T.
for (const property of this.properties) for (const property of this.properties)
// keyof Shape is a subset of keyof T.
property.definition.type.resetDiff(value?.[property.name as keyof T]); property.definition.type.resetDiff(value?.[property.name as keyof T]);
} }
hasChanged( hasChanged(originalValue: ModelPropertiesValues<T, Shape>|null|undefined, currentValue: ModelPropertiesValues<T, Shape>|null|undefined): boolean
originalValue: ModelPropertiesValues<T, Shape> | null | undefined, {
currentValue: ModelPropertiesValues<T, Shape> | null | undefined,
): boolean {
if (originalValue === undefined) return currentValue !== undefined; if (originalValue === undefined) return currentValue !== undefined;
if (originalValue === null) return currentValue !== null; if (originalValue === null) return currentValue !== null;
if (currentValue === undefined) return true; // Original value is not undefined. if (currentValue === undefined) return true; // Original value is not undefined.
if (currentValue === null) return true; // Original value is not null. if (currentValue === null) return true; // Original value is not null.
if (typeof originalValue !== "object" || Array.isArray(originalValue)) if (typeof originalValue !== "object" || Array.isArray(originalValue))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, originalValue, "value must be an object");
this,
originalValue,
"value must be an object",
);
if (typeof currentValue !== "object" || Array.isArray(currentValue)) if (typeof currentValue !== "object" || Array.isArray(currentValue))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, currentValue, "value must be an object");
this,
currentValue,
"value must be an object",
);
// If any property has changed, the value has changed. // If any property has changed, the value has changed.
for (const property of this.properties) for (const property of this.properties)
if ( if (property.definition.type.hasChanged(originalValue?.[property.name as keyof T], currentValue?.[property.name as keyof T]))
property.definition.type.hasChanged(
originalValue?.[property.name as keyof T],
currentValue?.[property.name as keyof T],
)
)
return true; return true;
return false; // No change detected. return false; // No change detected.
} }
serializedHasChanged( serializedHasChanged(originalValue: SerializedModel<T, Shape>|null|undefined, currentValue: SerializedModel<T, Shape>|null|undefined): boolean
originalValue: SerializedModel<T, Shape> | null | undefined, {
currentValue: SerializedModel<T, Shape> | null | undefined,
): boolean {
if (originalValue === undefined) return currentValue !== undefined; if (originalValue === undefined) return currentValue !== undefined;
if (originalValue === null) return currentValue !== null; if (originalValue === null) return currentValue !== null;
if (currentValue === undefined) return true; // Original value is not undefined. if (currentValue === undefined) return true; // Original value is not undefined.
if (currentValue === null) return true; // Original value is not null. if (currentValue === null) return true; // Original value is not null.
if (typeof originalValue !== "object" || Array.isArray(originalValue)) if (typeof originalValue !== "object" || Array.isArray(originalValue))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, originalValue, "value must be an object");
this,
originalValue,
"value must be an object",
);
if (typeof currentValue !== "object" || Array.isArray(currentValue)) if (typeof currentValue !== "object" || Array.isArray(currentValue))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, currentValue, "value must be an object");
this,
currentValue,
"value must be an object",
);
// If any property has changed, the value has changed. // If any property has changed, the value has changed.
for (const property of this.properties) for (const property of this.properties)
if ( if (property.definition.type.serializedHasChanged(originalValue?.[property.name], currentValue?.[property.name]))
property.definition.type.serializedHasChanged(
originalValue?.[property.name],
currentValue?.[property.name],
)
)
return true; return true;
return false; // No change detected. return false; // No change detected.
} }
clone<Type extends ModelPropertiesValues<T, Shape>>( clone<Type extends ModelPropertiesValues<T, Shape>>(value: Type|null|undefined): Type
value: Type | null | undefined, {
): Type {
// Handle NULL / undefined object. // Handle NULL / undefined object.
if (!value) return super.clone(value); if (!value) return super.clone(value);
@ -209,47 +169,29 @@ export class ObjectType<
// Initialize an empty object. // Initialize an empty object.
const cloned: Partial<ModelPropertiesValues<T, Shape>> = {}; const cloned: Partial<ModelPropertiesValues<T, Shape>> = {};
for (const property of this.properties) { for (const property of this.properties)
// For each defined property, clone it. { // For each defined property, clone it.
cloned[property.name as keyof T] = property.definition.type.clone( cloned[property.name as keyof T] = property.definition.type.clone(value?.[property.name]);
value?.[property.name],
);
} }
return cloned as Type; // Returning cloned object. return cloned as Type; // Returning cloned object.
} }
applyPatch<Type extends ModelPropertiesValues<T, Shape>>( applyPatch<Type extends ModelPropertiesValues<T, Shape>>(currentValue: Type|null|undefined, patchValue: SerializedModel<T, Shape>|null|undefined, updateOriginals: boolean): Type|null|undefined
currentValue: Type | null | undefined, {
patchValue: SerializedModel<T, Shape> | null | undefined,
updateOriginals: boolean,
): Type | null | undefined {
if (patchValue === undefined) return undefined; if (patchValue === undefined) return undefined;
if (patchValue === null) return null; if (patchValue === null) return null;
if (typeof patchValue !== "object" || Array.isArray(patchValue)) if (typeof patchValue !== "object" || Array.isArray(patchValue))
throw new InvalidTypeValueError( throw new InvalidTypeValueError(this, patchValue, "value must be an object");
this,
patchValue,
"value must be an object",
);
const patchedValue: Partial<Type> = const patchedValue: Partial<Type> = typeof currentValue === "object" && currentValue !== null ? currentValue : {};
typeof currentValue === "object" && currentValue !== null
? currentValue
: {};
for (const key in patchValue) { for (const key in patchValue)
// Apply the patch to each property of the patch value. { // Apply the patch to each property of the patch value.
const propertyDef = this.shape[key]; const propertyDef = this.shape[key];
if (propertyDef) if (propertyDef)
patchedValue[key as keyof Type] = ( patchedValue[key as keyof Type] = (propertyDef as UnknownDefinition).type.applyPatch(currentValue?.[key as keyof Type], patchValue[key], updateOriginals);
propertyDef as UnknownDefinition
).type.applyPatch(
currentValue?.[key as keyof Type],
patchValue[key],
updateOriginals,
);
} }
return patchedValue as Type; return patchedValue as Type;
@ -260,8 +202,7 @@ export class ObjectType<
* New object property definition. * New object property definition.
* @param shape Shape of the object. * @param shape Shape of the object.
*/ */
export function object<Shape extends ModelShape<T>, T extends object>( export function object<Shape extends ModelShape<T>, T extends object>(shape: Shape): Definition<SerializedModel<T, Shape>, ModelPropertiesValues<T, Shape>>
shape: Shape, {
): Definition<SerializedModel<T, Shape>, ModelPropertiesValues<T, Shape>> {
return define(new ObjectType(shape)); return define(new ObjectType(shape));
} }

View file

@ -4,15 +4,18 @@ import {define, Definition} from "../property-definition";
/** /**
* 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 | null | undefined): string | null | undefined { {
deserialize(value: string|null|undefined): string|null|undefined
{
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
return String(value); return String(value);
} }
serialize(value: string | null | undefined): string | null | undefined { serialize(value: string|null|undefined): string|null|undefined
{
if (value === undefined) return undefined; if (value === undefined) return undefined;
if (value === null) return null; if (value === null) return null;
@ -23,6 +26,7 @@ export class StringType extends Type<string, string> {
/** /**
* New string property definition. * New string property definition.
*/ */
export function string(): Definition<string, string> { export function string(): Definition<string, string>
{
return define(new StringType()); return define(new StringType());
} }

View file

@ -1,30 +1,26 @@
/** /**
* Abstract class of a Sharkitek model property type. * Abstract class of a Sharkitek model property type.
*/ */
export abstract class Type<SerializedType, ModelType> { 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( abstract serialize(value: ModelType|null|undefined): SerializedType|null|undefined;
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( abstract deserialize(value: SerializedType|null|undefined): ModelType|null|undefined;
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( serializeDiff(value: ModelType|null|undefined): Partial<SerializedType>|null|undefined
value: ModelType | null | undefined, {
): Partial<SerializedType> | null | undefined {
return this.serialize(value); // By default, nothing changes. return this.serialize(value); // By default, nothing changes.
} }
@ -32,7 +28,8 @@ export abstract class Type<SerializedType, ModelType> {
* 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: ModelType | null | undefined): void { resetDiff(value: ModelType|null|undefined): void
{
// By default, nothing to do. // By default, nothing to do.
} }
@ -41,10 +38,8 @@ export abstract class Type<SerializedType, ModelType> {
* @param originalValue Original value. * @param originalValue Original value.
* @param currentValue Current value. * @param currentValue Current value.
*/ */
hasChanged( hasChanged(originalValue: ModelType|null|undefined, currentValue: ModelType|null|undefined): boolean
originalValue: ModelType | null | undefined, {
currentValue: ModelType | null | undefined,
): boolean {
return originalValue !== currentValue; return originalValue !== currentValue;
} }
@ -53,10 +48,8 @@ export abstract class Type<SerializedType, ModelType> {
* @param originalValue Original serialized value. * @param originalValue Original serialized value.
* @param currentValue Current serialized value. * @param currentValue Current serialized value.
*/ */
serializedHasChanged( serializedHasChanged(originalValue: SerializedType|null|undefined, currentValue: SerializedType|null|undefined): boolean
originalValue: SerializedType | null | undefined, {
currentValue: SerializedType | null | undefined,
): boolean {
return originalValue !== currentValue; return originalValue !== currentValue;
} }
@ -64,7 +57,8 @@ export abstract class Type<SerializedType, ModelType> {
* Clone the provided value. * Clone the provided value.
* @param value The to clone. * @param value The to clone.
*/ */
clone<T extends ModelType>(value: T | null | undefined): T { clone<T extends ModelType>(value: T|null|undefined): T
{
return structuredClone(value); return structuredClone(value);
} }
@ -74,11 +68,8 @@ export abstract class Type<SerializedType, ModelType> {
* @param patchValue The serialized patch value. * @param patchValue The serialized patch value.
* @param updateOriginals Indicates if the original properties values must be updated or not. * @param updateOriginals Indicates if the original properties values must be updated or not.
*/ */
applyPatch<T extends ModelType>( applyPatch<T extends ModelType>(currentValue: T|null|undefined, patchValue: SerializedType|null|undefined, updateOriginals: boolean): T|null|undefined
currentValue: T | null | undefined, {
patchValue: SerializedType | null | undefined,
updateOriginals: boolean,
): T | null | undefined {
return this.deserialize(patchValue) as T; return this.deserialize(patchValue) as T;
} }
} }

View file

@ -1,10 +1,9 @@
/** /**
* Type definition of a class constructor. * Type definition of a class constructor.
*/ */
export type ConstructorOf<T extends object> = {new (): T}; export type ConstructorOf<T extends object> = { new(): T; };
/** /**
* Type definition of an original object overridden by another. * Type definition of an original object overridden by another.
*/ */
export type Modify<Original, Override> = Omit<Original, keyof Override> & export type Modify<Original, Override> = Omit<Original, keyof Override> & Override;
Override;

View file

@ -4,21 +4,12 @@ import {s} from "../src/library";
describe("errors", () => { describe("errors", () => {
it("tests type error", () => { it("tests type error", () => {
expect(new TypeError(s.property.string().type).message).toBe( expect((new TypeError(s.property.string().type)).message).toBe("Error in type StringType");
"Error in type StringType", expect((new TypeError(s.property.string().type, "test")).message).toBe("Error in type StringType: test");
);
expect(new TypeError(s.property.string().type, "test").message).toBe(
"Error in type StringType: test",
);
}); });
it("tests invalid type value error", () => { it("tests invalid type value error", () => {
expect( expect((new InvalidTypeValueError(s.property.decimal().type, ["value"])).message).toBe("Error in type DecimalType: [\"value\"] is an invalid value");
new InvalidTypeValueError(s.property.decimal().type, ["value"]).message, expect((new InvalidTypeValueError(s.property.decimal().type, ["value"], "test")).message).toBe("Error in type DecimalType: test");
).toBe('Error in type DecimalType: ["value"] is an invalid value');
expect(
new InvalidTypeValueError(s.property.decimal().type, ["value"], "test")
.message,
).toBe("Error in type DecimalType: test");
}); });
}); });

View file

@ -4,7 +4,8 @@ import {circular, defineModel, s} from "../src/library";
/** /**
* Test class of an account. * Test class of an account.
*/ */
class Account { class Account
{
static model = s.defineModel({ static model = s.defineModel({
Class: Account, Class: Account,
identifier: "id", identifier: "id",
@ -27,7 +28,8 @@ class Account {
/** /**
* Test class of an article. * Test class of an article.
*/ */
class Article { class Article
{
static model = s.defineModel({ static model = s.defineModel({
Class: Article, Class: Article,
identifier: "id", identifier: "id",
@ -37,7 +39,7 @@ class Article {
authors: s.property.array(s.property.model(() => Account.model)), authors: s.property.array(s.property.model(() => Account.model)),
text: s.property.string(), text: s.property.string(),
evaluation: s.property.decimal(), evaluation: s.property.decimal(),
tags: s.property.array(s.property.object({name: s.property.string()})), tags: s.property.array(s.property.object({ name: s.property.string() })),
comments: s.property.array(s.property.model(() => ArticleComment.model)), comments: s.property.array(s.property.model(() => ArticleComment.model)),
}, },
}); });
@ -47,14 +49,15 @@ class Article {
authors: Account[]; authors: Account[];
text: string; text: string;
evaluation: number; evaluation: number;
tags: {name: string}[]; tags: { name: string }[];
comments: ArticleComment[]; comments: ArticleComment[];
} }
/** /**
* Test class of a comment on an article. * Test class of a comment on an article.
*/ */
class ArticleComment { class ArticleComment
{
static model = s.defineModel({ static model = s.defineModel({
Class: ArticleComment, Class: ArticleComment,
identifier: "id", identifier: "id",
@ -75,7 +78,8 @@ class ArticleComment {
/** /**
* Get a test account instance. * Get a test account instance.
*/ */
function getTestAccount(): Account { function getTestAccount(): Account
{
const account = new Account(); const account = new Account();
account.id = 52; account.id = 52;
account.createdAt = new Date(); account.createdAt = new Date();
@ -85,13 +89,17 @@ function getTestAccount(): Account {
return account; return account;
} }
function getTestArticle(): Article { function getTestArticle(): Article
{
const article = new Article(); const article = new Article();
article.id = 1; article.id = 1;
article.title = "this is a test"; article.title = "this is a test";
article.text = "this is a long test."; article.text = "this is a long test.";
article.evaluation = 25.23; article.evaluation = 25.23;
article.tags = [{name: "test"}, {name: "foo"}]; article.tags = [
{ name: "test" },
{ name: "foo" },
];
article.authors = [getTestAccount()]; article.authors = [getTestAccount()];
article.comments = []; article.comments = [];
return article; return article;
@ -99,7 +107,8 @@ function getTestArticle(): Article {
describe("model", () => { describe("model", () => {
it("defines a new model, extending an existing one", () => { it("defines a new model, extending an existing one", () => {
class ExtendedAccount extends Account { class ExtendedAccount extends Account
{
static extendedModel = s.extend(Account.model, { static extendedModel = s.extend(Account.model, {
Class: ExtendedAccount, Class: ExtendedAccount,
properties: { properties: {
@ -138,7 +147,8 @@ describe("model", () => {
expect(Article.model.model(article).getIdentifier()).toBe(1); expect(Article.model.model(article).getIdentifier()).toBe(1);
}); });
it("gets a model composite identifier value", () => { it("gets a model composite identifier value", () => {
class CompositeModel { class CompositeModel
{
static model = s.defineModel({ static model = s.defineModel({
Class: CompositeModel, Class: CompositeModel,
properties: { properties: {
@ -147,7 +157,7 @@ describe("model", () => {
label: s.property.string(), label: s.property.string(),
}, },
identifier: ["firstId", "secondId"], identifier: ["firstId", "secondId"],
}); })
firstId: number; firstId: number;
secondId: number; secondId: number;
@ -155,15 +165,11 @@ describe("model", () => {
} }
expect( expect(
CompositeModel.model CompositeModel.model.model(Object.assign(new CompositeModel(), {
.model( firstId: 5,
Object.assign(new CompositeModel(), { secondId: 6,
firstId: 5, label: "test",
secondId: 6, })).getIdentifier()
label: "test",
}),
)
.getIdentifier(),
).toStrictEqual([5, 6]); ).toStrictEqual([5, 6]);
}); });
it("checks model dirtiness when altered, then reset diff", () => { it("checks model dirtiness when altered, then reset diff", () => {
@ -171,7 +177,7 @@ describe("model", () => {
expect(Article.model.model(article).isDirty()).toBeFalsy(); expect(Article.model.model(article).isDirty()).toBeFalsy();
article.title = "new title"; article.title = "new title";
expect(Article.model.model(article).isDirty()).toBeTruthy(); expect(Article.model.model(article).isDirty()).toBeTruthy();
Article.model.model(article).resetDiff(); Article.model.model(article).resetDiff()
expect(Article.model.model(article).isDirty()).toBeFalsy(); expect(Article.model.model(article).isDirty()).toBeFalsy();
}); });
@ -180,36 +186,14 @@ describe("model", () => {
id: 1, id: 1,
title: "this is a test", title: "this is a test",
authors: [ authors: [
Object.assign(new Account(), { Object.assign(new Account(), { id: 52, name: "John Doe", email: "test@test.test", createdAt: new Date("2022-08-07T08:47:01.000Z"), active: true, }),
id: 52, Object.assign(new Account(), { id: 4, name: "Tester", email: "another@test.test", createdAt: new Date("2022-09-07T18:32:55.000Z"), active: false, }),
name: "John Doe",
email: "test@test.test",
createdAt: new Date("2022-08-07T08:47:01.000Z"),
active: true,
}),
Object.assign(new Account(), {
id: 4,
name: "Tester",
email: "another@test.test",
createdAt: new Date("2022-09-07T18:32:55.000Z"),
active: false,
}),
], ],
text: "this is a long test.", text: "this is a long test.",
evaluation: 8.52, evaluation: 8.52,
tags: [{name: "test"}, {name: "foo"}], tags: [ {name: "test"}, {name: "foo"} ],
comments: [ comments: [
Object.assign(new ArticleComment(), { Object.assign(new ArticleComment(), { id: 542, author: Object.assign(new Account(), { id: 52, name: "John Doe", email: "test@test.test", createdAt: new Date("2022-08-07T08:47:01.000Z"), active: true, }), message: "comment content", }),
id: 542,
author: Object.assign(new Account(), {
id: 52,
name: "John Doe",
email: "test@test.test",
createdAt: new Date("2022-08-07T08:47:01.000Z"),
active: true,
}),
message: "comment content",
}),
], ],
}); });
@ -217,49 +201,23 @@ describe("model", () => {
id: 1, id: 1,
title: "this is a test", title: "this is a test",
authors: [ authors: [
{ { id: 52, name: "John Doe", email: "test@test.test", createdAt: "2022-08-07T08:47:01.000Z", active: true, },
id: 52, { id: 4, name: "Tester", email: "another@test.test", createdAt: "2022-09-07T18:32:55.000Z", active: false, },
name: "John Doe",
email: "test@test.test",
createdAt: "2022-08-07T08:47:01.000Z",
active: true,
},
{
id: 4,
name: "Tester",
email: "another@test.test",
createdAt: "2022-09-07T18:32:55.000Z",
active: false,
},
], ],
text: "this is a long test.", text: "this is a long test.",
evaluation: "8.52", evaluation: "8.52",
tags: [{name: "test"}, {name: "foo"}], tags: [ {name: "test"}, {name: "foo"} ],
comments: [ comments: [
{ { id: 542, author: { id: 52, name: "John Doe", email: "test@test.test", createdAt: "2022-08-07T08:47:01.000Z", active: true, }, message: "comment content", },
id: 542,
author: {
id: 52,
name: "John Doe",
email: "test@test.test",
createdAt: "2022-08-07T08:47:01.000Z",
active: true,
},
message: "comment content",
},
], ],
}); });
const deserializedArticleProperties = Article.model const deserializedArticleProperties = Article.model.model(deserializedArticle).getInstanceProperties();
.model(deserializedArticle)
.getInstanceProperties();
delete deserializedArticleProperties.authors[0]._sharkitek; delete deserializedArticleProperties.authors[0]._sharkitek;
delete deserializedArticleProperties.authors[1]._sharkitek; delete deserializedArticleProperties.authors[1]._sharkitek;
delete deserializedArticleProperties.comments[0]._sharkitek; delete deserializedArticleProperties.comments[0]._sharkitek;
delete (deserializedArticleProperties.comments[0].author as any)._sharkitek; delete (deserializedArticleProperties.comments[0].author as any)._sharkitek;
const expectedArticleProperties = Article.model const expectedArticleProperties = Article.model.model(expectedArticle).getInstanceProperties();
.model(expectedArticle)
.getInstanceProperties();
delete expectedArticleProperties.authors[0]._sharkitek; delete expectedArticleProperties.authors[0]._sharkitek;
delete expectedArticleProperties.authors[1]._sharkitek; delete expectedArticleProperties.authors[1]._sharkitek;
delete expectedArticleProperties.comments[0]._sharkitek; delete expectedArticleProperties.comments[0]._sharkitek;
@ -274,15 +232,9 @@ describe("model", () => {
title: "this is a test", title: "this is a test",
text: "this is a long test.", text: "this is a long test.",
evaluation: "25.23", evaluation: "25.23",
tags: [{name: "test"}, {name: "foo"}], tags: [{ name: "test" }, { name: "foo" }],
authors: [ authors: [
{ { id: 52, createdAt: article.authors[0].createdAt.toISOString(), name: "John Doe", email: "john@doe.test", active: true }
id: 52,
createdAt: article.authors[0].createdAt.toISOString(),
name: "John Doe",
email: "john@doe.test",
active: true,
},
], ],
comments: [], comments: [],
}); });
@ -293,36 +245,14 @@ describe("model", () => {
id: 1, id: 1,
title: "this is a test", title: "this is a test",
authors: [ authors: [
{ { id: 52, name: "John Doe", email: "test@test.test", createdAt: "2022-08-07T08:47:01.000Z", active: true, },
id: 52, { id: 4, name: "Tester", email: "another@test.test", createdAt: "2022-09-07T18:32:55.000Z", active: false, },
name: "John Doe",
email: "test@test.test",
createdAt: "2022-08-07T08:47:01.000Z",
active: true,
},
{
id: 4,
name: "Tester",
email: "another@test.test",
createdAt: "2022-09-07T18:32:55.000Z",
active: false,
},
], ],
text: "this is a long test.", text: "this is a long test.",
evaluation: "8.52", evaluation: "8.52",
tags: [{name: "test"}, {name: "foo"}], tags: [ {name: "test"}, {name: "foo"} ],
comments: [ comments: [
{ { id: 542, author: { id: 52, name: "John Doe", email: "test@test.test", createdAt: "2022-08-07T08:47:01.000Z", active: true, }, message: "comment content", },
id: 542,
author: {
id: 52,
name: "John Doe",
email: "test@test.test",
createdAt: "2022-08-07T08:47:01.000Z",
active: true,
},
message: "comment content",
},
], ],
}); });
@ -346,36 +276,14 @@ describe("model", () => {
id: 1, id: 1,
title: "this is a test", title: "this is a test",
authors: [ authors: [
{ { id: 52, name: "John Doe", email: "test@test.test", createdAt: "2022-08-07T08:47:01.000Z", active: true, },
id: 52, { id: 4, name: "Tester", email: "another@test.test", createdAt: "2022-09-07T18:32:55.000Z", active: false, },
name: "John Doe",
email: "test@test.test",
createdAt: "2022-08-07T08:47:01.000Z",
active: true,
},
{
id: 4,
name: "Tester",
email: "another@test.test",
createdAt: "2022-09-07T18:32:55.000Z",
active: false,
},
], ],
text: "this is a long test.", text: "this is a long test.",
evaluation: "8.52", evaluation: "8.52",
tags: [{name: "test"}, {name: "foo"}], tags: [ {name: "test"}, {name: "foo"} ],
comments: [ comments: [
{ { id: 542, author: { id: 52, name: "John Doe", email: "test@test.test", createdAt: "2022-08-07T08:47:01.000Z", active: true, }, message: "comment content", },
id: 542,
author: {
id: 52,
name: "John Doe",
email: "test@test.test",
createdAt: "2022-08-07T08:47:01.000Z",
active: true,
},
message: "comment content",
},
], ],
}); });
@ -383,7 +291,10 @@ describe("model", () => {
expect(Article.model.model(deserializedArticle).patch()).toStrictEqual({ expect(Article.model.model(deserializedArticle).patch()).toStrictEqual({
id: 1, id: 1,
authors: [{id: 52}, {id: 4, active: true}], authors: [
{ id: 52, },
{ id: 4, active: true },
],
}); });
deserializedArticle.comments[0].author.name = "Johnny"; deserializedArticle.comments[0].author.name = "Johnny";
@ -403,7 +314,8 @@ describe("model", () => {
}); });
it("deserializes and patches with fields that are not properties", () => { it("deserializes and patches with fields that are not properties", () => {
class TestModel { class TestModel
{
static model = defineModel({ static model = defineModel({
Class: TestModel, Class: TestModel,
properties: { properties: {
@ -411,12 +323,12 @@ describe("model", () => {
label: s.property.string(), label: s.property.string(),
}, },
identifier: "id", identifier: "id",
}); })
id: number; id: number;
label: string; label: string;
notAProperty: {hello: string} = {hello: "world"}; notAProperty: { hello: string } = { hello: "world" };
} }
const deserializedModel = TestModel.model.parse({ const deserializedModel = TestModel.model.parse({
@ -427,29 +339,17 @@ describe("model", () => {
expect(deserializedModel.label).toBe("testing"); expect(deserializedModel.label).toBe("testing");
expect(deserializedModel.notAProperty?.hello).toBe("world"); expect(deserializedModel.notAProperty?.hello).toBe("world");
const clonedDeserializedModel = TestModel.model const clonedDeserializedModel = TestModel.model.model(deserializedModel).clone();
.model(deserializedModel)
.clone();
deserializedModel.label = "new!"; deserializedModel.label = "new!";
expect(TestModel.model.model(deserializedModel).patch()).toStrictEqual({ expect(TestModel.model.model(deserializedModel).patch()).toStrictEqual({ id: 5, label: "new!" });
id: 5,
label: "new!",
});
deserializedModel.notAProperty.hello = "monster"; deserializedModel.notAProperty.hello = "monster";
expect(TestModel.model.model(deserializedModel).patch()).toStrictEqual({ expect(TestModel.model.model(deserializedModel).patch()).toStrictEqual({ id: 5 });
id: 5,
});
expect(TestModel.model.model(deserializedModel).serialize()).toStrictEqual({ expect(TestModel.model.model(deserializedModel).serialize()).toStrictEqual({ id: 5, label: "new!" });
id: 5,
label: "new!",
});
expect( expect(TestModel.model.model(clonedDeserializedModel).serialize()).toStrictEqual({ id: 5, label: "testing" });
TestModel.model.model(clonedDeserializedModel).serialize(),
).toStrictEqual({id: 5, label: "testing"});
expect(clonedDeserializedModel.notAProperty.hello).toEqual("world"); expect(clonedDeserializedModel.notAProperty.hello).toEqual("world");
}); });
@ -458,36 +358,14 @@ describe("model", () => {
id: 1, id: 1,
title: "this is a test", title: "this is a test",
authors: [ authors: [
{ { id: 52, name: "John Doe", email: "test@test.test", createdAt: "2022-08-07T08:47:01.000Z", active: true, },
id: 52, { id: 4, name: "Tester", email: "another@test.test", createdAt: "2022-09-07T18:32:55.000Z", active: false, },
name: "John Doe",
email: "test@test.test",
createdAt: "2022-08-07T08:47:01.000Z",
active: true,
},
{
id: 4,
name: "Tester",
email: "another@test.test",
createdAt: "2022-09-07T18:32:55.000Z",
active: false,
},
], ],
text: "this is a long test.", text: "this is a long test.",
evaluation: "8.52", evaluation: "8.52",
tags: [{name: "test"}, {name: "foo"}], tags: [ {name: "test"}, {name: "foo"} ],
comments: [ comments: [
{ { id: 542, author: { id: 52, name: "John Doe", email: "test@test.test", createdAt: "2022-08-07T08:47:01.000Z", active: true, }, message: "comment content", },
id: 542,
author: {
id: 52,
name: "John Doe",
email: "test@test.test",
createdAt: "2022-08-07T08:47:01.000Z",
active: true,
},
message: "comment content",
},
], ],
}); });
@ -509,16 +387,11 @@ describe("model", () => {
const testArticle = Article.model.from({ const testArticle = Article.model.from({
title: "this is a test", title: "this is a test",
authors: [ authors: [
Account.model.from({ Account.model.from({ name: "John Doe", email: "test@test.test", createdAt: new Date(), active: true }),
name: "John Doe",
email: "test@test.test",
createdAt: new Date(),
active: true,
}),
], ],
text: "this is a long text", text: "this is a long text",
evaluation: 8.52, evaluation: 8.52,
tags: [{name: "test"}, {name: "foo"}], tags: [{ name: "test" }, { name: "foo" }],
unknownField: true, unknownField: true,
anotherOne: "test", anotherOne: "test",
@ -538,17 +411,11 @@ describe("model", () => {
id: 1, id: 1,
title: "this is a test", title: "this is a test",
authors: [ authors: [
Account.model.from({ Account.model.from({ id: 55, name: "John Doe", email: "test@test.test", createdAt: new Date(), active: true }),
id: 55,
name: "John Doe",
email: "test@test.test",
createdAt: new Date(),
active: true,
}),
], ],
text: "this is a long text", text: "this is a long text",
evaluation: 8.52, evaluation: 8.52,
tags: [{name: "test"}, {name: "foo"}], tags: [{ name: "test" }, { name: "foo" }],
unknownField: true, unknownField: true,
anotherOne: "test", anotherOne: "test",
@ -560,30 +427,23 @@ describe("model", () => {
title: "new title", title: "new title",
}); });
expect(testArticle.title).toBe("new title"); expect(testArticle.title).toBe("new title");
expect(Article.model.model(testArticle).serializeDiff()).toStrictEqual({ expect(Article.model.model(testArticle).serializeDiff()).toStrictEqual({ id: 1 });
id: 1,
});
// Test originals update propagation. // Test originals update propagation.
Article.model.model(testArticle).applyPatch({ Article.model.model(testArticle).applyPatch({
authors: [{email: "john@test.test"}], authors: [ { email: "john@test.test" } ]
}); });
expect(testArticle.authors[0].email).toBe("john@test.test"); expect(testArticle.authors[0].email).toBe("john@test.test");
expect(Article.model.model(testArticle).serializeDiff()).toStrictEqual({ expect(Article.model.model(testArticle).serializeDiff()).toStrictEqual({ id: 1 });
id: 1,
});
// Test without originals update. // Test without originals update.
Article.model.model(testArticle).applyPatch( Article.model.model(testArticle).applyPatch({
{ authors: [ { name: "Johnny" } ]
authors: [{name: "Johnny"}], }, false);
},
false,
);
expect(testArticle.authors[0].name).toBe("Johnny"); expect(testArticle.authors[0].name).toBe("Johnny");
expect(Article.model.model(testArticle).serializeDiff()).toStrictEqual({ expect(Article.model.model(testArticle).serializeDiff()).toStrictEqual({
id: 1, id: 1,
authors: [{id: 55, name: "Johnny"}], authors: [ { id: 55, name: "Johnny" } ]
}); });
}); });
}); });

View file

@ -1,7 +1,8 @@
import {describe, expect, test} from "vitest"; import {describe, expect, test} from "vitest";
import {ArrayType, InvalidTypeValueError, s} from "../../../src/library"; import {ArrayType, InvalidTypeValueError, s} from "../../../src/library";
class TestModel { class TestModel
{
id: number; id: number;
name: string; name: string;
price: number; price: number;
@ -26,35 +27,19 @@ describe("array type", () => {
const testProperty = s.property.array(s.property.decimal()); const testProperty = s.property.array(s.property.decimal());
test("array type functions", () => { test("array type functions", () => {
expect(testProperty.type.serialize([12.547, 8, -52.11])).toEqual([ expect(testProperty.type.serialize([12.547, 8, -52.11])).toEqual(["12.547", "8", "-52.11"]);
"12.547", expect(testProperty.type.deserialize(["12.547", "8", "-52.11"])).toEqual([12.547, 8, -52.11]);
"8",
"-52.11",
]);
expect(testProperty.type.deserialize(["12.547", "8", "-52.11"])).toEqual([
12.547, 8, -52.11,
]);
{ { // Try to serialize the difference of an array with one changed model.
// Try to serialize the difference of an array with one changed model.
const propertyValue = [ const propertyValue = [
testModel.model( testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 22 })).instance,
Object.assign(new TestModel(), {id: 1, name: "test", price: 22}), testModel.model(Object.assign(new TestModel(), { id: 2, name: "another", price: 12.55 })).instance,
).instance,
testModel.model(
Object.assign(new TestModel(), {
id: 2,
name: "another",
price: 12.55,
}),
).instance,
]; ];
propertyValue[0].name = "new"; propertyValue[0].name = "new";
expect( expect(s.property.array(s.property.model(testModel)).type.serializeDiff(propertyValue)).toEqual([
s.property { id: 1, name: "new" },
.array(s.property.model(testModel)) { id: 2 },
.type.serializeDiff(propertyValue), ]);
).toEqual([{id: 1, name: "new"}, {id: 2}]);
} }
expect(testProperty.type.serialize(null)).toBe(null); expect(testProperty.type.serialize(null)).toBe(null);
@ -65,326 +50,157 @@ describe("array type", () => {
expect(testProperty.type.deserialize(undefined)).toBe(undefined); expect(testProperty.type.deserialize(undefined)).toBe(undefined);
expect(testProperty.type.serializeDiff(undefined)).toBe(undefined); expect(testProperty.type.serializeDiff(undefined)).toBe(undefined);
expect( expect(testProperty.type.hasChanged([12.547, 8, -52.11], [12.547, 8, -52.11])).toBeFalsy();
testProperty.type.hasChanged([12.547, 8, -52.11], [12.547, 8, -52.11]),
).toBeFalsy();
expect(testProperty.type.hasChanged(null, null)).toBeFalsy(); expect(testProperty.type.hasChanged(null, null)).toBeFalsy();
expect(testProperty.type.hasChanged(undefined, undefined)).toBeFalsy(); expect(testProperty.type.hasChanged(undefined, undefined)).toBeFalsy();
expect(testProperty.type.hasChanged(null, undefined)).toBeTruthy(); expect(testProperty.type.hasChanged(null, undefined)).toBeTruthy();
expect(testProperty.type.hasChanged(undefined, null)).toBeTruthy(); expect(testProperty.type.hasChanged(undefined, null)).toBeTruthy();
expect( expect(testProperty.type.hasChanged(null, [12.547, 8, -52.11])).toBeTruthy();
testProperty.type.hasChanged(null, [12.547, 8, -52.11]), expect(testProperty.type.hasChanged(undefined, [12.547, 8, -52.11])).toBeTruthy();
).toBeTruthy(); expect(testProperty.type.hasChanged([12.547, 8, -52.11], null)).toBeTruthy();
expect( expect(testProperty.type.hasChanged([12.547, 8, -52.11], undefined)).toBeTruthy();
testProperty.type.hasChanged(undefined, [12.547, 8, -52.11]), expect(testProperty.type.hasChanged([12.547, 8, -52.11], [12.547, -52.11, 8])).toBeTruthy();
).toBeTruthy(); expect(testProperty.type.hasChanged([12.547, -52.11, 8], [12.547, 8, -52.11])).toBeTruthy();
expect( expect(testProperty.type.hasChanged([12.547, 8, -52.11], [12.547, 8])).toBeTruthy();
testProperty.type.hasChanged([12.547, 8, -52.11], null), expect(testProperty.type.hasChanged([12.547, 8], [12.547, 8, -52.11])).toBeTruthy();
).toBeTruthy();
expect(
testProperty.type.hasChanged([12.547, 8, -52.11], undefined),
).toBeTruthy();
expect(
testProperty.type.hasChanged([12.547, 8, -52.11], [12.547, -52.11, 8]),
).toBeTruthy();
expect(
testProperty.type.hasChanged([12.547, -52.11, 8], [12.547, 8, -52.11]),
).toBeTruthy();
expect(
testProperty.type.hasChanged([12.547, 8, -52.11], [12.547, 8]),
).toBeTruthy();
expect(
testProperty.type.hasChanged([12.547, 8], [12.547, 8, -52.11]),
).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged(["12.547", "8", "-52.11"], ["12.547", "8", "-52.11"])).toBeFalsy();
testProperty.type.serializedHasChanged(
["12.547", "8", "-52.11"],
["12.547", "8", "-52.11"],
),
).toBeFalsy();
expect(testProperty.type.serializedHasChanged(null, null)).toBeFalsy(); expect(testProperty.type.serializedHasChanged(null, null)).toBeFalsy();
expect( expect(testProperty.type.serializedHasChanged(undefined, undefined)).toBeFalsy();
testProperty.type.serializedHasChanged(undefined, undefined), expect(testProperty.type.serializedHasChanged(null, undefined)).toBeTruthy();
).toBeFalsy(); expect(testProperty.type.serializedHasChanged(undefined, null)).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged(null, ["12.547", "8", "-52.11"])).toBeTruthy();
testProperty.type.serializedHasChanged(null, undefined), expect(testProperty.type.serializedHasChanged(undefined, ["12.547", "8", "-52.11"])).toBeTruthy();
).toBeTruthy(); expect(testProperty.type.serializedHasChanged(["12.547", "8", "-52.11"], null)).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged(["12.547", "8", "-52.11"], undefined)).toBeTruthy();
testProperty.type.serializedHasChanged(undefined, null), expect(testProperty.type.serializedHasChanged(["12.547", "8", "-52.11"], ["12.547", "-52.11", "8"])).toBeTruthy();
).toBeTruthy(); expect(testProperty.type.serializedHasChanged(["12.547", "-52.11", "8"], ["12.547", "8", "-52.11"])).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged(["12.547", "8", "-52.11"], ["12.547", "8"])).toBeTruthy();
testProperty.type.serializedHasChanged(null, ["12.547", "8", "-52.11"]), expect(testProperty.type.serializedHasChanged(["12.547", "8"], ["12.547", "8", "-52.11"])).toBeTruthy();
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(undefined, [
"12.547",
"8",
"-52.11",
]),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(["12.547", "8", "-52.11"], null),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
["12.547", "8", "-52.11"],
undefined,
),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
["12.547", "8", "-52.11"],
["12.547", "-52.11", "8"],
),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
["12.547", "-52.11", "8"],
["12.547", "8", "-52.11"],
),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
["12.547", "8", "-52.11"],
["12.547", "8"],
),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
["12.547", "8"],
["12.547", "8", "-52.11"],
),
).toBeTruthy();
{ { // Try to reset the difference of an array with one changed model.
// Try to reset the difference of an array with one changed model.
const propertyValue = [ const propertyValue = [
testModel.model( testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 22 })).instance,
Object.assign(new TestModel(), {id: 1, name: "test", price: 22}), testModel.model(Object.assign(new TestModel(), { id: 2, name: "another", price: 12.55 })).instance,
).instance,
testModel.model(
Object.assign(new TestModel(), {
id: 2,
name: "another",
price: 12.55,
}),
).instance,
]; ];
propertyValue[0].name = "new"; propertyValue[0].name = "new";
expect( expect(s.property.array(s.property.model(testModel)).type.serializeDiff(propertyValue)).toEqual([
s.property { id: 1, name: "new" },
.array(s.property.model(testModel)) { id: 2 },
.type.serializeDiff(propertyValue), ]);
).toEqual([{id: 1, name: "new"}, {id: 2}]); s.property.array(s.property.model(testModel)).type.resetDiff(propertyValue)
s.property expect(s.property.array(s.property.model(testModel)).type.serializeDiff(propertyValue)).toEqual([
.array(s.property.model(testModel)) { id: 1 },
.type.resetDiff(propertyValue); { id: 2 },
expect( ]);
s.property
.array(s.property.model(testModel))
.type.serializeDiff(propertyValue),
).toEqual([{id: 1}, {id: 2}]);
} }
testProperty.type.resetDiff(undefined); testProperty.type.resetDiff(undefined);
testProperty.type.resetDiff(null); testProperty.type.resetDiff(null);
{ { // Test that values are cloned in a different array.
// Test that values are cloned in a different array.
const propertyValue = [12.547, 8, -52.11]; const propertyValue = [12.547, 8, -52.11];
const clonedPropertyValue = testProperty.type.clone(propertyValue); const clonedPropertyValue = testProperty.type.clone(propertyValue);
expect(clonedPropertyValue).not.toBe(propertyValue); expect(clonedPropertyValue).not.toBe(propertyValue);
expect(clonedPropertyValue).toEqual(propertyValue); expect(clonedPropertyValue).toEqual(propertyValue);
} }
{ { // Test that values are cloned recursively.
// Test that values are cloned recursively.
const propertyValue = [ const propertyValue = [
testModel.model( testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 22 })).instance,
Object.assign(new TestModel(), {id: 1, name: "test", price: 22}), testModel.model(Object.assign(new TestModel(), { id: 2, name: "another", price: 12.55 })).instance,
).instance,
testModel.model(
Object.assign(new TestModel(), {
id: 2,
name: "another",
price: 12.55,
}),
).instance,
]; ];
// The arrays are different. // The arrays are different.
const clonedPropertyValue = s.property const clonedPropertyValue = s.property.array(s.property.model(testModel)).type.clone(propertyValue);
.array(s.property.model(testModel))
.type.clone(propertyValue);
expect(clonedPropertyValue).not.toBe(propertyValue); expect(clonedPropertyValue).not.toBe(propertyValue);
// Array values must be different objects but have the same values. // Array values must be different objects but have the same values.
expect(clonedPropertyValue[0]).not.toBe(propertyValue[0]); expect(clonedPropertyValue[0]).not.toBe(propertyValue[0]);
expect(clonedPropertyValue[1]).not.toBe(propertyValue[1]); expect(clonedPropertyValue[1]).not.toBe(propertyValue[1]);
expect( expect(testModel.model(clonedPropertyValue[0]).getInstanceProperties()).toEqual(testModel.model(propertyValue[0]).getInstanceProperties());
testModel.model(clonedPropertyValue[0]).getInstanceProperties(), expect(testModel.model(clonedPropertyValue[1]).getInstanceProperties()).toEqual(testModel.model(propertyValue[1]).getInstanceProperties());
).toEqual(testModel.model(propertyValue[0]).getInstanceProperties());
expect(
testModel.model(clonedPropertyValue[1]).getInstanceProperties(),
).toEqual(testModel.model(propertyValue[1]).getInstanceProperties());
} }
expect(testProperty.type.clone(undefined)).toBe(undefined); expect(testProperty.type.clone(undefined)).toBe(undefined);
expect(testProperty.type.clone(null)).toBe(null); expect(testProperty.type.clone(null)).toBe(null);
{ { // Test simple patch.
// Test simple patch.
expect( expect(
testProperty.type.applyPatch( testProperty.type.applyPatch([12.547, 8, -52.11], ["12.547", "444.34", "-52.11"], true)
[12.547, 8, -52.11],
["12.547", "444.34", "-52.11"],
true,
),
).toEqual([12.547, 444.34, -52.11]); ).toEqual([12.547, 444.34, -52.11]);
expect( expect(
testProperty.type.applyPatch( testProperty.type.applyPatch(undefined, ["12.547", "444.34", "-52.11"], false)
undefined,
["12.547", "444.34", "-52.11"],
false,
),
).toEqual([12.547, 444.34, -52.11]); ).toEqual([12.547, 444.34, -52.11]);
expect( expect(
testProperty.type.applyPatch( testProperty.type.applyPatch(null, ["12.547", "444.34", "-52.11"], false)
null,
["12.547", "444.34", "-52.11"],
false,
),
).toEqual([12.547, 444.34, -52.11]); ).toEqual([12.547, 444.34, -52.11]);
expect( expect(
testProperty.type.applyPatch([12.547, 8, -52.11], undefined, false), testProperty.type.applyPatch([12.547, 8, -52.11], undefined, false)
).toBeUndefined(); ).toBeUndefined();
expect( expect(
testProperty.type.applyPatch([12.547, 8, -52.11], null, false), testProperty.type.applyPatch([12.547, 8, -52.11], null, false)
).toBeNull(); ).toBeNull();
} }
{ { // Invalid patch.
// Invalid patch. expect(
expect(() => () => testProperty.type.applyPatch([12.547, 8, -52.11], {} as any, false)
testProperty.type.applyPatch([12.547, 8, -52.11], {} as any, false),
).toThrow(InvalidTypeValueError); ).toThrow(InvalidTypeValueError);
} }
{ { // Test recursive patch.
// Test recursive patch.
const propertyValue = [ const propertyValue = [
testModel.model( testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 22 })).instance,
Object.assign(new TestModel(), {id: 1, name: "test", price: 22}), testModel.model(Object.assign(new TestModel(), { id: 2, name: "another", price: 12.55 })).instance,
).instance,
testModel.model(
Object.assign(new TestModel(), {
id: 2,
name: "another",
price: 12.55,
}),
).instance,
]; ];
const patched = s.property const patched = s.property.array(s.property.model(testModel)).type.applyPatch(propertyValue, [{
.array(s.property.model(testModel)) id: 1,
.type.applyPatch( name: "new",
propertyValue, }, {
[ id: 2,
{ price: "13.65",
id: 1, }], true);
name: "new",
},
{
id: 2,
price: "13.65",
},
],
true,
);
// Check applied patch. // Check applied patch.
expect(patched).toEqual([ expect(patched).toEqual([
testModel.parse({id: 1, name: "new", price: "22"}), testModel.parse({ id: 1, name: "new", price: "22" }),
testModel.parse({id: 2, name: "another", price: "13.65"}), testModel.parse({ id: 2, name: "another", price: "13.65" }),
]); ]);
// Check that originals have been updated. // Check that originals have been updated.
expect(testModel.model(patched[0]).serializeDiff()).toEqual({id: 1}); expect(testModel.model(patched[0]).serializeDiff()).toEqual({ id: 1 });
patched[0].name = "test"; patched[0].name = "test";
expect(testModel.model(patched[0]).serializeDiff()).toEqual({ expect(testModel.model(patched[0]).serializeDiff()).toEqual({ id: 1, name: "test" });
id: 1, expect(testModel.model(patched[1]).serializeDiff()).toEqual({ id: 2 });
name: "test",
});
expect(testModel.model(patched[1]).serializeDiff()).toEqual({id: 2});
patched[1].price = 12.55; patched[1].price = 12.55;
expect(testModel.model(patched[1]).serializeDiff()).toEqual({ expect(testModel.model(patched[1]).serializeDiff()).toEqual({ id: 2, price: "12.55" });
id: 2,
price: "12.55",
});
} }
{ { // Test recursive patch without originals update.-
// Test recursive patch without originals update.-
const propertyValue = [ const propertyValue = [
testModel.model( testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 22 })).instance,
Object.assign(new TestModel(), {id: 1, name: "test", price: 22}), testModel.model(Object.assign(new TestModel(), { id: 2, name: "another", price: 12.55 })).instance,
).instance,
testModel.model(
Object.assign(new TestModel(), {
id: 2,
name: "another",
price: 12.55,
}),
).instance,
]; ];
const patched = s.property const patched = s.property.array(s.property.model(testModel)).type.applyPatch(propertyValue, [{
.array(s.property.model(testModel))
.type.applyPatch(
propertyValue,
[
{
id: 1,
name: "new",
},
{
id: 2,
price: "13.65",
},
],
false,
);
// Check that originals haven't been updated.
expect(testModel.model(patched[0]).serializeDiff()).toEqual({
id: 1, id: 1,
name: "new", name: "new",
}); }, {
expect(testModel.model(patched[1]).serializeDiff()).toEqual({
id: 2, id: 2,
price: "13.65", price: "13.65",
}); }], false);
// Check that originals haven't been updated.
expect(testModel.model(patched[0]).serializeDiff()).toEqual({ id: 1, name: "new" });
expect(testModel.model(patched[1]).serializeDiff()).toEqual({ id: 2, price: "13.65" });
} }
}); });
test("invalid parameters types", () => { test("invalid parameters types", () => {
expect(() => testProperty.type.serialize({} as any)).toThrowError( expect(() => testProperty.type.serialize({} as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(() => testProperty.type.deserialize({} as any)).toThrowError(InvalidTypeValueError);
); expect(() => testProperty.type.serializeDiff({} as any)).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.deserialize({} as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => testProperty.type.serializeDiff({} as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => testProperty.type.resetDiff({} as any)).not.toThrow(); expect(() => testProperty.type.resetDiff({} as any)).not.toThrow();
expect(testProperty.type.hasChanged({} as any, {} as any)).toBeTruthy(); expect(testProperty.type.hasChanged({} as any, {} as any)).toBeTruthy();
expect( expect(testProperty.type.hasChanged(false as any, false as any)).toBeFalsy();
testProperty.type.hasChanged(false as any, false as any), expect(testProperty.type.serializedHasChanged({} as any, {} as any)).toBeTruthy();
).toBeFalsy(); expect(testProperty.type.serializedHasChanged(false as any, false as any)).toBeFalsy();
expect( expect(() => testProperty.type.clone({} as any)).toThrowError(InvalidTypeValueError);
testProperty.type.serializedHasChanged({} as any, {} as any),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(false as any, false as any),
).toBeFalsy();
expect(() => testProperty.type.clone({} as any)).toThrowError(
InvalidTypeValueError,
);
}); });
}); });

View file

@ -28,9 +28,7 @@ describe("boolean type", () => {
expect(s.property.boolean().type.hasChanged(true, true)).toBeFalsy(); expect(s.property.boolean().type.hasChanged(true, true)).toBeFalsy();
expect(s.property.boolean().type.hasChanged(null, null)).toBeFalsy(); expect(s.property.boolean().type.hasChanged(null, null)).toBeFalsy();
expect( expect(s.property.boolean().type.hasChanged(undefined, undefined)).toBeFalsy();
s.property.boolean().type.hasChanged(undefined, undefined),
).toBeFalsy();
expect(s.property.boolean().type.hasChanged(null, undefined)).toBeTruthy(); expect(s.property.boolean().type.hasChanged(null, undefined)).toBeTruthy();
expect(s.property.boolean().type.hasChanged(undefined, null)).toBeTruthy(); expect(s.property.boolean().type.hasChanged(undefined, null)).toBeTruthy();
expect(s.property.boolean().type.hasChanged(null, false)).toBeTruthy(); expect(s.property.boolean().type.hasChanged(null, false)).toBeTruthy();
@ -38,58 +36,28 @@ describe("boolean type", () => {
expect(s.property.boolean().type.hasChanged(false, null)).toBeTruthy(); expect(s.property.boolean().type.hasChanged(false, null)).toBeTruthy();
expect(s.property.boolean().type.hasChanged(false, undefined)).toBeTruthy(); expect(s.property.boolean().type.hasChanged(false, undefined)).toBeTruthy();
expect( expect(s.property.boolean().type.serializedHasChanged(false, false)).toBeFalsy();
s.property.boolean().type.serializedHasChanged(false, false), expect(s.property.boolean().type.serializedHasChanged(null, null)).toBeFalsy();
).toBeFalsy(); expect(s.property.boolean().type.serializedHasChanged(undefined, undefined)).toBeFalsy();
expect( expect(s.property.boolean().type.serializedHasChanged(null, undefined)).toBeTruthy();
s.property.boolean().type.serializedHasChanged(null, null), expect(s.property.boolean().type.serializedHasChanged(undefined, null)).toBeTruthy();
).toBeFalsy(); expect(s.property.boolean().type.serializedHasChanged(null, false)).toBeTruthy();
expect( expect(s.property.boolean().type.serializedHasChanged(undefined, false)).toBeTruthy();
s.property.boolean().type.serializedHasChanged(undefined, undefined), expect(s.property.boolean().type.serializedHasChanged(false, null)).toBeTruthy();
).toBeFalsy(); expect(s.property.boolean().type.serializedHasChanged(false, undefined)).toBeTruthy();
expect(
s.property.boolean().type.serializedHasChanged(null, undefined),
).toBeTruthy();
expect(
s.property.boolean().type.serializedHasChanged(undefined, null),
).toBeTruthy();
expect(
s.property.boolean().type.serializedHasChanged(null, false),
).toBeTruthy();
expect(
s.property.boolean().type.serializedHasChanged(undefined, false),
).toBeTruthy();
expect(
s.property.boolean().type.serializedHasChanged(false, null),
).toBeTruthy();
expect(
s.property.boolean().type.serializedHasChanged(false, undefined),
).toBeTruthy();
s.property.boolean().type.resetDiff(false); s.property.boolean().type.resetDiff(false);
s.property.boolean().type.resetDiff(undefined); s.property.boolean().type.resetDiff(undefined);
s.property.boolean().type.resetDiff(null); s.property.boolean().type.resetDiff(null);
expect( expect(s.property.boolean().type.applyPatch(false, true, true)).toBeTruthy();
s.property.boolean().type.applyPatch(false, true, true), expect(s.property.boolean().type.applyPatch(false, true, false)).toBeTruthy();
).toBeTruthy(); expect(s.property.boolean().type.applyPatch(true, false, false)).toBeFalsy();
expect( expect(s.property.boolean().type.applyPatch(false, undefined, false)).toBeUndefined();
s.property.boolean().type.applyPatch(false, true, false),
).toBeTruthy();
expect(
s.property.boolean().type.applyPatch(true, false, false),
).toBeFalsy();
expect(
s.property.boolean().type.applyPatch(false, undefined, false),
).toBeUndefined();
expect(s.property.boolean().type.applyPatch(false, null, false)).toBeNull(); expect(s.property.boolean().type.applyPatch(false, null, false)).toBeNull();
expect( expect(s.property.boolean().type.applyPatch(undefined, null, false)).toBeNull();
s.property.boolean().type.applyPatch(undefined, null, false),
).toBeNull();
expect(s.property.boolean().type.applyPatch(null, null, false)).toBeNull(); expect(s.property.boolean().type.applyPatch(null, null, false)).toBeNull();
expect( expect(s.property.boolean().type.applyPatch(null, false, false)).toBeFalsy();
s.property.boolean().type.applyPatch(null, false, false),
).toBeFalsy();
}); });
test("invalid parameters types", () => { test("invalid parameters types", () => {
@ -100,20 +68,10 @@ describe("boolean type", () => {
expect(s.property.boolean().type.serializeDiff(1 as any)).toBeTruthy(); expect(s.property.boolean().type.serializeDiff(1 as any)).toBeTruthy();
expect(s.property.boolean().type.serializeDiff(0 as any)).toBeFalsy(); expect(s.property.boolean().type.serializeDiff(0 as any)).toBeFalsy();
expect(() => s.property.boolean().type.resetDiff({} as any)).not.toThrow(); expect(() => s.property.boolean().type.resetDiff({} as any)).not.toThrow();
expect( expect(s.property.boolean().type.hasChanged({} as any, {} as any)).toBeTruthy();
s.property.boolean().type.hasChanged({} as any, {} as any), expect(s.property.boolean().type.hasChanged(false as any, false as any)).toBeFalsy();
).toBeTruthy(); expect(s.property.boolean().type.serializedHasChanged({} as any, {} as any)).toBeTruthy();
expect( expect(s.property.boolean().type.serializedHasChanged(false as any, false as any)).toBeFalsy();
s.property.boolean().type.hasChanged(false as any, false as any),
).toBeFalsy();
expect(
s.property.boolean().type.serializedHasChanged({} as any, {} as any),
).toBeTruthy();
expect(
s.property
.boolean()
.type.serializedHasChanged(false as any, false as any),
).toBeFalsy();
expect(s.property.boolean().type.clone({} as any)).toStrictEqual({}); expect(s.property.boolean().type.clone({} as any)).toStrictEqual({});
}); });
}); });

View file

@ -10,24 +10,11 @@ describe("date type", () => {
}); });
test("date type functions", () => { test("date type functions", () => {
expect(s.property.date().type.serialize(testDate)).toBe( expect(s.property.date().type.serialize(testDate)).toBe(testDate.toISOString());
testDate.toISOString(), expect(s.property.date().type.deserialize(testDate.toISOString())?.getTime()).toBe(testDate.getTime());
); expect(s.property.date().type.serializeDiff(new Date(testDate))).toBe(testDate.toISOString());
expect( expect(s.property.date().type.deserialize("2565152-2156121-256123121 5121544175:21515612").valueOf()).toBeNaN();
s.property.date().type.deserialize(testDate.toISOString())?.getTime(), expect(s.property.date().type.serialize(new Date(NaN))).toBe((new Date(NaN)).toString());
).toBe(testDate.getTime());
expect(s.property.date().type.serializeDiff(new Date(testDate))).toBe(
testDate.toISOString(),
);
expect(
s.property
.date()
.type.deserialize("2565152-2156121-256123121 5121544175:21515612")
.valueOf(),
).toBeNaN();
expect(s.property.date().type.serialize(new Date(NaN))).toBe(
new Date(NaN).toString(),
);
expect(s.property.date().type.serialize(null)).toBe(null); expect(s.property.date().type.serialize(null)).toBe(null);
expect(s.property.date().type.deserialize(null)).toBe(null); expect(s.property.date().type.deserialize(null)).toBe(null);
@ -37,9 +24,7 @@ describe("date type", () => {
expect(s.property.date().type.deserialize(undefined)).toBe(undefined); expect(s.property.date().type.deserialize(undefined)).toBe(undefined);
expect(s.property.date().type.serializeDiff(undefined)).toBe(undefined); expect(s.property.date().type.serializeDiff(undefined)).toBe(undefined);
expect( expect(s.property.date().type.hasChanged(testDate, new Date(testDate))).toBeFalsy();
s.property.date().type.hasChanged(testDate, new Date(testDate)),
).toBeFalsy();
expect(s.property.date().type.hasChanged(null, null)).toBeFalsy(); expect(s.property.date().type.hasChanged(null, null)).toBeFalsy();
expect(s.property.date().type.hasChanged(undefined, undefined)).toBeFalsy(); expect(s.property.date().type.hasChanged(undefined, undefined)).toBeFalsy();
expect(s.property.date().type.hasChanged(null, undefined)).toBeTruthy(); expect(s.property.date().type.hasChanged(null, undefined)).toBeTruthy();
@ -48,129 +33,48 @@ describe("date type", () => {
expect(s.property.date().type.hasChanged(undefined, testDate)).toBeTruthy(); expect(s.property.date().type.hasChanged(undefined, testDate)).toBeTruthy();
expect(s.property.date().type.hasChanged(testDate, null)).toBeTruthy(); expect(s.property.date().type.hasChanged(testDate, null)).toBeTruthy();
expect(s.property.date().type.hasChanged(new Date(NaN), null)).toBeTruthy(); expect(s.property.date().type.hasChanged(new Date(NaN), null)).toBeTruthy();
expect( expect(s.property.date().type.hasChanged(new Date(NaN), undefined)).toBeTruthy();
s.property.date().type.hasChanged(new Date(NaN), undefined), expect(s.property.date().type.hasChanged(new Date(NaN), new Date(NaN))).toBeFalsy();
).toBeTruthy();
expect(
s.property.date().type.hasChanged(new Date(NaN), new Date(NaN)),
).toBeFalsy();
expect( expect(s.property.date().type.serializedHasChanged(testDate.toISOString(), (new Date(testDate)).toISOString())).toBeFalsy();
s.property
.date()
.type.serializedHasChanged(
testDate.toISOString(),
new Date(testDate).toISOString(),
),
).toBeFalsy();
expect(s.property.date().type.serializedHasChanged(null, null)).toBeFalsy(); expect(s.property.date().type.serializedHasChanged(null, null)).toBeFalsy();
expect( expect(s.property.date().type.serializedHasChanged(undefined, undefined)).toBeFalsy();
s.property.date().type.serializedHasChanged(undefined, undefined), expect(s.property.date().type.serializedHasChanged(null, undefined)).toBeTruthy();
).toBeFalsy(); expect(s.property.date().type.serializedHasChanged(undefined, null)).toBeTruthy();
expect( expect(s.property.date().type.serializedHasChanged(null, testDate.toISOString())).toBeTruthy();
s.property.date().type.serializedHasChanged(null, undefined), expect(s.property.date().type.serializedHasChanged(undefined, testDate.toISOString())).toBeTruthy();
).toBeTruthy(); expect(s.property.date().type.serializedHasChanged(testDate.toISOString(), null)).toBeTruthy();
expect( expect(s.property.date().type.serializedHasChanged((new Date(NaN)).toString(), null)).toBeTruthy();
s.property.date().type.serializedHasChanged(undefined, null), expect(s.property.date().type.serializedHasChanged((new Date(NaN)).toString(), undefined)).toBeTruthy();
).toBeTruthy(); expect(s.property.date().type.serializedHasChanged((new Date(NaN)).toString(), (new Date(NaN)).toString())).toBeFalsy();
expect(
s.property.date().type.serializedHasChanged(null, testDate.toISOString()),
).toBeTruthy();
expect(
s.property
.date()
.type.serializedHasChanged(undefined, testDate.toISOString()),
).toBeTruthy();
expect(
s.property.date().type.serializedHasChanged(testDate.toISOString(), null),
).toBeTruthy();
expect(
s.property
.date()
.type.serializedHasChanged(new Date(NaN).toString(), null),
).toBeTruthy();
expect(
s.property
.date()
.type.serializedHasChanged(new Date(NaN).toString(), undefined),
).toBeTruthy();
expect(
s.property
.date()
.type.serializedHasChanged(
new Date(NaN).toString(),
new Date(NaN).toString(),
),
).toBeFalsy();
s.property.date().type.resetDiff(testDate); s.property.date().type.resetDiff(testDate);
s.property.date().type.resetDiff(undefined); s.property.date().type.resetDiff(undefined);
s.property.date().type.resetDiff(null); s.property.date().type.resetDiff(null);
{ { // Test that the date is cloned in a different object.
// Test that the date is cloned in a different object.
const propertyValue = new Date(); const propertyValue = new Date();
const clonedPropertyValue = s.property.date().type.clone(propertyValue); const clonedPropertyValue = s.property.date().type.clone(propertyValue);
expect(clonedPropertyValue).not.toBe(propertyValue); expect(clonedPropertyValue).not.toBe(propertyValue);
expect(clonedPropertyValue).toEqual(propertyValue); expect(clonedPropertyValue).toEqual(propertyValue);
} }
expect( expect(s.property.date().type.applyPatch(new Date("2022-02-22"), testDate.toISOString(), false)?.getTime()).toBe(testDate.getTime());
s.property expect(s.property.date().type.applyPatch(null, testDate.toISOString(), true)?.getTime()).toBe(testDate.getTime());
.date() expect(s.property.date().type.applyPatch(undefined, "2565152-2156121-256123121 5121544175:21515612", false).valueOf()).toBeNaN();
.type.applyPatch(new Date("2022-02-22"), testDate.toISOString(), false) expect(s.property.date().type.applyPatch(new Date(), undefined, false)).toBeUndefined();
?.getTime(), expect(s.property.date().type.applyPatch(new Date(), null, false)).toBeNull();
).toBe(testDate.getTime());
expect(
s.property
.date()
.type.applyPatch(null, testDate.toISOString(), true)
?.getTime(),
).toBe(testDate.getTime());
expect(
s.property
.date()
.type.applyPatch(
undefined,
"2565152-2156121-256123121 5121544175:21515612",
false,
)
.valueOf(),
).toBeNaN();
expect(
s.property.date().type.applyPatch(new Date(), undefined, false),
).toBeUndefined();
expect(
s.property.date().type.applyPatch(new Date(), null, false),
).toBeNull();
}); });
test("invalid parameters types", () => { test("invalid parameters types", () => {
expect(() => s.property.date().type.serialize({} as any)).toThrowError( expect(() => s.property.date().type.serialize({} as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(s.property.date().type.deserialize({} as any).getTime()).toBe(NaN);
); expect(() => s.property.date().type.serializeDiff({} as any)).toThrowError(InvalidTypeValueError);
expect(
s.property
.date()
.type.deserialize({} as any)
.getTime(),
).toBe(NaN);
expect(() => s.property.date().type.serializeDiff({} as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => s.property.date().type.resetDiff({} as any)).not.toThrow(); expect(() => s.property.date().type.resetDiff({} as any)).not.toThrow();
expect( expect(s.property.date().type.hasChanged({} as any, {} as any)).toBeTruthy();
s.property.date().type.hasChanged({} as any, {} as any), expect(s.property.date().type.hasChanged(false as any, false as any)).toBeFalsy();
).toBeTruthy(); expect(s.property.date().type.serializedHasChanged({} as any, {} as any)).toBeTruthy();
expect( expect(s.property.date().type.serializedHasChanged(false as any, false as any)).toBeFalsy();
s.property.date().type.hasChanged(false as any, false as any),
).toBeFalsy();
expect(
s.property.date().type.serializedHasChanged({} as any, {} as any),
).toBeTruthy();
expect(
s.property.date().type.serializedHasChanged(false as any, false as any),
).toBeFalsy();
expect(s.property.date().type.clone({} as any)).toStrictEqual({}); expect(s.property.date().type.clone({} as any)).toStrictEqual({});
}); });
}); });

View file

@ -22,9 +22,7 @@ describe("decimal type", () => {
expect(s.property.decimal().type.hasChanged(5.257, 5.257)).toBeFalsy(); expect(s.property.decimal().type.hasChanged(5.257, 5.257)).toBeFalsy();
expect(s.property.decimal().type.hasChanged(null, null)).toBeFalsy(); expect(s.property.decimal().type.hasChanged(null, null)).toBeFalsy();
expect( expect(s.property.decimal().type.hasChanged(undefined, undefined)).toBeFalsy();
s.property.decimal().type.hasChanged(undefined, undefined),
).toBeFalsy();
expect(s.property.decimal().type.hasChanged(null, undefined)).toBeTruthy(); expect(s.property.decimal().type.hasChanged(null, undefined)).toBeTruthy();
expect(s.property.decimal().type.hasChanged(undefined, null)).toBeTruthy(); expect(s.property.decimal().type.hasChanged(undefined, null)).toBeTruthy();
expect(s.property.decimal().type.hasChanged(null, 5.257)).toBeTruthy(); expect(s.property.decimal().type.hasChanged(null, 5.257)).toBeTruthy();
@ -32,75 +30,37 @@ describe("decimal type", () => {
expect(s.property.decimal().type.hasChanged(5.257, null)).toBeTruthy(); expect(s.property.decimal().type.hasChanged(5.257, null)).toBeTruthy();
expect(s.property.decimal().type.hasChanged(5.257, undefined)).toBeTruthy(); expect(s.property.decimal().type.hasChanged(5.257, undefined)).toBeTruthy();
expect( expect(s.property.decimal().type.serializedHasChanged("5.257", "5.257")).toBeFalsy();
s.property.decimal().type.serializedHasChanged("5.257", "5.257"), expect(s.property.decimal().type.serializedHasChanged(null, null)).toBeFalsy();
).toBeFalsy(); expect(s.property.decimal().type.serializedHasChanged(undefined, undefined)).toBeFalsy();
expect( expect(s.property.decimal().type.serializedHasChanged(null, undefined)).toBeTruthy();
s.property.decimal().type.serializedHasChanged(null, null), expect(s.property.decimal().type.serializedHasChanged(undefined, null)).toBeTruthy();
).toBeFalsy(); expect(s.property.decimal().type.serializedHasChanged(null, "5.257")).toBeTruthy();
expect( expect(s.property.decimal().type.serializedHasChanged(undefined, "5.257")).toBeTruthy();
s.property.decimal().type.serializedHasChanged(undefined, undefined), expect(s.property.decimal().type.serializedHasChanged("5.257", null)).toBeTruthy();
).toBeFalsy(); expect(s.property.decimal().type.serializedHasChanged("5.257", undefined)).toBeTruthy();
expect(
s.property.decimal().type.serializedHasChanged(null, undefined),
).toBeTruthy();
expect(
s.property.decimal().type.serializedHasChanged(undefined, null),
).toBeTruthy();
expect(
s.property.decimal().type.serializedHasChanged(null, "5.257"),
).toBeTruthy();
expect(
s.property.decimal().type.serializedHasChanged(undefined, "5.257"),
).toBeTruthy();
expect(
s.property.decimal().type.serializedHasChanged("5.257", null),
).toBeTruthy();
expect(
s.property.decimal().type.serializedHasChanged("5.257", undefined),
).toBeTruthy();
s.property.decimal().type.resetDiff(5.257); s.property.decimal().type.resetDiff(5.257);
s.property.decimal().type.resetDiff(undefined); s.property.decimal().type.resetDiff(undefined);
s.property.decimal().type.resetDiff(null); s.property.decimal().type.resetDiff(null);
expect(s.property.decimal().type.applyPatch(1, "5.257", false)).toBe(5.257); expect(s.property.decimal().type.applyPatch(1, "5.257", false)).toBe(5.257);
expect(s.property.decimal().type.applyPatch(undefined, "5.257", true)).toBe( expect(s.property.decimal().type.applyPatch(undefined, "5.257", true)).toBe(5.257);
5.257, expect(s.property.decimal().type.applyPatch(null, "5.257", false)).toBe(5.257);
); expect(s.property.decimal().type.applyPatch(5.257, undefined, false)).toBeUndefined();
expect(s.property.decimal().type.applyPatch(null, "5.257", false)).toBe(
5.257,
);
expect(
s.property.decimal().type.applyPatch(5.257, undefined, false),
).toBeUndefined();
expect(s.property.decimal().type.applyPatch(5.257, null, false)).toBeNull(); expect(s.property.decimal().type.applyPatch(5.257, null, false)).toBeNull();
}); });
test("invalid parameters types", () => { test("invalid parameters types", () => {
expect(() => s.property.decimal().type.serialize({} as any)).toThrowError( expect(() => s.property.decimal().type.serialize({} as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError,
);
expect(s.property.decimal().type.deserialize({} as any)).toBe(NaN); expect(s.property.decimal().type.deserialize({} as any)).toBe(NaN);
expect(s.property.decimal().type.deserialize({} as any)).toBe(NaN); expect(s.property.decimal().type.deserialize({} as any)).toBe(NaN);
expect(() => expect(() => s.property.decimal().type.serializeDiff({} as any)).toThrowError(InvalidTypeValueError);
s.property.decimal().type.serializeDiff({} as any),
).toThrowError(InvalidTypeValueError);
expect(() => s.property.decimal().type.resetDiff({} as any)).not.toThrow(); expect(() => s.property.decimal().type.resetDiff({} as any)).not.toThrow();
expect( expect(s.property.decimal().type.hasChanged({} as any, {} as any)).toBeTruthy();
s.property.decimal().type.hasChanged({} as any, {} as any), expect(s.property.decimal().type.hasChanged(false as any, false as any)).toBeFalsy();
).toBeTruthy(); expect(s.property.decimal().type.serializedHasChanged({} as any, {} as any)).toBeTruthy();
expect( expect(s.property.decimal().type.serializedHasChanged(false as any, false as any)).toBeFalsy();
s.property.decimal().type.hasChanged(false as any, false as any),
).toBeFalsy();
expect(
s.property.decimal().type.serializedHasChanged({} as any, {} as any),
).toBeTruthy();
expect(
s.property
.decimal()
.type.serializedHasChanged(false as any, false as any),
).toBeFalsy();
expect(s.property.decimal().type.clone({} as any)).toStrictEqual({}); expect(s.property.decimal().type.clone({} as any)).toStrictEqual({});
}); });
}); });

View file

@ -1,10 +1,5 @@
import {describe, expect, test} from "vitest"; import {describe, expect, test} from "vitest";
import { import {InvalidTypeValueError, NumericType, s, StringType} from "../../../src/library";
InvalidTypeValueError,
NumericType,
s,
StringType,
} from "../../../src/library";
import {MapType} from "../../../src/model/types/map"; import {MapType} from "../../../src/model/types/map";
describe("map type", () => { describe("map type", () => {
@ -13,10 +8,7 @@ describe("map type", () => {
expect(mapType.type).toBeInstanceOf(MapType); expect(mapType.type).toBeInstanceOf(MapType);
}); });
const testProperty = s.property.map( const testProperty = s.property.map(s.property.string(), s.property.decimal());
s.property.string(),
s.property.decimal(),
);
const testMapValue = new Map<string, number>(); const testMapValue = new Map<string, number>();
testMapValue.set("test", 1.52); testMapValue.set("test", 1.52);
testMapValue.set("another", 55); testMapValue.set("another", 55);
@ -26,12 +18,10 @@ describe("map type", () => {
test: "1.52", test: "1.52",
another: "55", another: "55",
}); });
expect( expect(testProperty.type.deserialize({
testProperty.type.deserialize({ test: "1.52",
test: "1.52", another: "55",
another: "55", })).toEqual(testMapValue);
}),
).toEqual(testMapValue);
expect(testProperty.type.serializeDiff(testMapValue)).toEqual({ expect(testProperty.type.serializeDiff(testMapValue)).toEqual({
test: "1.52", test: "1.52",
another: "55", another: "55",
@ -48,17 +38,11 @@ describe("map type", () => {
const anotherTestMapValue = new Map<string, number>(); const anotherTestMapValue = new Map<string, number>();
anotherTestMapValue.set("test", 1.52); anotherTestMapValue.set("test", 1.52);
anotherTestMapValue.set("another", 55); anotherTestMapValue.set("another", 55);
expect( expect(testProperty.type.hasChanged(testMapValue, anotherTestMapValue)).toBeFalsy();
testProperty.type.hasChanged(testMapValue, anotherTestMapValue),
).toBeFalsy();
anotherTestMapValue.set("test", 1.521); anotherTestMapValue.set("test", 1.521);
expect( expect(testProperty.type.hasChanged(testMapValue, anotherTestMapValue)).toBeTruthy();
testProperty.type.hasChanged(testMapValue, anotherTestMapValue),
).toBeTruthy();
anotherTestMapValue.delete("test"); anotherTestMapValue.delete("test");
expect( expect(testProperty.type.hasChanged(testMapValue, anotherTestMapValue)).toBeTruthy();
testProperty.type.hasChanged(testMapValue, anotherTestMapValue),
).toBeTruthy();
expect(testProperty.type.hasChanged(null, null)).toBeFalsy(); expect(testProperty.type.hasChanged(null, null)).toBeFalsy();
expect(testProperty.type.hasChanged(undefined, undefined)).toBeFalsy(); expect(testProperty.type.hasChanged(undefined, undefined)).toBeFalsy();
expect(testProperty.type.hasChanged(null, undefined)).toBeTruthy(); expect(testProperty.type.hasChanged(null, undefined)).toBeTruthy();
@ -68,109 +52,63 @@ describe("map type", () => {
expect(testProperty.type.hasChanged(testMapValue, null)).toBeTruthy(); expect(testProperty.type.hasChanged(testMapValue, null)).toBeTruthy();
expect(testProperty.type.hasChanged(testMapValue, undefined)).toBeTruthy(); expect(testProperty.type.hasChanged(testMapValue, undefined)).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged({ test: "1.52", another: "55" }, { test: "1.52", another: "55" })).toBeFalsy();
testProperty.type.serializedHasChanged( expect(testProperty.type.serializedHasChanged({ test: "1.52", another: "55" }, { test: "1.521", another: "55" })).toBeTruthy();
{test: "1.52", another: "55"}, expect(testProperty.type.serializedHasChanged({ test: "1.52", another: "55" }, { another: "55" })).toBeTruthy();
{test: "1.52", another: "55"},
),
).toBeFalsy();
expect(
testProperty.type.serializedHasChanged(
{test: "1.52", another: "55"},
{test: "1.521", another: "55"},
),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
{test: "1.52", another: "55"},
{another: "55"},
),
).toBeTruthy();
expect(testProperty.type.serializedHasChanged(null, null)).toBeFalsy(); expect(testProperty.type.serializedHasChanged(null, null)).toBeFalsy();
expect( expect(testProperty.type.serializedHasChanged(undefined, undefined)).toBeFalsy();
testProperty.type.serializedHasChanged(undefined, undefined), expect(testProperty.type.serializedHasChanged(null, undefined)).toBeTruthy();
).toBeFalsy(); expect(testProperty.type.serializedHasChanged(undefined, null)).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged(null, { test: "1.52", another: "55" })).toBeTruthy();
testProperty.type.serializedHasChanged(null, undefined), expect(testProperty.type.serializedHasChanged(undefined, { test: "1.52", another: "55" })).toBeTruthy();
).toBeTruthy(); expect(testProperty.type.serializedHasChanged({ test: "1.52", another: "55" }, null)).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged({ test: "1.52", another: "55" }, undefined)).toBeTruthy();
testProperty.type.serializedHasChanged(undefined, null),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(null, {
test: "1.52",
another: "55",
}),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(undefined, {
test: "1.52",
another: "55",
}),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
{test: "1.52", another: "55"},
null,
),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
{test: "1.52", another: "55"},
undefined,
),
).toBeTruthy();
testProperty.type.resetDiff(testMapValue); testProperty.type.resetDiff(testMapValue);
testProperty.type.resetDiff(undefined); testProperty.type.resetDiff(undefined);
testProperty.type.resetDiff(null); testProperty.type.resetDiff(null);
{ { // Test that keys and values are cloned in a different map.
// Test that keys and values are cloned in a different map.
const clonedTestMapValue = testProperty.type.clone(testMapValue); const clonedTestMapValue = testProperty.type.clone(testMapValue);
expect(clonedTestMapValue).not.toBe(testMapValue); expect(clonedTestMapValue).not.toBe(testMapValue);
expect(clonedTestMapValue).toEqual(testMapValue); expect(clonedTestMapValue).toEqual(testMapValue);
} }
{ { // Test that values are cloned in a different object.
// Test that values are cloned in a different object.
const propertyValue = new Map(); const propertyValue = new Map();
propertyValue.set("test", [12, 11]); propertyValue.set("test", [12, 11]);
const clonedPropertyValue = s.property const clonedPropertyValue = s.property.stringMap(s.property.array(s.property.numeric())).type.clone(propertyValue);
.stringMap(s.property.array(s.property.numeric()))
.type.clone(propertyValue);
expect(clonedPropertyValue).not.toBe(propertyValue); expect(clonedPropertyValue).not.toBe(propertyValue);
expect(clonedPropertyValue).toEqual(propertyValue); expect(clonedPropertyValue).toEqual(propertyValue);
expect(clonedPropertyValue.get("test")).not.toBe( expect(clonedPropertyValue.get("test")).not.toBe(propertyValue.get("test"));
propertyValue.get("test"), expect(clonedPropertyValue.get("test")).toEqual(propertyValue.get("test"));
);
expect(clonedPropertyValue.get("test")).toEqual(
propertyValue.get("test"),
);
} }
expect(testProperty.type.clone(undefined)).toBe(undefined); expect(testProperty.type.clone(undefined)).toBe(undefined);
expect(testProperty.type.clone(null)).toBe(null); expect(testProperty.type.clone(null)).toBe(null);
{ { // Apply a patch with undefined / NULL values.
// Apply a patch with undefined / NULL values. expect(testProperty.type.applyPatch(
expect( testMapValue,
testProperty.type.applyPatch(testMapValue, undefined, false), undefined,
).toBeUndefined(); false
expect(testProperty.type.applyPatch(testMapValue, null, true)).toBeNull(); )).toBeUndefined();
expect(testProperty.type.applyPatch(
testMapValue,
null,
true
)).toBeNull();
} }
{ { // Invalid patch.
// Invalid patch. expect(
expect(() => () => testProperty.type.applyPatch(testMapValue, 5416 as any, false)
testProperty.type.applyPatch(testMapValue, 5416 as any, false),
).toThrow(InvalidTypeValueError); ).toThrow(InvalidTypeValueError);
} }
{ { // Apply a patch.
// Apply a patch.
{ {
const objectInstance = testProperty.type.applyPatch( const objectInstance = testProperty.type.applyPatch(
testMapValue, testMapValue,
{test: "1.521"}, { test: "1.521" },
true, true,
); );
@ -183,8 +121,8 @@ describe("map type", () => {
{ {
const objectInstance = testProperty.type.applyPatch( const objectInstance = testProperty.type.applyPatch(
undefined, undefined,
{test: "1.52"}, { test: "1.52" },
false, false
); );
const expectedMapValue = new Map<string, number>(); const expectedMapValue = new Map<string, number>();
@ -195,8 +133,8 @@ describe("map type", () => {
{ {
const objectInstance = testProperty.type.applyPatch( const objectInstance = testProperty.type.applyPatch(
null, null,
{test: "1.52"}, { test: "1.52" },
false, false
); );
const expectedMapValue = new Map<string, number>(); const expectedMapValue = new Map<string, number>();
@ -207,40 +145,18 @@ describe("map type", () => {
}); });
test("invalid parameters types", () => { test("invalid parameters types", () => {
expect(() => testProperty.type.serialize(5 as any)).toThrowError( expect(() => testProperty.type.serialize(5 as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(() => testProperty.type.deserialize(5 as any)).toThrowError(InvalidTypeValueError);
); expect(() => testProperty.type.serializeDiff(5 as any)).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.deserialize(5 as any)).toThrowError( expect(() => testProperty.type.clone(5 as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError,
);
expect(() => testProperty.type.serializeDiff(5 as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => testProperty.type.clone(5 as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => testProperty.type.serialize([] as any)).toThrowError( expect(() => testProperty.type.serialize([] as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(() => testProperty.type.deserialize([] as any)).toThrowError(InvalidTypeValueError);
); expect(() => testProperty.type.serializeDiff([] as any)).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.deserialize([] as any)).toThrowError( expect(() => testProperty.type.clone([] as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError,
);
expect(() => testProperty.type.serializeDiff([] as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => testProperty.type.clone([] as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => testProperty.type.serialize({} as any)).toThrowError( expect(() => testProperty.type.serialize({} as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(() => testProperty.type.serializeDiff({} as any)).toThrowError(InvalidTypeValueError);
); expect(() => testProperty.type.clone({} as any)).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.serializeDiff({} as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => testProperty.type.clone({} as any)).toThrowError(
InvalidTypeValueError,
);
}); });
}); });

View file

@ -1,7 +1,8 @@
import {describe, expect, test} from "vitest"; import {describe, expect, test} from "vitest";
import {InvalidTypeValueError, ModelType, s} from "../../../src/library"; import {InvalidTypeValueError, ModelType, s} from "../../../src/library";
class TestModel { class TestModel
{
id: number; id: number;
name: string; name: string;
price: number; price: number;
@ -24,381 +25,164 @@ describe("model type", () => {
}); });
test("model type functions", () => { test("model type functions", () => {
{ { // Try to serialize / deserialize.
// Try to serialize / deserialize. const testModelInstance = testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance;
const testModelInstance = testModel.model( expect(s.property.model(testModel).type.serialize(testModelInstance)).toEqual({ id: 1, name: "test", price: "12.548777" });
Object.assign(new TestModel(), { expect(testModel.model(
id: 1, s.property.model(testModel).type.deserialize({ id: 1, name: "test", price: "12.548777" })
name: "test", ).getInstanceProperties()).toEqual(testModel.model(testModelInstance).getInstanceProperties());
price: 12.548777,
}),
).instance;
expect(
s.property.model(testModel).type.serialize(testModelInstance),
).toEqual({id: 1, name: "test", price: "12.548777"});
expect(
testModel
.model(
s.property
.model(testModel)
.type.deserialize({id: 1, name: "test", price: "12.548777"}),
)
.getInstanceProperties(),
).toEqual(testModel.model(testModelInstance).getInstanceProperties());
} }
{ { // Try to serialize the difference.
// Try to serialize the difference. const testModelInstance = testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance;
const testModelInstance = testModel.model(
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance;
testModelInstance.name = "new"; testModelInstance.name = "new";
expect( expect(s.property.model(testModel).type.serializeDiff(testModelInstance)).toEqual({ id: 1, name: "new" });
s.property.model(testModel).type.serializeDiff(testModelInstance),
).toEqual({id: 1, name: "new"});
} }
expect(s.property.model(testModel).type.serialize(null)).toEqual(null); expect(s.property.model(testModel).type.serialize(null)).toEqual(null);
expect(s.property.model(testModel).type.deserialize(null)).toEqual(null); expect(s.property.model(testModel).type.deserialize(null)).toEqual(null);
expect(s.property.model(testModel).type.serializeDiff(null)).toEqual(null); expect(s.property.model(testModel).type.serializeDiff(null)).toEqual(null);
expect(s.property.model(testModel).type.serialize(undefined)).toEqual( expect(s.property.model(testModel).type.serialize(undefined)).toEqual(undefined);
undefined, expect(s.property.model(testModel).type.deserialize(undefined)).toEqual(undefined);
); expect(s.property.model(testModel).type.serializeDiff(undefined)).toEqual(undefined);
expect(s.property.model(testModel).type.deserialize(undefined)).toEqual(
undefined,
);
expect(s.property.model(testModel).type.serializeDiff(undefined)).toEqual(
undefined,
);
{ {
const testModelInstance = testModel.model( const testModelInstance = testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance;
Object.assign(new TestModel(), { expect(s.property.model(testModel).type.hasChanged(testModelInstance, testModelInstance)).toBeFalsy();
id: 1,
name: "test",
price: 12.548777,
}),
).instance;
expect(
s.property
.model(testModel)
.type.hasChanged(testModelInstance, testModelInstance),
).toBeFalsy();
} }
{ {
const testModelInstance = testModel.model( const testModelInstance = testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance;
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance;
testModelInstance.price = 12.548778; testModelInstance.price = 12.548778;
expect( expect(s.property.model(testModel).type.hasChanged(testModelInstance, testModelInstance)).toBeTruthy();
s.property
.model(testModel)
.type.hasChanged(testModelInstance, testModelInstance),
).toBeTruthy();
} }
expect(s.property.model(testModel).type.hasChanged(null, null)).toBeFalsy(); expect(s.property.model(testModel).type.hasChanged(null, null)).toBeFalsy();
expect( expect(s.property.model(testModel).type.hasChanged(undefined, undefined)).toBeFalsy();
s.property.model(testModel).type.hasChanged(undefined, undefined), expect(s.property.model(testModel).type.hasChanged(null, undefined)).toBeTruthy();
).toBeFalsy(); expect(s.property.model(testModel).type.hasChanged(undefined, null)).toBeTruthy();
expect( expect(s.property.model(testModel).type.hasChanged(null, testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance)).toBeTruthy();
s.property.model(testModel).type.hasChanged(null, undefined), expect(s.property.model(testModel).type.hasChanged(undefined, testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance)).toBeTruthy();
).toBeTruthy(); expect(s.property.model(testModel).type.hasChanged(testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance, null)).toBeTruthy();
expect( expect(s.property.model(testModel).type.hasChanged(testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance, undefined)).toBeTruthy();
s.property.model(testModel).type.hasChanged(undefined, null),
).toBeTruthy();
expect(
s.property.model(testModel).type.hasChanged(
null,
testModel.model(
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance,
),
).toBeTruthy();
expect(
s.property.model(testModel).type.hasChanged(
undefined,
testModel.model(
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance,
),
).toBeTruthy();
expect(
s.property.model(testModel).type.hasChanged(
testModel.model(
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance,
null,
),
).toBeTruthy();
expect(
s.property.model(testModel).type.hasChanged(
testModel.model(
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance,
undefined,
),
).toBeTruthy();
expect( expect(s.property.model(testModel).type.serializedHasChanged(
s.property { id: 1, name: "test", price: "12.548777" },
.model(testModel) { id: 1, price: "12.548777", name: "test" },
.type.serializedHasChanged( )).toBeFalsy();
{id: 1, name: "test", price: "12.548777"}, expect(s.property.model(testModel).type.serializedHasChanged(
{id: 1, price: "12.548777", name: "test"}, { id: 1, name: "test", price: "12.548777" },
), { id: 1, name: "test", price: "12.548778" },
).toBeFalsy(); )).toBeTruthy();
expect( expect(s.property.model(testModel).type.serializedHasChanged(null, null)).toBeFalsy();
s.property expect(s.property.model(testModel).type.serializedHasChanged(undefined, undefined)).toBeFalsy();
.model(testModel) expect(s.property.model(testModel).type.serializedHasChanged(null, undefined)).toBeTruthy();
.type.serializedHasChanged( expect(s.property.model(testModel).type.serializedHasChanged(undefined, null)).toBeTruthy();
{id: 1, name: "test", price: "12.548777"}, expect(s.property.model(testModel).type.serializedHasChanged(null, { id: 1, name: "test", price: "12.548777" })).toBeTruthy();
{id: 1, name: "test", price: "12.548778"}, expect(s.property.model(testModel).type.serializedHasChanged(undefined, { id: 1, name: "test", price: "12.548777" })).toBeTruthy();
), expect(s.property.model(testModel).type.serializedHasChanged({ id: 1, name: "test", price: "12.548777" }, null)).toBeTruthy();
).toBeTruthy(); expect(s.property.model(testModel).type.serializedHasChanged({ id: 1, name: "test", price: "12.548777" }, undefined)).toBeTruthy();
expect(
s.property.model(testModel).type.serializedHasChanged(null, null),
).toBeFalsy();
expect(
s.property
.model(testModel)
.type.serializedHasChanged(undefined, undefined),
).toBeFalsy();
expect(
s.property.model(testModel).type.serializedHasChanged(null, undefined),
).toBeTruthy();
expect(
s.property.model(testModel).type.serializedHasChanged(undefined, null),
).toBeTruthy();
expect(
s.property.model(testModel).type.serializedHasChanged(null, {
id: 1,
name: "test",
price: "12.548777",
}),
).toBeTruthy();
expect(
s.property.model(testModel).type.serializedHasChanged(undefined, {
id: 1,
name: "test",
price: "12.548777",
}),
).toBeTruthy();
expect(
s.property
.model(testModel)
.type.serializedHasChanged(
{id: 1, name: "test", price: "12.548777"},
null,
),
).toBeTruthy();
expect(
s.property
.model(testModel)
.type.serializedHasChanged(
{id: 1, name: "test", price: "12.548777"},
undefined,
),
).toBeTruthy();
{ { // Serializing the difference to check that the difference has been reset.
// Serializing the difference to check that the difference has been reset. const testModelInstance = testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance;
const testModelInstance = testModel.model(
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance;
testModelInstance.price = 555.555; testModelInstance.price = 555.555;
expect(testModel.model(testModelInstance).serializeDiff()).toEqual({ expect(testModel.model(testModelInstance).serializeDiff()).toEqual({ id: 1, price: "555.555" });
id: 1,
price: "555.555",
});
s.property.model(testModel).type.resetDiff(testModelInstance); s.property.model(testModel).type.resetDiff(testModelInstance);
expect(testModel.model(testModelInstance).serializeDiff()).toEqual({ expect(testModel.model(testModelInstance).serializeDiff()).toEqual({ id: 1 });
id: 1,
});
} }
s.property.model(testModel).type.resetDiff(undefined); s.property.model(testModel).type.resetDiff(undefined);
s.property.model(testModel).type.resetDiff(null); s.property.model(testModel).type.resetDiff(null);
{ { // Test that values are cloned in a different model instance.
// Test that values are cloned in a different model instance. const testModelInstance = testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance;
const testModelInstance = testModel.model(
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance;
testModelInstance.price = 555.555; testModelInstance.price = 555.555;
const clonedModelInstance = s.property const clonedModelInstance = s.property.model(testModel).type.clone(testModelInstance);
.model(testModel)
.type.clone(testModelInstance);
expect(clonedModelInstance).not.toBe(testModelInstance); expect(clonedModelInstance).not.toBe(testModelInstance);
expect( expect(testModel.model(clonedModelInstance).getInstanceProperties()).toEqual(testModel.model(testModelInstance).getInstanceProperties());
testModel.model(clonedModelInstance).getInstanceProperties(), expect(testModel.model(clonedModelInstance).serializeDiff()).toEqual(testModel.model(testModelInstance).serializeDiff());
).toEqual(testModel.model(testModelInstance).getInstanceProperties());
expect(testModel.model(clonedModelInstance).serializeDiff()).toEqual(
testModel.model(testModelInstance).serializeDiff(),
);
} }
expect(s.property.model(testModel).type.clone(undefined)).toBe(undefined); expect(s.property.model(testModel).type.clone(undefined)).toBe(undefined);
expect(s.property.model(testModel).type.clone(null)).toBe(null); expect(s.property.model(testModel).type.clone(null)).toBe(null);
{ { // Apply a patch with undefined / NULL values.
// Apply a patch with undefined / NULL values. expect(s.property.model(testModel).type.applyPatch(
expect( testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance,
s.property.model(testModel).type.applyPatch( undefined,
testModel.model( false
Object.assign(new TestModel(), { )).toBeUndefined();
id: 1, expect(s.property.model(testModel).type.applyPatch(
name: "test", testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance,
price: 12.548777, null,
}), true
).instance, )).toBeNull();
undefined,
false,
),
).toBeUndefined();
expect(
s.property.model(testModel).type.applyPatch(
testModel.model(
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance,
null,
true,
),
).toBeNull();
} }
{ { // Invalid patch.
// Invalid patch. expect(
expect(() => () => s.property.model(testModel).type.applyPatch(
s.property.model(testModel).type.applyPatch( testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance,
testModel.model(
Object.assign(new TestModel(), {
id: 1,
name: "test",
price: 12.548777,
}),
).instance,
5416 as any, 5416 as any,
false, false
), )
).toThrow(InvalidTypeValueError); ).toThrow(InvalidTypeValueError);
} }
{ { // Apply a patch with originals update.
// Apply a patch with originals update.
{ {
const modelInstance = s.property.model(testModel).type.applyPatch( const modelInstance = s.property.model(testModel).type.applyPatch(
testModel.model( testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance,
Object.assign(new TestModel(), { { id: 1, name: "another" },
id: 1,
name: "test",
price: 12.548777,
}),
).instance,
{id: 1, name: "another"},
true, true,
); );
expect( expect(testModel.model(modelInstance).getInstanceProperties()).toStrictEqual({
testModel.model(modelInstance).getInstanceProperties(),
).toStrictEqual({
id: 1, id: 1,
name: "another", name: "another",
price: 12.548777, price: 12.548777,
}); });
expect(testModel.model(modelInstance).serializeDiff()).toStrictEqual({ expect(testModel.model(modelInstance).serializeDiff()).toStrictEqual({ id: 1 });
id: 1,
});
} }
{ {
const modelInstance = s.property const modelInstance = s.property.model(testModel).type.applyPatch(
.model(testModel) undefined,
.type.applyPatch(undefined, {id: 1, name: "test"}, true); { id: 1, name: "test" },
true
);
expect( expect(testModel.model(modelInstance).getInstanceProperties()).toStrictEqual({
testModel.model(modelInstance).getInstanceProperties(),
).toStrictEqual({
id: 1, id: 1,
name: "test", name: "test",
price: undefined, price: undefined,
}); });
expect(testModel.model(modelInstance).serializeDiff()).toStrictEqual({ expect(testModel.model(modelInstance).serializeDiff()).toStrictEqual({ id: 1 });
id: 1,
});
} }
{ {
const modelInstance = s.property const modelInstance = s.property.model(testModel).type.applyPatch(
.model(testModel) null,
.type.applyPatch(null, {id: 1, name: "test"}, true); { id: 1, name: "test" },
true
);
expect( expect(testModel.model(modelInstance).getInstanceProperties()).toStrictEqual({
testModel.model(modelInstance).getInstanceProperties(),
).toStrictEqual({
id: 1, id: 1,
name: "test", name: "test",
price: undefined, price: undefined,
}); });
expect(testModel.model(modelInstance).serializeDiff()).toStrictEqual({ expect(testModel.model(modelInstance).serializeDiff()).toStrictEqual({ id: 1 });
id: 1,
});
} }
} }
{ { // Apply a patch without originals update.
// Apply a patch without originals update.
{ {
const modelInstance = s.property.model(testModel).type.applyPatch( const modelInstance = s.property.model(testModel).type.applyPatch(
testModel.model( testModel.model(Object.assign(new TestModel(), { id: 1, name: "test", price: 12.548777 })).instance,
Object.assign(new TestModel(), { { id: 1, name: "another" },
id: 1,
name: "test",
price: 12.548777,
}),
).instance,
{id: 1, name: "another"},
false, false,
); );
expect( expect(testModel.model(modelInstance).getInstanceProperties()).toStrictEqual({
testModel.model(modelInstance).getInstanceProperties(),
).toStrictEqual({
id: 1, id: 1,
name: "another", name: "another",
price: 12.548777, price: 12.548777,
@ -410,13 +194,13 @@ describe("model type", () => {
} }
{ {
const modelInstance = s.property const modelInstance = s.property.model(testModel).type.applyPatch(
.model(testModel) undefined,
.type.applyPatch(undefined, {id: 1, name: "test"}, false); { id: 1, name: "test" },
false
);
expect( expect(testModel.model(modelInstance).getInstanceProperties()).toStrictEqual({
testModel.model(modelInstance).getInstanceProperties(),
).toStrictEqual({
id: 1, id: 1,
name: "test", name: "test",
price: undefined, price: undefined,
@ -428,13 +212,13 @@ describe("model type", () => {
} }
{ {
const modelInstance = s.property const modelInstance = s.property.model(testModel).type.applyPatch(
.model(testModel) null,
.type.applyPatch(null, {id: 1, name: "test"}, false); { id: 1, name: "test" },
false
);
expect( expect(testModel.model(modelInstance).getInstanceProperties()).toStrictEqual({
testModel.model(modelInstance).getInstanceProperties(),
).toStrictEqual({
id: 1, id: 1,
name: "test", name: "test",
price: undefined, price: undefined,
@ -448,78 +232,27 @@ describe("model type", () => {
}); });
test("invalid parameters types", () => { test("invalid parameters types", () => {
expect(() => expect(() => s.property.model(testModel).type.serialize(5 as any)).toThrowError(InvalidTypeValueError);
s.property.model(testModel).type.serialize(5 as any), expect(() => s.property.model(testModel).type.deserialize(5 as any)).toThrowError(InvalidTypeValueError);
).toThrowError(InvalidTypeValueError); expect(() => s.property.model(testModel).type.serializeDiff(5 as any)).toThrowError(InvalidTypeValueError);
expect(() => expect(() => s.property.model(testModel).type.resetDiff(5 as any)).toThrowError(InvalidTypeValueError);
s.property.model(testModel).type.deserialize(5 as any), expect(() => s.property.model(testModel).type.hasChanged(5 as any, 5 as any)).toThrowError(InvalidTypeValueError);
).toThrowError(InvalidTypeValueError); expect(() => s.property.model(testModel).type.serializedHasChanged(5 as any, 5 as any)).toThrowError(InvalidTypeValueError);
expect(() => expect(() => s.property.model(testModel).type.clone(5 as any)).toThrowError(InvalidTypeValueError);
s.property.model(testModel).type.serializeDiff(5 as any),
).toThrowError(InvalidTypeValueError);
expect(() =>
s.property.model(testModel).type.resetDiff(5 as any),
).toThrowError(InvalidTypeValueError);
expect(() =>
s.property.model(testModel).type.hasChanged(5 as any, 5 as any),
).toThrowError(InvalidTypeValueError);
expect(() =>
s.property.model(testModel).type.serializedHasChanged(5 as any, 5 as any),
).toThrowError(InvalidTypeValueError);
expect(() => s.property.model(testModel).type.clone(5 as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => expect(() => s.property.model(testModel).type.serialize([] as any)).toThrowError(InvalidTypeValueError);
s.property.model(testModel).type.serialize([] as any), expect(() => s.property.model(testModel).type.deserialize([] as any)).toThrowError(InvalidTypeValueError);
).toThrowError(InvalidTypeValueError); expect(() => s.property.model(testModel).type.serializeDiff([] as any)).toThrowError(InvalidTypeValueError);
expect(() => expect(() => s.property.model(testModel).type.resetDiff([] as any)).toThrowError(InvalidTypeValueError);
s.property.model(testModel).type.deserialize([] as any), expect(() => s.property.model(testModel).type.hasChanged(testModel.model(new TestModel()).instance, [] as any)).toThrowError(InvalidTypeValueError);
).toThrowError(InvalidTypeValueError); expect(() => s.property.model(testModel).type.serializedHasChanged({} as any, [] as any)).toThrowError(InvalidTypeValueError);
expect(() => expect(() => s.property.model(testModel).type.clone([] as any)).toThrowError(InvalidTypeValueError);
s.property.model(testModel).type.serializeDiff([] as any),
).toThrowError(InvalidTypeValueError);
expect(() =>
s.property.model(testModel).type.resetDiff([] as any),
).toThrowError(InvalidTypeValueError);
expect(() =>
s.property
.model(testModel)
.type.hasChanged(testModel.model(new TestModel()).instance, [] as any),
).toThrowError(InvalidTypeValueError);
expect(() =>
s.property
.model(testModel)
.type.serializedHasChanged({} as any, [] as any),
).toThrowError(InvalidTypeValueError);
expect(() =>
s.property.model(testModel).type.clone([] as any),
).toThrowError(InvalidTypeValueError);
expect(() => expect(() => s.property.model(testModel).type.serialize(new class{} as any)).toThrowError(InvalidTypeValueError);
s.property.model(testModel).type.serialize(new (class {})() as any), expect(() => s.property.model(testModel).type.serializeDiff(new class{} as any)).toThrowError(InvalidTypeValueError);
).toThrowError(InvalidTypeValueError); expect(() => s.property.model(testModel).type.resetDiff(new class{} as any)).toThrowError(InvalidTypeValueError);
expect(() => expect(() => s.property.model(testModel).type.hasChanged(testModel.model(new TestModel()).instance, new class{} as any)).toThrowError(InvalidTypeValueError);
s.property.model(testModel).type.serializeDiff(new (class {})() as any), expect(s.property.model(testModel).type.serializedHasChanged({} as any, new class{} as any)).toBeFalsy();
).toThrowError(InvalidTypeValueError); expect(() => s.property.model(testModel).type.clone(new class{} as any)).toThrowError(InvalidTypeValueError);
expect(() =>
s.property.model(testModel).type.resetDiff(new (class {})() as any),
).toThrowError(InvalidTypeValueError);
expect(() =>
s.property
.model(testModel)
.type.hasChanged(
testModel.model(new TestModel()).instance,
new (class {})() as any,
),
).toThrowError(InvalidTypeValueError);
expect(
s.property
.model(testModel)
.type.serializedHasChanged({} as any, new (class {})() as any),
).toBeFalsy();
expect(() =>
s.property.model(testModel).type.clone(new (class {})() as any),
).toThrowError(InvalidTypeValueError);
}); });
}); });

View file

@ -22,9 +22,7 @@ describe("numeric type", () => {
expect(s.property.numeric().type.hasChanged(5.257, 5.257)).toBeFalsy(); expect(s.property.numeric().type.hasChanged(5.257, 5.257)).toBeFalsy();
expect(s.property.numeric().type.hasChanged(null, null)).toBeFalsy(); expect(s.property.numeric().type.hasChanged(null, null)).toBeFalsy();
expect( expect(s.property.numeric().type.hasChanged(undefined, undefined)).toBeFalsy();
s.property.numeric().type.hasChanged(undefined, undefined),
).toBeFalsy();
expect(s.property.numeric().type.hasChanged(null, undefined)).toBeTruthy(); expect(s.property.numeric().type.hasChanged(null, undefined)).toBeTruthy();
expect(s.property.numeric().type.hasChanged(undefined, null)).toBeTruthy(); expect(s.property.numeric().type.hasChanged(undefined, null)).toBeTruthy();
expect(s.property.numeric().type.hasChanged(null, 5.257)).toBeTruthy(); expect(s.property.numeric().type.hasChanged(null, 5.257)).toBeTruthy();
@ -32,33 +30,15 @@ describe("numeric type", () => {
expect(s.property.numeric().type.hasChanged(5.257, null)).toBeTruthy(); expect(s.property.numeric().type.hasChanged(5.257, null)).toBeTruthy();
expect(s.property.numeric().type.hasChanged(5.257, undefined)).toBeTruthy(); expect(s.property.numeric().type.hasChanged(5.257, undefined)).toBeTruthy();
expect( expect(s.property.numeric().type.serializedHasChanged(5.257, 5.257)).toBeFalsy();
s.property.numeric().type.serializedHasChanged(5.257, 5.257), expect(s.property.numeric().type.serializedHasChanged(null, null)).toBeFalsy();
).toBeFalsy(); expect(s.property.numeric().type.serializedHasChanged(undefined, undefined)).toBeFalsy();
expect( expect(s.property.numeric().type.serializedHasChanged(null, undefined)).toBeTruthy();
s.property.numeric().type.serializedHasChanged(null, null), expect(s.property.numeric().type.serializedHasChanged(undefined, null)).toBeTruthy();
).toBeFalsy(); expect(s.property.numeric().type.serializedHasChanged(null, 5.257)).toBeTruthy();
expect( expect(s.property.numeric().type.serializedHasChanged(undefined, 5.257)).toBeTruthy();
s.property.numeric().type.serializedHasChanged(undefined, undefined), expect(s.property.numeric().type.serializedHasChanged(5.257, null)).toBeTruthy();
).toBeFalsy(); expect(s.property.numeric().type.serializedHasChanged(5.257, undefined)).toBeTruthy();
expect(
s.property.numeric().type.serializedHasChanged(null, undefined),
).toBeTruthy();
expect(
s.property.numeric().type.serializedHasChanged(undefined, null),
).toBeTruthy();
expect(
s.property.numeric().type.serializedHasChanged(null, 5.257),
).toBeTruthy();
expect(
s.property.numeric().type.serializedHasChanged(undefined, 5.257),
).toBeTruthy();
expect(
s.property.numeric().type.serializedHasChanged(5.257, null),
).toBeTruthy();
expect(
s.property.numeric().type.serializedHasChanged(5.257, undefined),
).toBeTruthy();
s.property.numeric().type.resetDiff(5.257); s.property.numeric().type.resetDiff(5.257);
s.property.numeric().type.resetDiff(undefined); s.property.numeric().type.resetDiff(undefined);
@ -66,40 +46,20 @@ describe("numeric type", () => {
expect(s.property.numeric().type.applyPatch(1, 5.257, false)).toBe(5.257); expect(s.property.numeric().type.applyPatch(1, 5.257, false)).toBe(5.257);
expect(s.property.numeric().type.applyPatch(null, 5.257, true)).toBe(5.257); expect(s.property.numeric().type.applyPatch(null, 5.257, true)).toBe(5.257);
expect(s.property.numeric().type.applyPatch(undefined, 5.257, false)).toBe( expect(s.property.numeric().type.applyPatch(undefined, 5.257, false)).toBe(5.257);
5.257, expect(s.property.numeric().type.applyPatch(5.257, undefined, false)).toBeUndefined();
);
expect(
s.property.numeric().type.applyPatch(5.257, undefined, false),
).toBeUndefined();
expect(s.property.numeric().type.applyPatch(5.257, null, false)).toBeNull(); expect(s.property.numeric().type.applyPatch(5.257, null, false)).toBeNull();
}); });
test("invalid parameters types", () => { test("invalid parameters types", () => {
expect(() => s.property.numeric().type.serialize({} as any)).toThrowError( expect(() => s.property.numeric().type.serialize({} as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(() => s.property.numeric().type.deserialize({} as any)).toThrowError(InvalidTypeValueError)
); expect(() => s.property.numeric().type.serializeDiff({} as any)).toThrowError(InvalidTypeValueError);
expect(() => s.property.numeric().type.deserialize({} as any)).toThrowError(
InvalidTypeValueError,
);
expect(() =>
s.property.numeric().type.serializeDiff({} as any),
).toThrowError(InvalidTypeValueError);
expect(() => s.property.numeric().type.resetDiff({} as any)).not.toThrow(); expect(() => s.property.numeric().type.resetDiff({} as any)).not.toThrow();
expect( expect(s.property.numeric().type.hasChanged({} as any, {} as any)).toBeTruthy();
s.property.numeric().type.hasChanged({} as any, {} as any), expect(s.property.numeric().type.hasChanged(false as any, false as any)).toBeFalsy();
).toBeTruthy(); expect(s.property.numeric().type.serializedHasChanged({} as any, {} as any)).toBeTruthy();
expect( expect(s.property.numeric().type.serializedHasChanged(false as any, false as any)).toBeFalsy();
s.property.numeric().type.hasChanged(false as any, false as any),
).toBeFalsy();
expect(
s.property.numeric().type.serializedHasChanged({} as any, {} as any),
).toBeTruthy();
expect(
s.property
.numeric()
.type.serializedHasChanged(false as any, false as any),
).toBeFalsy();
expect(s.property.numeric().type.clone({} as any)).toStrictEqual({}); expect(s.property.numeric().type.clone({} as any)).toStrictEqual({});
}); });
}); });

View file

@ -1,11 +1,5 @@
import {describe, expect, test} from "vitest"; import {describe, expect, test} from "vitest";
import { import {InvalidTypeValueError, NumericType, ObjectType, s, StringType} from "../../../src/library";
InvalidTypeValueError,
NumericType,
ObjectType,
s,
StringType,
} from "../../../src/library";
describe("object type", () => { describe("object type", () => {
test("object type definition", () => { test("object type definition", () => {
@ -16,12 +10,10 @@ describe("object type", () => {
expect(objectType.type).toBeInstanceOf(ObjectType); expect(objectType.type).toBeInstanceOf(ObjectType);
expect((objectType.type as any).properties).toHaveLength(2); expect((objectType.type as any).properties).toHaveLength(2);
for (const property of (objectType.type as any).properties) { for (const property of (objectType.type as any).properties)
// Check all object properties. { // Check all object properties.
if (property.name == "test") if (property.name == "test") expect(property.definition.type).toBeInstanceOf(StringType);
expect(property.definition.type).toBeInstanceOf(StringType); else if (property.name == "another") expect(property.definition.type).toBeInstanceOf(NumericType);
else if (property.name == "another")
expect(property.definition.type).toBeInstanceOf(NumericType);
else expect.unreachable(); else expect.unreachable();
} }
}); });
@ -32,15 +24,9 @@ describe("object type", () => {
}); });
test("object type functions", () => { test("object type functions", () => {
expect( expect(testProperty.type.serialize({ test: "test", another: 12.548777 })).toEqual({ test: "test", another: "12.548777" });
testProperty.type.serialize({test: "test", another: 12.548777}), expect(testProperty.type.deserialize({ test: "test", another: "12.548777" })).toEqual({ test: "test", another: 12.548777 });
).toEqual({test: "test", another: "12.548777"}); expect(testProperty.type.serializeDiff({ test: "test", another: 12.548777 })).toEqual({ test: "test", another: "12.548777" });
expect(
testProperty.type.deserialize({test: "test", another: "12.548777"}),
).toEqual({test: "test", another: 12.548777});
expect(
testProperty.type.serializeDiff({test: "test", another: 12.548777}),
).toEqual({test: "test", another: "12.548777"});
expect(testProperty.type.serialize(null)).toEqual(null); expect(testProperty.type.serialize(null)).toEqual(null);
expect(testProperty.type.deserialize(null)).toEqual(null); expect(testProperty.type.deserialize(null)).toEqual(null);
@ -50,105 +36,41 @@ describe("object type", () => {
expect(testProperty.type.deserialize(undefined)).toEqual(undefined); expect(testProperty.type.deserialize(undefined)).toEqual(undefined);
expect(testProperty.type.serializeDiff(undefined)).toEqual(undefined); expect(testProperty.type.serializeDiff(undefined)).toEqual(undefined);
expect( expect(testProperty.type.hasChanged({ test: "test", another: 12.548777 }, { another: 12.548777, test: "test" })).toBeFalsy();
testProperty.type.hasChanged( expect(testProperty.type.hasChanged({ test: "test", another: 12.548777 }, { test: "test", another: 12.548778 })).toBeTruthy();
{test: "test", another: 12.548777},
{another: 12.548777, test: "test"},
),
).toBeFalsy();
expect(
testProperty.type.hasChanged(
{test: "test", another: 12.548777},
{test: "test", another: 12.548778},
),
).toBeTruthy();
expect(testProperty.type.hasChanged(null, null)).toBeFalsy(); expect(testProperty.type.hasChanged(null, null)).toBeFalsy();
expect(testProperty.type.hasChanged(undefined, undefined)).toBeFalsy(); expect(testProperty.type.hasChanged(undefined, undefined)).toBeFalsy();
expect(testProperty.type.hasChanged(null, undefined)).toBeTruthy(); expect(testProperty.type.hasChanged(null, undefined)).toBeTruthy();
expect(testProperty.type.hasChanged(undefined, null)).toBeTruthy(); expect(testProperty.type.hasChanged(undefined, null)).toBeTruthy();
expect( expect(testProperty.type.hasChanged(null, { test: "test", another: 12.548777 })).toBeTruthy();
testProperty.type.hasChanged(null, {test: "test", another: 12.548777}), expect(testProperty.type.hasChanged(undefined, { test: "test", another: 12.548777 })).toBeTruthy();
).toBeTruthy(); expect(testProperty.type.hasChanged({ test: "test", another: 12.548777 }, null)).toBeTruthy();
expect( expect(testProperty.type.hasChanged({ test: "test", another: 12.548777 }, undefined)).toBeTruthy();
testProperty.type.hasChanged(undefined, {
test: "test",
another: 12.548777,
}),
).toBeTruthy();
expect(
testProperty.type.hasChanged({test: "test", another: 12.548777}, null),
).toBeTruthy();
expect(
testProperty.type.hasChanged(
{test: "test", another: 12.548777},
undefined,
),
).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged({ test: "test", another: "12.548777" }, { another: "12.548777", test: "test" })).toBeFalsy();
testProperty.type.serializedHasChanged( expect(testProperty.type.serializedHasChanged({ test: "test", another: "12.548777" }, { test: "test", another: "12.548778" })).toBeTruthy();
{test: "test", another: "12.548777"},
{another: "12.548777", test: "test"},
),
).toBeFalsy();
expect(
testProperty.type.serializedHasChanged(
{test: "test", another: "12.548777"},
{test: "test", another: "12.548778"},
),
).toBeTruthy();
expect(testProperty.type.serializedHasChanged(null, null)).toBeFalsy(); expect(testProperty.type.serializedHasChanged(null, null)).toBeFalsy();
expect( expect(testProperty.type.serializedHasChanged(undefined, undefined)).toBeFalsy();
testProperty.type.serializedHasChanged(undefined, undefined), expect(testProperty.type.serializedHasChanged(null, undefined)).toBeTruthy();
).toBeFalsy(); expect(testProperty.type.serializedHasChanged(undefined, null)).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged(null, { test: "test", another: "12.548777" })).toBeTruthy();
testProperty.type.serializedHasChanged(null, undefined), expect(testProperty.type.serializedHasChanged(undefined, { test: "test", another: "12.548777" })).toBeTruthy();
).toBeTruthy(); expect(testProperty.type.serializedHasChanged({ test: "test", another: "12.548777" }, null)).toBeTruthy();
expect( expect(testProperty.type.serializedHasChanged({ test: "test", another: "12.548777" }, undefined)).toBeTruthy();
testProperty.type.serializedHasChanged(undefined, null),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(null, {
test: "test",
another: "12.548777",
}),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(undefined, {
test: "test",
another: "12.548777",
}),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
{test: "test", another: "12.548777"},
null,
),
).toBeTruthy();
expect(
testProperty.type.serializedHasChanged(
{test: "test", another: "12.548777"},
undefined,
),
).toBeTruthy();
testProperty.type.resetDiff({test: "test", another: 12.548777}); testProperty.type.resetDiff({ test: "test", another: 12.548777 });
testProperty.type.resetDiff(undefined); testProperty.type.resetDiff(undefined);
testProperty.type.resetDiff(null); testProperty.type.resetDiff(null);
{ { // Test that values are cloned in a different object.
// Test that values are cloned in a different object. const propertyValue = { test: "test", another: 12.548777 };
const propertyValue = {test: "test", another: 12.548777};
const clonedPropertyValue = testProperty.type.clone(propertyValue); const clonedPropertyValue = testProperty.type.clone(propertyValue);
expect(clonedPropertyValue).not.toBe(propertyValue); expect(clonedPropertyValue).not.toBe(propertyValue);
expect(clonedPropertyValue).toEqual(propertyValue); expect(clonedPropertyValue).toEqual(propertyValue);
} }
{ { // Test that values are cloned in a different object.
// Test that values are cloned in a different object. const propertyValue = { arr: [12, 11] };
const propertyValue = {arr: [12, 11]}; const clonedPropertyValue = s.property.object({ arr: s.property.array(s.property.numeric()) }).type.clone(propertyValue);
const clonedPropertyValue = s.property
.object({arr: s.property.array(s.property.numeric())})
.type.clone(propertyValue);
expect(clonedPropertyValue).not.toBe(propertyValue); expect(clonedPropertyValue).not.toBe(propertyValue);
expect(clonedPropertyValue).toEqual(propertyValue); expect(clonedPropertyValue).toEqual(propertyValue);
expect(clonedPropertyValue.arr).not.toBe(propertyValue.arr); expect(clonedPropertyValue.arr).not.toBe(propertyValue.arr);
@ -157,41 +79,30 @@ describe("object type", () => {
expect(testProperty.type.clone(undefined)).toBe(undefined); expect(testProperty.type.clone(undefined)).toBe(undefined);
expect(testProperty.type.clone(null)).toBe(null); expect(testProperty.type.clone(null)).toBe(null);
{ { // Apply a patch with undefined / NULL values.
// Apply a patch with undefined / NULL values. expect(testProperty.type.applyPatch(
expect( { test: "test", another: 12.548777 },
testProperty.type.applyPatch( undefined,
{test: "test", another: 12.548777}, false
undefined, )).toBeUndefined();
false, expect(testProperty.type.applyPatch(
), { test: "test", another: 12.548777 },
).toBeUndefined(); null,
expect( true
testProperty.type.applyPatch( )).toBeNull();
{test: "test", another: 12.548777},
null,
true,
),
).toBeNull();
} }
{ { // Invalid patch.
// Invalid patch. expect(
expect(() => () => testProperty.type.applyPatch({ test: "test", another: 12.548777 }, 5416 as any, false)
testProperty.type.applyPatch(
{test: "test", another: 12.548777},
5416 as any,
false,
),
).toThrow(InvalidTypeValueError); ).toThrow(InvalidTypeValueError);
} }
{ { // Apply a patch.
// Apply a patch.
{ {
const objectInstance = testProperty.type.applyPatch( const objectInstance = testProperty.type.applyPatch(
{test: "test", another: 12.548777}, { test: "test", another: 12.548777 },
{test: "another"}, { test: "another" },
true, true,
); );
@ -204,8 +115,8 @@ describe("object type", () => {
{ {
const objectInstance = testProperty.type.applyPatch( const objectInstance = testProperty.type.applyPatch(
undefined, undefined,
{test: "test"}, { test: "test" },
false, false
); );
expect(objectInstance).toStrictEqual({ expect(objectInstance).toStrictEqual({
@ -216,8 +127,8 @@ describe("object type", () => {
{ {
const objectInstance = testProperty.type.applyPatch( const objectInstance = testProperty.type.applyPatch(
null, null,
{test: "test"}, { test: "test" },
false, false
); );
expect(objectInstance).toStrictEqual({ expect(objectInstance).toStrictEqual({
@ -228,48 +139,20 @@ describe("object type", () => {
}); });
test("invalid parameters types", () => { test("invalid parameters types", () => {
expect(() => testProperty.type.serialize(5 as any)).toThrowError( expect(() => testProperty.type.serialize(5 as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(() => testProperty.type.deserialize(5 as any)).toThrowError(InvalidTypeValueError);
); expect(() => testProperty.type.serializeDiff(5 as any)).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.deserialize(5 as any)).toThrowError( expect(() => testProperty.type.resetDiff(5 as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(() => testProperty.type.hasChanged(5 as any, 5 as any)).toThrowError(InvalidTypeValueError);
); expect(() => testProperty.type.serializedHasChanged(5 as any, 5 as any)).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.serializeDiff(5 as any)).toThrowError( expect(() => testProperty.type.clone(5 as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError,
);
expect(() => testProperty.type.resetDiff(5 as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => testProperty.type.hasChanged(5 as any, 5 as any)).toThrowError(
InvalidTypeValueError,
);
expect(() =>
testProperty.type.serializedHasChanged(5 as any, 5 as any),
).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.clone(5 as any)).toThrowError(
InvalidTypeValueError,
);
expect(() => testProperty.type.serialize([] as any)).toThrowError( expect(() => testProperty.type.serialize([] as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(() => testProperty.type.deserialize([] as any)).toThrowError(InvalidTypeValueError);
); expect(() => testProperty.type.serializeDiff([] as any)).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.deserialize([] as any)).toThrowError( expect(() => testProperty.type.resetDiff([] as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError, expect(() => testProperty.type.hasChanged({} as any, [] as any)).toThrowError(InvalidTypeValueError);
); expect(() => testProperty.type.serializedHasChanged({} as any, [] as any)).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.serializeDiff([] as any)).toThrowError( expect(() => testProperty.type.clone([] as any)).toThrowError(InvalidTypeValueError);
InvalidTypeValueError,
);
expect(() => testProperty.type.resetDiff([] as any)).toThrowError(
InvalidTypeValueError,
);
expect(() =>
testProperty.type.hasChanged({} as any, [] as any),
).toThrowError(InvalidTypeValueError);
expect(() =>
testProperty.type.serializedHasChanged({} as any, [] as any),
).toThrowError(InvalidTypeValueError);
expect(() => testProperty.type.clone([] as any)).toThrowError(
InvalidTypeValueError,
);
}); });
}); });

View file

@ -22,9 +22,7 @@ describe("string type", () => {
expect(s.property.string().type.hasChanged("test", "test")).toBeFalsy(); expect(s.property.string().type.hasChanged("test", "test")).toBeFalsy();
expect(s.property.string().type.hasChanged(null, null)).toBeFalsy(); expect(s.property.string().type.hasChanged(null, null)).toBeFalsy();
expect( expect(s.property.string().type.hasChanged(undefined, undefined)).toBeFalsy();
s.property.string().type.hasChanged(undefined, undefined),
).toBeFalsy();
expect(s.property.string().type.hasChanged(null, undefined)).toBeTruthy(); expect(s.property.string().type.hasChanged(null, undefined)).toBeTruthy();
expect(s.property.string().type.hasChanged(undefined, null)).toBeTruthy(); expect(s.property.string().type.hasChanged(undefined, null)).toBeTruthy();
expect(s.property.string().type.hasChanged(null, "test")).toBeTruthy(); expect(s.property.string().type.hasChanged(null, "test")).toBeTruthy();
@ -32,82 +30,40 @@ describe("string type", () => {
expect(s.property.string().type.hasChanged("test", null)).toBeTruthy(); expect(s.property.string().type.hasChanged("test", null)).toBeTruthy();
expect(s.property.string().type.hasChanged("test", undefined)).toBeTruthy(); expect(s.property.string().type.hasChanged("test", undefined)).toBeTruthy();
expect( expect(s.property.string().type.serializedHasChanged("test", "test")).toBeFalsy();
s.property.string().type.serializedHasChanged("test", "test"), expect(s.property.string().type.serializedHasChanged(null, null)).toBeFalsy();
).toBeFalsy(); expect(s.property.string().type.serializedHasChanged(undefined, undefined)).toBeFalsy();
expect( expect(s.property.string().type.serializedHasChanged(null, undefined)).toBeTruthy();
s.property.string().type.serializedHasChanged(null, null), expect(s.property.string().type.serializedHasChanged(undefined, null)).toBeTruthy();
).toBeFalsy(); expect(s.property.string().type.serializedHasChanged(null, "test")).toBeTruthy();
expect( expect(s.property.string().type.serializedHasChanged(undefined, "test")).toBeTruthy();
s.property.string().type.serializedHasChanged(undefined, undefined), expect(s.property.string().type.serializedHasChanged("test", null)).toBeTruthy();
).toBeFalsy(); expect(s.property.string().type.serializedHasChanged("test", undefined)).toBeTruthy();
expect(
s.property.string().type.serializedHasChanged(null, undefined),
).toBeTruthy();
expect(
s.property.string().type.serializedHasChanged(undefined, null),
).toBeTruthy();
expect(
s.property.string().type.serializedHasChanged(null, "test"),
).toBeTruthy();
expect(
s.property.string().type.serializedHasChanged(undefined, "test"),
).toBeTruthy();
expect(
s.property.string().type.serializedHasChanged("test", null),
).toBeTruthy();
expect(
s.property.string().type.serializedHasChanged("test", undefined),
).toBeTruthy();
s.property.string().type.resetDiff("test"); s.property.string().type.resetDiff("test");
s.property.string().type.resetDiff(undefined); s.property.string().type.resetDiff(undefined);
s.property.string().type.resetDiff(null); s.property.string().type.resetDiff(null);
expect(s.property.string().type.applyPatch("another", "test", false)).toBe( expect(s.property.string().type.applyPatch("another", "test", false)).toBe("test");
"test", expect(s.property.string().type.applyPatch(undefined, "test", true)).toBe("test");
); expect(s.property.string().type.applyPatch(null, "test", false)).toBe("test");
expect(s.property.string().type.applyPatch(undefined, "test", true)).toBe( expect(s.property.string().type.applyPatch("test", undefined, false)).toBeUndefined();
"test",
);
expect(s.property.string().type.applyPatch(null, "test", false)).toBe(
"test",
);
expect(
s.property.string().type.applyPatch("test", undefined, false),
).toBeUndefined();
expect(s.property.string().type.applyPatch("test", null, false)).toBeNull(); expect(s.property.string().type.applyPatch("test", null, false)).toBeNull();
}); });
test("invalid parameters types", () => { test("invalid parameters types", () => {
const testDate = new Date(); const testDate = new Date();
expect(s.property.string().type.serialize({} as any)).toBe( expect(s.property.string().type.serialize({} as any)).toBe("[object Object]");
"[object Object]",
);
expect(s.property.string().type.serialize(2120 as any)).toBe("2120"); expect(s.property.string().type.serialize(2120 as any)).toBe("2120");
expect(s.property.string().type.serialize(testDate as any)).toBe( expect(s.property.string().type.serialize(testDate as any)).toBe(testDate.toString());
testDate.toString(), expect(s.property.string().type.deserialize({} as any)).toBe("[object Object]");
);
expect(s.property.string().type.deserialize({} as any)).toBe(
"[object Object]",
);
expect(s.property.string().type.deserialize(2120 as any)).toBe("2120"); expect(s.property.string().type.deserialize(2120 as any)).toBe("2120");
expect(s.property.string().type.serializeDiff({} as any)).toBe( expect(s.property.string().type.serializeDiff({} as any)).toBe("[object Object]");
"[object Object]",
);
expect(s.property.string().type.serializeDiff(2120 as any)).toBe("2120"); expect(s.property.string().type.serializeDiff(2120 as any)).toBe("2120");
expect( expect(s.property.string().type.hasChanged({} as any, {} as any)).toBeTruthy();
s.property.string().type.hasChanged({} as any, {} as any), expect(s.property.string().type.hasChanged(false as any, false as any)).toBeFalsy();
).toBeTruthy(); expect(s.property.string().type.serializedHasChanged({} as any, {} as any)).toBeTruthy();
expect( expect(s.property.string().type.serializedHasChanged(false as any, false as any)).toBeFalsy();
s.property.string().type.hasChanged(false as any, false as any),
).toBeFalsy();
expect(
s.property.string().type.serializedHasChanged({} as any, {} as any),
).toBeTruthy();
expect(
s.property.string().type.serializedHasChanged(false as any, false as any),
).toBeFalsy();
expect(s.property.string().type.clone({} as any)).toStrictEqual({}); expect(s.property.string().type.clone({} as any)).toStrictEqual({});
}); });
}); });

View file

@ -1,27 +1,30 @@
{ {
"ts-node": { "ts-node": {
"compilerOptions": { "compilerOptions": {
"module": "ESNext", "module": "ESNext",
"types": ["node"] "types": ["node"],
} }
}, },
"compilerOptions": { "compilerOptions": {
"outDir": "./lib/", "outDir": "./lib/",
"incremental": true, "incremental": true,
"sourceMap": true, "sourceMap": true,
"noImplicitAny": true, "noImplicitAny": true,
"noImplicitThis": true, "noImplicitThis": true,
"esModuleInterop": true, "esModuleInterop": true,
"allowSyntheticDefaultImports": true, "allowSyntheticDefaultImports": true,
"resolveJsonModule": true, "resolveJsonModule": true,
"experimentalDecorators": true, "experimentalDecorators": true,
"emitDecoratorMetadata": true, "emitDecoratorMetadata": true,
"declaration": true, "declaration": true,
"declarationMap": true, "declarationMap": true,
"module": "ES6", "module": "ES6",
"target": "ES6", "target": "ES6",
"moduleResolution": "Bundler", "moduleResolution": "Bundler",
"lib": ["ESNext", "DOM"] "lib": [
} "ESNext",
"DOM"
]
}
} }

View file

@ -3,8 +3,8 @@ import dts from "vite-plugin-dts";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig(({mode}: ConfigEnv): UserConfig => { export default defineConfig(({ mode }: ConfigEnv): UserConfig => {
return { return ({
build: { build: {
outDir: "lib", outDir: "lib",
sourcemap: true, sourcemap: true,
@ -22,6 +22,6 @@ export default defineConfig(({mode}: ConfigEnv): UserConfig => {
rollupTypes: true, rollupTypes: true,
exclude: ["node_modules"], exclude: ["node_modules"],
}), }),
], ]
}; });
}); });

610
yarn.lock
View file

@ -361,7 +361,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24, @jridgewell/trace-mapping@npm:^0.3.25": "@jridgewell/trace-mapping@npm:^0.3.23, @jridgewell/trace-mapping@npm:^0.3.24":
version: 0.3.25 version: 0.3.25
resolution: "@jridgewell/trace-mapping@npm:0.3.25" resolution: "@jridgewell/trace-mapping@npm:0.3.25"
dependencies: dependencies:
@ -467,142 +467,135 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-android-arm-eabi@npm:4.44.0": "@rollup/rollup-android-arm-eabi@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-android-arm-eabi@npm:4.44.0" resolution: "@rollup/rollup-android-arm-eabi@npm:4.36.0"
conditions: os=android & cpu=arm conditions: os=android & cpu=arm
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-android-arm64@npm:4.44.0": "@rollup/rollup-android-arm64@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-android-arm64@npm:4.44.0" resolution: "@rollup/rollup-android-arm64@npm:4.36.0"
conditions: os=android & cpu=arm64 conditions: os=android & cpu=arm64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-darwin-arm64@npm:4.44.0": "@rollup/rollup-darwin-arm64@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-darwin-arm64@npm:4.44.0" resolution: "@rollup/rollup-darwin-arm64@npm:4.36.0"
conditions: os=darwin & cpu=arm64 conditions: os=darwin & cpu=arm64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-darwin-x64@npm:4.44.0": "@rollup/rollup-darwin-x64@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-darwin-x64@npm:4.44.0" resolution: "@rollup/rollup-darwin-x64@npm:4.36.0"
conditions: os=darwin & cpu=x64 conditions: os=darwin & cpu=x64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-freebsd-arm64@npm:4.44.0": "@rollup/rollup-freebsd-arm64@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-freebsd-arm64@npm:4.44.0" resolution: "@rollup/rollup-freebsd-arm64@npm:4.36.0"
conditions: os=freebsd & cpu=arm64 conditions: os=freebsd & cpu=arm64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-freebsd-x64@npm:4.44.0": "@rollup/rollup-freebsd-x64@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-freebsd-x64@npm:4.44.0" resolution: "@rollup/rollup-freebsd-x64@npm:4.36.0"
conditions: os=freebsd & cpu=x64 conditions: os=freebsd & cpu=x64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-arm-gnueabihf@npm:4.44.0": "@rollup/rollup-linux-arm-gnueabihf@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.44.0" resolution: "@rollup/rollup-linux-arm-gnueabihf@npm:4.36.0"
conditions: os=linux & cpu=arm & libc=glibc conditions: os=linux & cpu=arm & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-arm-musleabihf@npm:4.44.0": "@rollup/rollup-linux-arm-musleabihf@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.44.0" resolution: "@rollup/rollup-linux-arm-musleabihf@npm:4.36.0"
conditions: os=linux & cpu=arm & libc=musl conditions: os=linux & cpu=arm & libc=musl
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-arm64-gnu@npm:4.44.0": "@rollup/rollup-linux-arm64-gnu@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.44.0" resolution: "@rollup/rollup-linux-arm64-gnu@npm:4.36.0"
conditions: os=linux & cpu=arm64 & libc=glibc conditions: os=linux & cpu=arm64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-arm64-musl@npm:4.44.0": "@rollup/rollup-linux-arm64-musl@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-arm64-musl@npm:4.44.0" resolution: "@rollup/rollup-linux-arm64-musl@npm:4.36.0"
conditions: os=linux & cpu=arm64 & libc=musl conditions: os=linux & cpu=arm64 & libc=musl
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-loongarch64-gnu@npm:4.44.0": "@rollup/rollup-linux-loongarch64-gnu@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.44.0" resolution: "@rollup/rollup-linux-loongarch64-gnu@npm:4.36.0"
conditions: os=linux & cpu=loong64 & libc=glibc conditions: os=linux & cpu=loong64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-powerpc64le-gnu@npm:4.44.0": "@rollup/rollup-linux-powerpc64le-gnu@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.44.0" resolution: "@rollup/rollup-linux-powerpc64le-gnu@npm:4.36.0"
conditions: os=linux & cpu=ppc64 & libc=glibc conditions: os=linux & cpu=ppc64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-riscv64-gnu@npm:4.44.0": "@rollup/rollup-linux-riscv64-gnu@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.44.0" resolution: "@rollup/rollup-linux-riscv64-gnu@npm:4.36.0"
conditions: os=linux & cpu=riscv64 & libc=glibc conditions: os=linux & cpu=riscv64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-riscv64-musl@npm:4.44.0": "@rollup/rollup-linux-s390x-gnu@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-riscv64-musl@npm:4.44.0" resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.36.0"
conditions: os=linux & cpu=riscv64 & libc=musl
languageName: node
linkType: hard
"@rollup/rollup-linux-s390x-gnu@npm:4.44.0":
version: 4.44.0
resolution: "@rollup/rollup-linux-s390x-gnu@npm:4.44.0"
conditions: os=linux & cpu=s390x & libc=glibc conditions: os=linux & cpu=s390x & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-x64-gnu@npm:4.44.0": "@rollup/rollup-linux-x64-gnu@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-x64-gnu@npm:4.44.0" resolution: "@rollup/rollup-linux-x64-gnu@npm:4.36.0"
conditions: os=linux & cpu=x64 & libc=glibc conditions: os=linux & cpu=x64 & libc=glibc
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-linux-x64-musl@npm:4.44.0": "@rollup/rollup-linux-x64-musl@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-linux-x64-musl@npm:4.44.0" resolution: "@rollup/rollup-linux-x64-musl@npm:4.36.0"
conditions: os=linux & cpu=x64 & libc=musl conditions: os=linux & cpu=x64 & libc=musl
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-win32-arm64-msvc@npm:4.44.0": "@rollup/rollup-win32-arm64-msvc@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.44.0" resolution: "@rollup/rollup-win32-arm64-msvc@npm:4.36.0"
conditions: os=win32 & cpu=arm64 conditions: os=win32 & cpu=arm64
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-win32-ia32-msvc@npm:4.44.0": "@rollup/rollup-win32-ia32-msvc@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.44.0" resolution: "@rollup/rollup-win32-ia32-msvc@npm:4.36.0"
conditions: os=win32 & cpu=ia32 conditions: os=win32 & cpu=ia32
languageName: node languageName: node
linkType: hard linkType: hard
"@rollup/rollup-win32-x64-msvc@npm:4.44.0": "@rollup/rollup-win32-x64-msvc@npm:4.36.0":
version: 4.44.0 version: 4.36.0
resolution: "@rollup/rollup-win32-x64-msvc@npm:4.44.0" resolution: "@rollup/rollup-win32-x64-msvc@npm:4.36.0"
conditions: os=win32 & cpu=x64 conditions: os=win32 & cpu=x64
languageName: node languageName: node
linkType: hard linkType: hard
@ -669,14 +662,13 @@ __metadata:
version: 0.0.0-use.local version: 0.0.0-use.local
resolution: "@sharkitek/core@workspace:." resolution: "@sharkitek/core@workspace:."
dependencies: dependencies:
"@types/node": "npm:^24.0.3" "@types/node": "npm:^22.13.14"
"@vitest/coverage-v8": "npm:^3.2.4" "@vitest/coverage-v8": "npm:^3.0.9"
prettier: "npm:^3.6.0"
ts-node: "npm:^10.9.2" ts-node: "npm:^10.9.2"
typescript: "npm:^5.8.3" typescript: "npm:^5.8.2"
vite: "npm:^6.3.5" vite: "npm:^6.2.3"
vite-plugin-dts: "npm:^4.5.4" vite-plugin-dts: "npm:^4.5.3"
vitest: "npm:^3.2.4" vitest: "npm:^3.0.9"
languageName: unknown languageName: unknown
linkType: soft linkType: soft
@ -722,152 +714,126 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@types/chai@npm:^5.2.2": "@types/estree@npm:1.0.6, @types/estree@npm:^1.0.0":
version: 5.2.2
resolution: "@types/chai@npm:5.2.2"
dependencies:
"@types/deep-eql": "npm:*"
checksum: 10c0/49282bf0e8246800ebb36f17256f97bd3a8c4fb31f92ad3c0eaa7623518d7e87f1eaad4ad206960fcaf7175854bdff4cb167e4fe96811e0081b4ada83dd533ec
languageName: node
linkType: hard
"@types/deep-eql@npm:*":
version: 4.0.2
resolution: "@types/deep-eql@npm:4.0.2"
checksum: 10c0/bf3f811843117900d7084b9d0c852da9a044d12eb40e6de73b552598a6843c21291a8a381b0532644574beecd5e3491c5ff3a0365ab86b15d59862c025384844
languageName: node
linkType: hard
"@types/estree@npm:1.0.8":
version: 1.0.8
resolution: "@types/estree@npm:1.0.8"
checksum: 10c0/39d34d1afaa338ab9763f37ad6066e3f349444f9052b9676a7cc0252ef9485a41c6d81c9c4e0d26e9077993354edf25efc853f3224dd4b447175ef62bdcc86a5
languageName: node
linkType: hard
"@types/estree@npm:^1.0.0":
version: 1.0.6 version: 1.0.6
resolution: "@types/estree@npm:1.0.6" resolution: "@types/estree@npm:1.0.6"
checksum: 10c0/cdfd751f6f9065442cd40957c07fd80361c962869aa853c1c2fd03e101af8b9389d8ff4955a43a6fcfa223dd387a089937f95be0f3eec21ca527039fd2d9859a checksum: 10c0/cdfd751f6f9065442cd40957c07fd80361c962869aa853c1c2fd03e101af8b9389d8ff4955a43a6fcfa223dd387a089937f95be0f3eec21ca527039fd2d9859a
languageName: node languageName: node
linkType: hard linkType: hard
"@types/node@npm:^24.0.3": "@types/node@npm:^22.13.14":
version: 24.0.3 version: 22.13.14
resolution: "@types/node@npm:24.0.3" resolution: "@types/node@npm:22.13.14"
dependencies: dependencies:
undici-types: "npm:~7.8.0" undici-types: "npm:~6.20.0"
checksum: 10c0/9c3c4e87600d1cf11e291c2fd4bfd806a615455463c30a0ef6dc9c801b3423344d9b82b8084e3ccabce485a7421ebb61a66e9676181bd7d9aea4759998a120d5 checksum: 10c0/fa2ab5b8277bfbcc86c42e46a3ea9871b0d559894cc9d955685d17178c9499f0b1bf03d1d1ea8d92ef2dda818988f4035acb8abf9dc15423a998fa56173ab804
languageName: node languageName: node
linkType: hard linkType: hard
"@vitest/coverage-v8@npm:^3.2.4": "@vitest/coverage-v8@npm:^3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "@vitest/coverage-v8@npm:3.2.4" resolution: "@vitest/coverage-v8@npm:3.0.9"
dependencies: dependencies:
"@ampproject/remapping": "npm:^2.3.0" "@ampproject/remapping": "npm:^2.3.0"
"@bcoe/v8-coverage": "npm:^1.0.2" "@bcoe/v8-coverage": "npm:^1.0.2"
ast-v8-to-istanbul: "npm:^0.3.3" debug: "npm:^4.4.0"
debug: "npm:^4.4.1"
istanbul-lib-coverage: "npm:^3.2.2" istanbul-lib-coverage: "npm:^3.2.2"
istanbul-lib-report: "npm:^3.0.1" istanbul-lib-report: "npm:^3.0.1"
istanbul-lib-source-maps: "npm:^5.0.6" istanbul-lib-source-maps: "npm:^5.0.6"
istanbul-reports: "npm:^3.1.7" istanbul-reports: "npm:^3.1.7"
magic-string: "npm:^0.30.17" magic-string: "npm:^0.30.17"
magicast: "npm:^0.3.5" magicast: "npm:^0.3.5"
std-env: "npm:^3.9.0" std-env: "npm:^3.8.0"
test-exclude: "npm:^7.0.1" test-exclude: "npm:^7.0.1"
tinyrainbow: "npm:^2.0.0" tinyrainbow: "npm:^2.0.0"
peerDependencies: peerDependencies:
"@vitest/browser": 3.2.4 "@vitest/browser": 3.0.9
vitest: 3.2.4 vitest: 3.0.9
peerDependenciesMeta: peerDependenciesMeta:
"@vitest/browser": "@vitest/browser":
optional: true optional: true
checksum: 10c0/cae3e58d81d56e7e1cdecd7b5baab7edd0ad9dee8dec9353c52796e390e452377d3f04174d40b6986b17c73241a5e773e422931eaa8102dcba0605ff24b25193 checksum: 10c0/90925acdb783a7293f58cc902e53f46c230c581c75cb6ae4f0e093286039f99097361dde9ecbfd8d887e631797fd31ca32b715b346265dec8462f1d61a11db96
languageName: node languageName: node
linkType: hard linkType: hard
"@vitest/expect@npm:3.2.4": "@vitest/expect@npm:3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "@vitest/expect@npm:3.2.4" resolution: "@vitest/expect@npm:3.0.9"
dependencies: dependencies:
"@types/chai": "npm:^5.2.2" "@vitest/spy": "npm:3.0.9"
"@vitest/spy": "npm:3.2.4" "@vitest/utils": "npm:3.0.9"
"@vitest/utils": "npm:3.2.4"
chai: "npm:^5.2.0" chai: "npm:^5.2.0"
tinyrainbow: "npm:^2.0.0" tinyrainbow: "npm:^2.0.0"
checksum: 10c0/7586104e3fd31dbe1e6ecaafb9a70131e4197dce2940f727b6a84131eee3decac7b10f9c7c72fa5edbdb68b6f854353bd4c0fa84779e274207fb7379563b10db checksum: 10c0/4e5eef8fbc9c3e47f3fb69dbbd5b51aabdf1b6de2f781556d37d79731678fc83cf4a01d146226b12a27df051a4110153a6172506c9c74ae08e5b924a9c947f08
languageName: node languageName: node
linkType: hard linkType: hard
"@vitest/mocker@npm:3.2.4": "@vitest/mocker@npm:3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "@vitest/mocker@npm:3.2.4" resolution: "@vitest/mocker@npm:3.0.9"
dependencies: dependencies:
"@vitest/spy": "npm:3.2.4" "@vitest/spy": "npm:3.0.9"
estree-walker: "npm:^3.0.3" estree-walker: "npm:^3.0.3"
magic-string: "npm:^0.30.17" magic-string: "npm:^0.30.17"
peerDependencies: peerDependencies:
msw: ^2.4.9 msw: ^2.4.9
vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 vite: ^5.0.0 || ^6.0.0
peerDependenciesMeta: peerDependenciesMeta:
msw: msw:
optional: true optional: true
vite: vite:
optional: true optional: true
checksum: 10c0/f7a4aea19bbbf8f15905847ee9143b6298b2c110f8b64789224cb0ffdc2e96f9802876aa2ca83f1ec1b6e1ff45e822abb34f0054c24d57b29ab18add06536ccd checksum: 10c0/9083a83902ca550cf004413b9fc87c8367a789e18a3c5a61e63c72810f9153e7d1c100c66f0b0656ea1035a700a373d5b78b49de0963ab62333c720aeec9f1b3
languageName: node languageName: node
linkType: hard linkType: hard
"@vitest/pretty-format@npm:3.2.4, @vitest/pretty-format@npm:^3.2.4": "@vitest/pretty-format@npm:3.0.9, @vitest/pretty-format@npm:^3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "@vitest/pretty-format@npm:3.2.4" resolution: "@vitest/pretty-format@npm:3.0.9"
dependencies: dependencies:
tinyrainbow: "npm:^2.0.0" tinyrainbow: "npm:^2.0.0"
checksum: 10c0/5ad7d4278e067390d7d633e307fee8103958806a419ca380aec0e33fae71b44a64415f7a9b4bc11635d3c13d4a9186111c581d3cef9c65cc317e68f077456887 checksum: 10c0/56ae7b1f14df2905b3205d4e121727631c4938ec44f76c1e9fa49923919010378f0dad70b1d277672f3ef45ddf6372140c8d1da95e45df8282f70b74328fce47
languageName: node languageName: node
linkType: hard linkType: hard
"@vitest/runner@npm:3.2.4": "@vitest/runner@npm:3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "@vitest/runner@npm:3.2.4" resolution: "@vitest/runner@npm:3.0.9"
dependencies: dependencies:
"@vitest/utils": "npm:3.2.4" "@vitest/utils": "npm:3.0.9"
pathe: "npm:^2.0.3" pathe: "npm:^2.0.3"
strip-literal: "npm:^3.0.0" checksum: 10c0/b276f238a16a6d02bb244f655d9cd8db8cce4708a6267cc48476a785ca8887741c440ae27b379a5bbbb6fe4f9f12675f13da0270253043195defd7a36bf15114
checksum: 10c0/e8be51666c72b3668ae3ea348b0196656a4a5adb836cb5e270720885d9517421815b0d6c98bfdf1795ed02b994b7bfb2b21566ee356a40021f5bf4f6ed4e418a
languageName: node languageName: node
linkType: hard linkType: hard
"@vitest/snapshot@npm:3.2.4": "@vitest/snapshot@npm:3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "@vitest/snapshot@npm:3.2.4" resolution: "@vitest/snapshot@npm:3.0.9"
dependencies: dependencies:
"@vitest/pretty-format": "npm:3.2.4" "@vitest/pretty-format": "npm:3.0.9"
magic-string: "npm:^0.30.17" magic-string: "npm:^0.30.17"
pathe: "npm:^2.0.3" pathe: "npm:^2.0.3"
checksum: 10c0/f8301a3d7d1559fd3d59ed51176dd52e1ed5c2d23aa6d8d6aa18787ef46e295056bc726a021698d8454c16ed825ecba163362f42fa90258bb4a98cfd2c9424fc checksum: 10c0/8298caa334d357cb22b1946cbebedb22f04d38fe080d6da7445873221fe6f89c2b82fe4f368d9eb8a62a77bd76d1b4234595bb085279d48130f09ba6b2e18637
languageName: node languageName: node
linkType: hard linkType: hard
"@vitest/spy@npm:3.2.4": "@vitest/spy@npm:3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "@vitest/spy@npm:3.2.4" resolution: "@vitest/spy@npm:3.0.9"
dependencies: dependencies:
tinyspy: "npm:^4.0.3" tinyspy: "npm:^3.0.2"
checksum: 10c0/6ebf0b4697dc238476d6b6a60c76ba9eb1dd8167a307e30f08f64149612fd50227682b876420e4c2e09a76334e73f72e3ebf0e350714dc22474258292e202024 checksum: 10c0/993085dbaf9e651ca9516f88e440424d29279def998186628a1ebcab5558a3045fee8562630608f58303507135f6f3bf9970f65639f3b9baa8bf86cab3eb4742
languageName: node languageName: node
linkType: hard linkType: hard
"@vitest/utils@npm:3.2.4": "@vitest/utils@npm:3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "@vitest/utils@npm:3.2.4" resolution: "@vitest/utils@npm:3.0.9"
dependencies: dependencies:
"@vitest/pretty-format": "npm:3.2.4" "@vitest/pretty-format": "npm:3.0.9"
loupe: "npm:^3.1.4" loupe: "npm:^3.1.3"
tinyrainbow: "npm:^2.0.0" tinyrainbow: "npm:^2.0.0"
checksum: 10c0/024a9b8c8bcc12cf40183c246c244b52ecff861c6deb3477cbf487ac8781ad44c68a9c5fd69f8c1361878e55b97c10d99d511f2597f1f7244b5e5101d028ba64 checksum: 10c0/b966dfb3b926ee9bea59c1fb297abc67adaa23a8a582453ee81167b238446394693617a5e0523eb2791d6983173ef1c07bf28a76bd5a63b49a100610ed6b6a6c
languageName: node languageName: node
linkType: hard linkType: hard
@ -1160,17 +1126,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"ast-v8-to-istanbul@npm:^0.3.3":
version: 0.3.3
resolution: "ast-v8-to-istanbul@npm:0.3.3"
dependencies:
"@jridgewell/trace-mapping": "npm:^0.3.25"
estree-walker: "npm:^3.0.3"
js-tokens: "npm:^9.0.1"
checksum: 10c0/ffc39bc3ab4b8c1f7aea945960ce6b1e518bab3da7c800277eab2da07d397eeae4a2cb8a5a5f817225646c8ea495c1e4434fbe082c84bae8042abddef53f50b2
languageName: node
linkType: hard
"balanced-match@npm:^1.0.0": "balanced-match@npm:^1.0.0":
version: 1.0.2 version: 1.0.2
resolution: "balanced-match@npm:1.0.2" resolution: "balanced-match@npm:1.0.2"
@ -1373,18 +1328,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"debug@npm:^4.4.1":
version: 4.4.1
resolution: "debug@npm:4.4.1"
dependencies:
ms: "npm:^2.1.3"
peerDependenciesMeta:
supports-color:
optional: true
checksum: 10c0/d2b44bc1afd912b49bb7ebb0d50a860dc93a4dd7d946e8de94abc957bb63726b7dd5aa48c18c2386c379ec024c46692e15ed3ed97d481729f929201e671fcd55
languageName: node
linkType: hard
"deep-eql@npm:^5.0.1": "deep-eql@npm:^5.0.1":
version: 5.0.2 version: 5.0.2
resolution: "deep-eql@npm:5.0.2" resolution: "deep-eql@npm:5.0.2"
@ -1464,10 +1407,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"es-module-lexer@npm:^1.7.0": "es-module-lexer@npm:^1.6.0":
version: 1.7.0 version: 1.6.0
resolution: "es-module-lexer@npm:1.7.0" resolution: "es-module-lexer@npm:1.6.0"
checksum: 10c0/4c935affcbfeba7fb4533e1da10fa8568043df1e3574b869385980de9e2d475ddc36769891936dbb07036edb3c3786a8b78ccf44964cd130dedc1f2c984b6c7b checksum: 10c0/667309454411c0b95c476025929881e71400d74a746ffa1ff4cb450bd87f8e33e8eef7854d68e401895039ac0bac64e7809acbebb6253e055dd49ea9e3ea9212
languageName: node languageName: node
linkType: hard linkType: hard
@ -1573,10 +1516,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"expect-type@npm:^1.2.1": "expect-type@npm:^1.1.0":
version: 1.2.1 version: 1.2.0
resolution: "expect-type@npm:1.2.1" resolution: "expect-type@npm:1.2.0"
checksum: 10c0/b775c9adab3c190dd0d398c722531726cdd6022849b4adba19dceab58dda7e000a7c6c872408cd73d665baa20d381eca36af4f7b393a4ba60dd10232d1fb8898 checksum: 10c0/6069e1980bf16b9385646800e23499c1447df636c433014f6bbabe4bb0e20bd0033f30d38a6f9ae0938b0203a9e870cc82cdfd74b7c837b480cefb8e8240d8e8
languageName: node languageName: node
linkType: hard linkType: hard
@ -1601,18 +1544,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"fdir@npm:^6.4.4, fdir@npm:^6.4.6":
version: 6.4.6
resolution: "fdir@npm:6.4.6"
peerDependencies:
picomatch: ^3 || ^4
peerDependenciesMeta:
picomatch:
optional: true
checksum: 10c0/45b559cff889934ebb8bc498351e5acba40750ada7e7d6bde197768d2fa67c149be8ae7f8ff34d03f4e1eb20f2764116e56440aaa2f6689e9a4aa7ef06acafe9
languageName: node
linkType: hard
"foreground-child@npm:^3.1.0": "foreground-child@npm:^3.1.0":
version: 3.3.1 version: 3.3.1
resolution: "foreground-child@npm:3.3.1" resolution: "foreground-child@npm:3.3.1"
@ -1975,13 +1906,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"js-tokens@npm:^9.0.1":
version: 9.0.1
resolution: "js-tokens@npm:9.0.1"
checksum: 10c0/68dcab8f233dde211a6b5fd98079783cbcd04b53617c1250e3553ee16ab3e6134f5e65478e41d82f6d351a052a63d71024553933808570f04dbf828d7921e80e
languageName: node
linkType: hard
"json-schema-traverse@npm:^1.0.0": "json-schema-traverse@npm:^1.0.0":
version: 1.0.0 version: 1.0.0
resolution: "json-schema-traverse@npm:1.0.0" resolution: "json-schema-traverse@npm:1.0.0"
@ -2027,20 +1951,13 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"loupe@npm:^3.1.0": "loupe@npm:^3.1.0, loupe@npm:^3.1.3":
version: 3.1.3 version: 3.1.3
resolution: "loupe@npm:3.1.3" resolution: "loupe@npm:3.1.3"
checksum: 10c0/f5dab4144254677de83a35285be1b8aba58b3861439ce4ba65875d0d5f3445a4a496daef63100ccf02b2dbc25bf58c6db84c9cb0b96d6435331e9d0a33b48541 checksum: 10c0/f5dab4144254677de83a35285be1b8aba58b3861439ce4ba65875d0d5f3445a4a496daef63100ccf02b2dbc25bf58c6db84c9cb0b96d6435331e9d0a33b48541
languageName: node languageName: node
linkType: hard linkType: hard
"loupe@npm:^3.1.4":
version: 3.1.4
resolution: "loupe@npm:3.1.4"
checksum: 10c0/5c2e6aefaad25f812d361c750b8cf4ff91d68de289f141d7c85c2ce9bb79eeefa06a93c85f7b87cba940531ed8f15e492f32681d47eed23842ad1963eb3a154d
languageName: node
linkType: hard
"lru-cache@npm:^10.2.0": "lru-cache@npm:^10.2.0":
version: 10.4.3 version: 10.4.3
resolution: "lru-cache@npm:10.4.3" resolution: "lru-cache@npm:10.4.3"
@ -2279,7 +2196,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"nanoid@npm:^3.3.11, nanoid@npm:^3.3.8": "nanoid@npm:^3.3.8":
version: 3.3.11 version: 3.3.11
resolution: "nanoid@npm:3.3.11" resolution: "nanoid@npm:3.3.11"
bin: bin:
@ -2462,26 +2379,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"postcss@npm:^8.5.5":
version: 8.5.6
resolution: "postcss@npm:8.5.6"
dependencies:
nanoid: "npm:^3.3.11"
picocolors: "npm:^1.1.1"
source-map-js: "npm:^1.2.1"
checksum: 10c0/5127cc7c91ed7a133a1b7318012d8bfa112da9ef092dddf369ae699a1f10ebbd89b1b9f25f3228795b84585c72aabd5ced5fc11f2ba467eedf7b081a66fad024
languageName: node
linkType: hard
"prettier@npm:^3.6.0":
version: 3.6.0
resolution: "prettier@npm:3.6.0"
bin:
prettier: bin/prettier.cjs
checksum: 10c0/636897c8084b71ef1f740a46199df0d8f6ed896aa497212cc8cc229d3026b9dca82fa92f61e55924c4708cea11e39a88478e2d93c4818741c55b7408a9ac6d91
languageName: node
linkType: hard
"promise-inflight@npm:^1.0.1": "promise-inflight@npm:^1.0.1":
version: 1.0.1 version: 1.0.1
resolution: "promise-inflight@npm:1.0.1" resolution: "promise-inflight@npm:1.0.1"
@ -2575,31 +2472,30 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"rollup@npm:^4.34.9, rollup@npm:^4.40.0": "rollup@npm:^4.30.1":
version: 4.44.0 version: 4.36.0
resolution: "rollup@npm:4.44.0" resolution: "rollup@npm:4.36.0"
dependencies: dependencies:
"@rollup/rollup-android-arm-eabi": "npm:4.44.0" "@rollup/rollup-android-arm-eabi": "npm:4.36.0"
"@rollup/rollup-android-arm64": "npm:4.44.0" "@rollup/rollup-android-arm64": "npm:4.36.0"
"@rollup/rollup-darwin-arm64": "npm:4.44.0" "@rollup/rollup-darwin-arm64": "npm:4.36.0"
"@rollup/rollup-darwin-x64": "npm:4.44.0" "@rollup/rollup-darwin-x64": "npm:4.36.0"
"@rollup/rollup-freebsd-arm64": "npm:4.44.0" "@rollup/rollup-freebsd-arm64": "npm:4.36.0"
"@rollup/rollup-freebsd-x64": "npm:4.44.0" "@rollup/rollup-freebsd-x64": "npm:4.36.0"
"@rollup/rollup-linux-arm-gnueabihf": "npm:4.44.0" "@rollup/rollup-linux-arm-gnueabihf": "npm:4.36.0"
"@rollup/rollup-linux-arm-musleabihf": "npm:4.44.0" "@rollup/rollup-linux-arm-musleabihf": "npm:4.36.0"
"@rollup/rollup-linux-arm64-gnu": "npm:4.44.0" "@rollup/rollup-linux-arm64-gnu": "npm:4.36.0"
"@rollup/rollup-linux-arm64-musl": "npm:4.44.0" "@rollup/rollup-linux-arm64-musl": "npm:4.36.0"
"@rollup/rollup-linux-loongarch64-gnu": "npm:4.44.0" "@rollup/rollup-linux-loongarch64-gnu": "npm:4.36.0"
"@rollup/rollup-linux-powerpc64le-gnu": "npm:4.44.0" "@rollup/rollup-linux-powerpc64le-gnu": "npm:4.36.0"
"@rollup/rollup-linux-riscv64-gnu": "npm:4.44.0" "@rollup/rollup-linux-riscv64-gnu": "npm:4.36.0"
"@rollup/rollup-linux-riscv64-musl": "npm:4.44.0" "@rollup/rollup-linux-s390x-gnu": "npm:4.36.0"
"@rollup/rollup-linux-s390x-gnu": "npm:4.44.0" "@rollup/rollup-linux-x64-gnu": "npm:4.36.0"
"@rollup/rollup-linux-x64-gnu": "npm:4.44.0" "@rollup/rollup-linux-x64-musl": "npm:4.36.0"
"@rollup/rollup-linux-x64-musl": "npm:4.44.0" "@rollup/rollup-win32-arm64-msvc": "npm:4.36.0"
"@rollup/rollup-win32-arm64-msvc": "npm:4.44.0" "@rollup/rollup-win32-ia32-msvc": "npm:4.36.0"
"@rollup/rollup-win32-ia32-msvc": "npm:4.44.0" "@rollup/rollup-win32-x64-msvc": "npm:4.36.0"
"@rollup/rollup-win32-x64-msvc": "npm:4.44.0" "@types/estree": "npm:1.0.6"
"@types/estree": "npm:1.0.8"
fsevents: "npm:~2.3.2" fsevents: "npm:~2.3.2"
dependenciesMeta: dependenciesMeta:
"@rollup/rollup-android-arm-eabi": "@rollup/rollup-android-arm-eabi":
@ -2628,8 +2524,6 @@ __metadata:
optional: true optional: true
"@rollup/rollup-linux-riscv64-gnu": "@rollup/rollup-linux-riscv64-gnu":
optional: true optional: true
"@rollup/rollup-linux-riscv64-musl":
optional: true
"@rollup/rollup-linux-s390x-gnu": "@rollup/rollup-linux-s390x-gnu":
optional: true optional: true
"@rollup/rollup-linux-x64-gnu": "@rollup/rollup-linux-x64-gnu":
@ -2646,7 +2540,7 @@ __metadata:
optional: true optional: true
bin: bin:
rollup: dist/bin/rollup rollup: dist/bin/rollup
checksum: 10c0/ff3e0741f2fc7b7b183079628cf50fcfc9163bef86ecfbc9f4e4023adfdee375b7075940963514e2bc4969764688d38d67095bce038b0ad5d572207f114afff5 checksum: 10c0/52ad34ba18edb3613253ecbc7db5c8d6067ed103d8786051e96d42bcb383f7473bbda91b25297435b8a531fe308726cf1bb978456b9fcce044e4885510d73252
languageName: node languageName: node
linkType: hard linkType: hard
@ -2804,10 +2698,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"std-env@npm:^3.9.0": "std-env@npm:^3.8.0":
version: 3.9.0 version: 3.8.1
resolution: "std-env@npm:3.9.0" resolution: "std-env@npm:3.8.1"
checksum: 10c0/4a6f9218aef3f41046c3c7ecf1f98df00b30a07f4f35c6d47b28329bc2531eef820828951c7d7b39a1c5eb19ad8a46e3ddfc7deb28f0a2f3ceebee11bab7ba50 checksum: 10c0/e9b19cca6bc6f06f91607db5b636662914ca8ec9efc525a99da6ec7e493afec109d3b017d21d9782b4369fcfb2891c7c4b4e3c60d495fdadf6861ce434e07bf8
languageName: node languageName: node
linkType: hard linkType: hard
@ -2874,15 +2768,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"strip-literal@npm:^3.0.0":
version: 3.0.0
resolution: "strip-literal@npm:3.0.0"
dependencies:
js-tokens: "npm:^9.0.1"
checksum: 10c0/d81657f84aba42d4bbaf2a677f7e7f34c1f3de5a6726db8bc1797f9c0b303ba54d4660383a74bde43df401cf37cce1dff2c842c55b077a4ceee11f9e31fba828
languageName: node
linkType: hard
"supports-color@npm:^7.1.0": "supports-color@npm:^7.1.0":
version: 7.2.0 version: 7.2.0
resolution: "supports-color@npm:7.2.0" resolution: "supports-color@npm:7.2.0"
@ -2947,20 +2832,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tinyglobby@npm:^0.2.13, tinyglobby@npm:^0.2.14": "tinypool@npm:^1.0.2":
version: 0.2.14 version: 1.0.2
resolution: "tinyglobby@npm:0.2.14" resolution: "tinypool@npm:1.0.2"
dependencies: checksum: 10c0/31ac184c0ff1cf9a074741254fe9ea6de95026749eb2b8ec6fd2b9d8ca94abdccda731f8e102e7f32e72ed3b36d32c6975fd5f5523df3f1b6de6c3d8dfd95e63
fdir: "npm:^6.4.4"
picomatch: "npm:^4.0.2"
checksum: 10c0/f789ed6c924287a9b7d3612056ed0cda67306cd2c80c249fd280cf1504742b12583a2089b61f4abbd24605f390809017240e250241f09938054c9b363e51c0a6
languageName: node
linkType: hard
"tinypool@npm:^1.1.1":
version: 1.1.1
resolution: "tinypool@npm:1.1.1"
checksum: 10c0/bf26727d01443061b04fa863f571016950888ea994ba0cd8cba3a1c51e2458d84574341ab8dbc3664f1c3ab20885c8cf9ff1cc4b18201f04c2cde7d317fff69b
languageName: node languageName: node
linkType: hard linkType: hard
@ -2971,10 +2846,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"tinyspy@npm:^4.0.3": "tinyspy@npm:^3.0.2":
version: 4.0.3 version: 3.0.2
resolution: "tinyspy@npm:4.0.3" resolution: "tinyspy@npm:3.0.2"
checksum: 10c0/0a92a18b5350945cc8a1da3a22c9ad9f4e2945df80aaa0c43e1b3a3cfb64d8501e607ebf0305e048e3c3d3e0e7f8eb10cea27dc17c21effb73e66c4a3be36373 checksum: 10c0/55ffad24e346622b59292e097c2ee30a63919d5acb7ceca87fc0d1c223090089890587b426e20054733f97a58f20af2c349fb7cc193697203868ab7ba00bcea0
languageName: node languageName: node
linkType: hard linkType: hard
@ -3023,7 +2898,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@npm:5.8.2": "typescript@npm:5.8.2, typescript@npm:^5.8.2":
version: 5.8.2 version: 5.8.2
resolution: "typescript@npm:5.8.2" resolution: "typescript@npm:5.8.2"
bin: bin:
@ -3033,17 +2908,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@npm:^5.8.3": "typescript@patch:typescript@npm%3A5.8.2#optional!builtin<compat/typescript>, typescript@patch:typescript@npm%3A^5.8.2#optional!builtin<compat/typescript>":
version: 5.8.3
resolution: "typescript@npm:5.8.3"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/5f8bb01196e542e64d44db3d16ee0e4063ce4f3e3966df6005f2588e86d91c03e1fb131c2581baf0fb65ee79669eea6e161cd448178986587e9f6844446dbb48
languageName: node
linkType: hard
"typescript@patch:typescript@npm%3A5.8.2#optional!builtin<compat/typescript>":
version: 5.8.2 version: 5.8.2
resolution: "typescript@patch:typescript@npm%3A5.8.2#optional!builtin<compat/typescript>::version=5.8.2&hash=5786d5" resolution: "typescript@patch:typescript@npm%3A5.8.2#optional!builtin<compat/typescript>::version=5.8.2&hash=5786d5"
bin: bin:
@ -3053,16 +2918,6 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"typescript@patch:typescript@npm%3A^5.8.3#optional!builtin<compat/typescript>":
version: 5.8.3
resolution: "typescript@patch:typescript@npm%3A5.8.3#optional!builtin<compat/typescript>::version=5.8.3&hash=5786d5"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
checksum: 10c0/39117e346ff8ebd87ae1510b3a77d5d92dae5a89bde588c747d25da5c146603a99c8ee588c7ef80faaf123d89ed46f6dbd918d534d641083177d5fac38b8a1cb
languageName: node
linkType: hard
"ufo@npm:^1.5.4": "ufo@npm:^1.5.4":
version: 1.5.4 version: 1.5.4
resolution: "ufo@npm:1.5.4" resolution: "ufo@npm:1.5.4"
@ -3070,10 +2925,10 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"undici-types@npm:~7.8.0": "undici-types@npm:~6.20.0":
version: 7.8.0 version: 6.20.0
resolution: "undici-types@npm:7.8.0" resolution: "undici-types@npm:6.20.0"
checksum: 10c0/9d9d246d1dc32f318d46116efe3cfca5a72d4f16828febc1918d94e58f6ffcf39c158aa28bf5b4fc52f410446bc7858f35151367bd7a49f21746cab6497b709b checksum: 10c0/68e659a98898d6a836a9a59e6adf14a5d799707f5ea629433e025ac90d239f75e408e2e5ff086afc3cace26f8b26ee52155293564593fbb4a2f666af57fc59bf
languageName: node languageName: node
linkType: hard linkType: hard
@ -3125,24 +2980,24 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"vite-node@npm:3.2.4": "vite-node@npm:3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "vite-node@npm:3.2.4" resolution: "vite-node@npm:3.0.9"
dependencies: dependencies:
cac: "npm:^6.7.14" cac: "npm:^6.7.14"
debug: "npm:^4.4.1" debug: "npm:^4.4.0"
es-module-lexer: "npm:^1.7.0" es-module-lexer: "npm:^1.6.0"
pathe: "npm:^2.0.3" pathe: "npm:^2.0.3"
vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" vite: "npm:^5.0.0 || ^6.0.0"
bin: bin:
vite-node: vite-node.mjs vite-node: vite-node.mjs
checksum: 10c0/6ceca67c002f8ef6397d58b9539f80f2b5d79e103a18367288b3f00a8ab55affa3d711d86d9112fce5a7fa658a212a087a005a045eb8f4758947dd99af2a6c6b checksum: 10c0/97768a64182832c1ae1797667920fec002d283506b628b684df707fc453c6bf58719029c52c7a4cdf98f5a5a44769036126efdb8192d4040ba3d39f271aa338b
languageName: node languageName: node
linkType: hard linkType: hard
"vite-plugin-dts@npm:^4.5.4": "vite-plugin-dts@npm:^4.5.3":
version: 4.5.4 version: 4.5.3
resolution: "vite-plugin-dts@npm:4.5.4" resolution: "vite-plugin-dts@npm:4.5.3"
dependencies: dependencies:
"@microsoft/api-extractor": "npm:^7.50.1" "@microsoft/api-extractor": "npm:^7.50.1"
"@rollup/pluginutils": "npm:^5.1.4" "@rollup/pluginutils": "npm:^5.1.4"
@ -3159,76 +3014,18 @@ __metadata:
peerDependenciesMeta: peerDependenciesMeta:
vite: vite:
optional: true optional: true
checksum: 10c0/5fcb7f3739d115f36195a692c0e9f9fca4e504bbbbabe29e71ee06630dd05ea2920169371e80e548eb4779d2eca14107277497838d7df588d53e1fadf84be861 checksum: 10c0/e86ff3a92bda138b5d69696297f520e796282d65af8f459c2ed7870e79fc67b3ea74afabe79678c2141e1ab651ec73c3928d8ae255ff08ff0c9e08477f8b1bcf
languageName: node languageName: node
linkType: hard linkType: hard
"vite@npm:^5.0.0 || ^6.0.0 || ^7.0.0-0": "vite@npm:^5.0.0 || ^6.0.0, vite@npm:^6.2.3":
version: 7.0.0-beta.2 version: 6.2.3
resolution: "vite@npm:7.0.0-beta.2" resolution: "vite@npm:6.2.3"
dependencies: dependencies:
esbuild: "npm:^0.25.0" esbuild: "npm:^0.25.0"
fdir: "npm:^6.4.6"
fsevents: "npm:~2.3.3" fsevents: "npm:~2.3.3"
picomatch: "npm:^4.0.2"
postcss: "npm:^8.5.5"
rollup: "npm:^4.40.0"
tinyglobby: "npm:^0.2.14"
peerDependencies:
"@types/node": ^20.19.0 || >=22.12.0
jiti: ">=1.21.0"
less: ^4.0.0
lightningcss: ^1.21.0
sass: ^1.70.0
sass-embedded: ^1.70.0
stylus: ">=0.54.8"
sugarss: ^5.0.0
terser: ^5.16.0
tsx: ^4.8.1
yaml: ^2.4.2
dependenciesMeta:
fsevents:
optional: true
peerDependenciesMeta:
"@types/node":
optional: true
jiti:
optional: true
less:
optional: true
lightningcss:
optional: true
sass:
optional: true
sass-embedded:
optional: true
stylus:
optional: true
sugarss:
optional: true
terser:
optional: true
tsx:
optional: true
yaml:
optional: true
bin:
vite: bin/vite.js
checksum: 10c0/2cd51b6749171e255e478329c3f5c6fad9a3de0f36731396d1bebd77411bd93b5d835d0d8549fe707ddf5fb0e0c2f562e29b04d095d73408e4d340464182d140
languageName: node
linkType: hard
"vite@npm:^6.3.5":
version: 6.3.5
resolution: "vite@npm:6.3.5"
dependencies:
esbuild: "npm:^0.25.0"
fdir: "npm:^6.4.4"
fsevents: "npm:~2.3.3"
picomatch: "npm:^4.0.2"
postcss: "npm:^8.5.3" postcss: "npm:^8.5.3"
rollup: "npm:^4.34.9" rollup: "npm:^4.30.1"
tinyglobby: "npm:^0.2.13"
peerDependencies: peerDependencies:
"@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0
jiti: ">=1.21.0" jiti: ">=1.21.0"
@ -3269,43 +3066,40 @@ __metadata:
optional: true optional: true
bin: bin:
vite: bin/vite.js vite: bin/vite.js
checksum: 10c0/df70201659085133abffc6b88dcdb8a57ef35f742a01311fc56a4cfcda6a404202860729cc65a2c401a724f6e25f9ab40ce4339ed4946f550541531ced6fe41c checksum: 10c0/ba6ad7e83e5a63fb0b6f62d3a3963624b8784bdc1bfa2a83e16cf268fb58c76bd9f8e69f39ed34bf8711cdb8fd7702916f878781da53c232c34ef7a85e0600cf
languageName: node languageName: node
linkType: hard linkType: hard
"vitest@npm:^3.2.4": "vitest@npm:^3.0.9":
version: 3.2.4 version: 3.0.9
resolution: "vitest@npm:3.2.4" resolution: "vitest@npm:3.0.9"
dependencies: dependencies:
"@types/chai": "npm:^5.2.2" "@vitest/expect": "npm:3.0.9"
"@vitest/expect": "npm:3.2.4" "@vitest/mocker": "npm:3.0.9"
"@vitest/mocker": "npm:3.2.4" "@vitest/pretty-format": "npm:^3.0.9"
"@vitest/pretty-format": "npm:^3.2.4" "@vitest/runner": "npm:3.0.9"
"@vitest/runner": "npm:3.2.4" "@vitest/snapshot": "npm:3.0.9"
"@vitest/snapshot": "npm:3.2.4" "@vitest/spy": "npm:3.0.9"
"@vitest/spy": "npm:3.2.4" "@vitest/utils": "npm:3.0.9"
"@vitest/utils": "npm:3.2.4"
chai: "npm:^5.2.0" chai: "npm:^5.2.0"
debug: "npm:^4.4.1" debug: "npm:^4.4.0"
expect-type: "npm:^1.2.1" expect-type: "npm:^1.1.0"
magic-string: "npm:^0.30.17" magic-string: "npm:^0.30.17"
pathe: "npm:^2.0.3" pathe: "npm:^2.0.3"
picomatch: "npm:^4.0.2" std-env: "npm:^3.8.0"
std-env: "npm:^3.9.0"
tinybench: "npm:^2.9.0" tinybench: "npm:^2.9.0"
tinyexec: "npm:^0.3.2" tinyexec: "npm:^0.3.2"
tinyglobby: "npm:^0.2.14" tinypool: "npm:^1.0.2"
tinypool: "npm:^1.1.1"
tinyrainbow: "npm:^2.0.0" tinyrainbow: "npm:^2.0.0"
vite: "npm:^5.0.0 || ^6.0.0 || ^7.0.0-0" vite: "npm:^5.0.0 || ^6.0.0"
vite-node: "npm:3.2.4" vite-node: "npm:3.0.9"
why-is-node-running: "npm:^2.3.0" why-is-node-running: "npm:^2.3.0"
peerDependencies: peerDependencies:
"@edge-runtime/vm": "*" "@edge-runtime/vm": "*"
"@types/debug": ^4.1.12 "@types/debug": ^4.1.12
"@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0 "@types/node": ^18.0.0 || ^20.0.0 || >=22.0.0
"@vitest/browser": 3.2.4 "@vitest/browser": 3.0.9
"@vitest/ui": 3.2.4 "@vitest/ui": 3.0.9
happy-dom: "*" happy-dom: "*"
jsdom: "*" jsdom: "*"
peerDependenciesMeta: peerDependenciesMeta:
@ -3325,7 +3119,7 @@ __metadata:
optional: true optional: true
bin: bin:
vitest: vitest.mjs vitest: vitest.mjs
checksum: 10c0/5bf53ede3ae6a0e08956d72dab279ae90503f6b5a05298a6a5e6ef47d2fd1ab386aaf48fafa61ed07a0ebfe9e371772f1ccbe5c258dd765206a8218bf2eb79eb checksum: 10c0/5bcd25cab1681f3a968a6483cd5fe115791bc02769bd73bc680bf40153474391a03a6329781b0fb0b8c2f95c82eb342a972bd5132d9bd0d4be92977af19574d0
languageName: node languageName: node
linkType: hard linkType: hard