@twin.org/entity 0.0.1-next.5 → 0.0.1-next.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -61,7 +61,7 @@ function property(options) {
61
61
  const entitySchema = DecoratorHelper.getSchema(target);
62
62
  entitySchema.properties ??= [];
63
63
  const idx = entitySchema.properties.findIndex(p => p.property === propertyKey);
64
- if (idx >= 0) {
64
+ if (idx !== -1) {
65
65
  entitySchema.properties[idx] = {
66
66
  ...options,
67
67
  property: propertyKey
@@ -533,6 +533,60 @@ class EntitySchemaHelper {
533
533
  }
534
534
  return finalSortKeys;
535
535
  }
536
+ /**
537
+ * Validate the entity against the schema.
538
+ * @param entity The entity to validate.
539
+ * @param entitySchema The schema to validate against.
540
+ * @throws If the entity is invalid.
541
+ */
542
+ static validateEntity(entity, entitySchema) {
543
+ core.Guards.object(EntitySchemaHelper._CLASS_NAME, "entity", entity);
544
+ core.Guards.object(EntitySchemaHelper._CLASS_NAME, "entitySchema", entitySchema);
545
+ const properties = entitySchema.properties ?? [];
546
+ if (properties.length === 0 && core.Is.objectValue(entity)) {
547
+ throw new core.GeneralError(EntitySchemaHelper._CLASS_NAME, "invalidEntityProperties");
548
+ }
549
+ const allKeys = Object.keys(entity);
550
+ for (const prop of properties) {
551
+ const idx = allKeys.indexOf(prop.property);
552
+ if (idx !== -1) {
553
+ allKeys.splice(idx, 1);
554
+ }
555
+ const value = entity[prop.property];
556
+ if (core.Is.empty(value)) {
557
+ // If the value is empty but the property is not optional, then it's invalid
558
+ if (!prop.optional) {
559
+ throw new core.GeneralError(EntitySchemaHelper._CLASS_NAME, "invalidOptional", {
560
+ property: prop.property,
561
+ type: prop.type
562
+ });
563
+ }
564
+ }
565
+ else if (prop.type === "integer" && core.Is.integer(value)) ;
566
+ else if (prop.type === "object" &&
567
+ (core.Is.object(value) ||
568
+ core.Is.array(value) ||
569
+ core.Is.string(value) ||
570
+ core.Is.number(value) ||
571
+ core.Is.boolean(value) ||
572
+ core.Is.null(value))) ;
573
+ else if (prop.type === "array" && core.Is.array(value)) ;
574
+ else if (prop.type !== typeof value) {
575
+ // The schema type does not match the value type
576
+ throw new core.GeneralError(EntitySchemaHelper._CLASS_NAME, "invalidEntityProperty", {
577
+ value,
578
+ property: prop.property,
579
+ type: prop.type
580
+ });
581
+ }
582
+ }
583
+ if (allKeys.length > 0) {
584
+ // There are keys in the entity that are not in the schema
585
+ throw new core.GeneralError(EntitySchemaHelper._CLASS_NAME, "invalidEntityKeys", {
586
+ keys: allKeys.join(", ")
587
+ });
588
+ }
589
+ }
536
590
  }
537
591
 
538
592
  // Copyright 2024 IOTA Stiftung.
@@ -572,7 +626,9 @@ class EntitySorter {
572
626
  */
573
627
  static compare(entity1, entity2, prop, type, direction = SortDirection.Ascending) {
574
628
  let res = 0;
575
- if (!core.Is.empty(entity1[prop]) && !core.Is.empty(entity2[prop])) {
629
+ const hasProp1 = !core.Is.empty(entity1[prop]);
630
+ const hasProp2 = !core.Is.empty(entity2[prop]);
631
+ if (hasProp1 && hasProp2) {
576
632
  if (type === "number" || type === "integer") {
577
633
  res = entity1[prop] - entity2[prop];
578
634
  }
@@ -593,6 +649,12 @@ class EntitySorter {
593
649
  res = entity1[prop].localeCompare(entity2[prop]);
594
650
  }
595
651
  }
652
+ else if (hasProp1) {
653
+ res = -1;
654
+ }
655
+ else {
656
+ res = 1;
657
+ }
596
658
  return direction === SortDirection.Ascending ? res : res * -1;
597
659
  }
598
660
  }
@@ -59,7 +59,7 @@ function property(options) {
59
59
  const entitySchema = DecoratorHelper.getSchema(target);
60
60
  entitySchema.properties ??= [];
61
61
  const idx = entitySchema.properties.findIndex(p => p.property === propertyKey);
62
- if (idx >= 0) {
62
+ if (idx !== -1) {
63
63
  entitySchema.properties[idx] = {
64
64
  ...options,
65
65
  property: propertyKey
@@ -531,6 +531,60 @@ class EntitySchemaHelper {
531
531
  }
532
532
  return finalSortKeys;
533
533
  }
534
+ /**
535
+ * Validate the entity against the schema.
536
+ * @param entity The entity to validate.
537
+ * @param entitySchema The schema to validate against.
538
+ * @throws If the entity is invalid.
539
+ */
540
+ static validateEntity(entity, entitySchema) {
541
+ Guards.object(EntitySchemaHelper._CLASS_NAME, "entity", entity);
542
+ Guards.object(EntitySchemaHelper._CLASS_NAME, "entitySchema", entitySchema);
543
+ const properties = entitySchema.properties ?? [];
544
+ if (properties.length === 0 && Is.objectValue(entity)) {
545
+ throw new GeneralError(EntitySchemaHelper._CLASS_NAME, "invalidEntityProperties");
546
+ }
547
+ const allKeys = Object.keys(entity);
548
+ for (const prop of properties) {
549
+ const idx = allKeys.indexOf(prop.property);
550
+ if (idx !== -1) {
551
+ allKeys.splice(idx, 1);
552
+ }
553
+ const value = entity[prop.property];
554
+ if (Is.empty(value)) {
555
+ // If the value is empty but the property is not optional, then it's invalid
556
+ if (!prop.optional) {
557
+ throw new GeneralError(EntitySchemaHelper._CLASS_NAME, "invalidOptional", {
558
+ property: prop.property,
559
+ type: prop.type
560
+ });
561
+ }
562
+ }
563
+ else if (prop.type === "integer" && Is.integer(value)) ;
564
+ else if (prop.type === "object" &&
565
+ (Is.object(value) ||
566
+ Is.array(value) ||
567
+ Is.string(value) ||
568
+ Is.number(value) ||
569
+ Is.boolean(value) ||
570
+ Is.null(value))) ;
571
+ else if (prop.type === "array" && Is.array(value)) ;
572
+ else if (prop.type !== typeof value) {
573
+ // The schema type does not match the value type
574
+ throw new GeneralError(EntitySchemaHelper._CLASS_NAME, "invalidEntityProperty", {
575
+ value,
576
+ property: prop.property,
577
+ type: prop.type
578
+ });
579
+ }
580
+ }
581
+ if (allKeys.length > 0) {
582
+ // There are keys in the entity that are not in the schema
583
+ throw new GeneralError(EntitySchemaHelper._CLASS_NAME, "invalidEntityKeys", {
584
+ keys: allKeys.join(", ")
585
+ });
586
+ }
587
+ }
534
588
  }
535
589
 
536
590
  // Copyright 2024 IOTA Stiftung.
@@ -570,7 +624,9 @@ class EntitySorter {
570
624
  */
571
625
  static compare(entity1, entity2, prop, type, direction = SortDirection.Ascending) {
572
626
  let res = 0;
573
- if (!Is.empty(entity1[prop]) && !Is.empty(entity2[prop])) {
627
+ const hasProp1 = !Is.empty(entity1[prop]);
628
+ const hasProp2 = !Is.empty(entity2[prop]);
629
+ if (hasProp1 && hasProp2) {
574
630
  if (type === "number" || type === "integer") {
575
631
  res = entity1[prop] - entity2[prop];
576
632
  }
@@ -591,6 +647,12 @@ class EntitySorter {
591
647
  res = entity1[prop].localeCompare(entity2[prop]);
592
648
  }
593
649
  }
650
+ else if (hasProp1) {
651
+ res = -1;
652
+ }
653
+ else {
654
+ res = 1;
655
+ }
594
656
  return direction === SortDirection.Ascending ? res : res * -1;
595
657
  }
596
658
  }
@@ -35,4 +35,11 @@ export declare class EntitySchemaHelper {
35
35
  property: keyof T;
36
36
  sortDirection: SortDirection;
37
37
  }[]): IEntitySort<T>[] | undefined;
38
+ /**
39
+ * Validate the entity against the schema.
40
+ * @param entity The entity to validate.
41
+ * @param entitySchema The schema to validate against.
42
+ * @throws If the entity is invalid.
43
+ */
44
+ static validateEntity<T>(entity: T, entitySchema: IEntitySchema<T>): void;
38
45
  }
package/docs/changelog.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @twin.org/entity - Changelog
2
2
 
3
- ## 0.0.1-next.5
3
+ ## [0.0.1-next.51](https://github.com/twinfoundation/framework/compare/entity-v0.0.1-next.50...entity-v0.0.1-next.51) (2025-03-27)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * **entity:** Synchronize repo versions
9
+
10
+
11
+ ### Dependencies
12
+
13
+ * The following workspace dependencies were updated
14
+ * dependencies
15
+ * @twin.org/core bumped from 0.0.1-next.50 to 0.0.1-next.51
16
+
17
+ ## [0.0.1-next.50](https://github.com/twinfoundation/framework/compare/entity-v0.0.1-next.49...entity-v0.0.1-next.50) (2025-03-26)
18
+
19
+
20
+ ### Miscellaneous Chores
21
+
22
+ * **entity:** Synchronize repo versions
23
+
24
+
25
+ ### Dependencies
26
+
27
+ * The following workspace dependencies were updated
28
+ * dependencies
29
+ * @twin.org/core bumped from 0.0.1-next.49 to 0.0.1-next.50
30
+
31
+ ## 0.0.1-next.49
4
32
 
5
33
  - Initial Release
@@ -26,7 +26,9 @@ Get the schema from the reflection metadata.
26
26
 
27
27
  #### Parameters
28
28
 
29
- **target**: `any`
29
+ ##### target
30
+
31
+ `any`
30
32
 
31
33
  The object to get the schema data from.
32
34
 
@@ -50,11 +52,15 @@ Set the schema from the reflection metadata.
50
52
 
51
53
  #### Parameters
52
54
 
53
- **target**: `any`
55
+ ##### target
56
+
57
+ `any`
54
58
 
55
59
  The object to get the schema data from.
56
60
 
57
- **entitySchema**: [`IEntitySchema`](../interfaces/IEntitySchema.md)\<`T`\>
61
+ ##### entitySchema
62
+
63
+ [`IEntitySchema`](../interfaces/IEntitySchema.md)\<`T`\>
58
64
 
59
65
  The schema to set.
60
66
 
@@ -26,11 +26,15 @@ See if the entity matches the conditions.
26
26
 
27
27
  #### Parameters
28
28
 
29
- **entity**: `T`
29
+ ##### entity
30
+
31
+ `T`
30
32
 
31
33
  The entity to test.
32
34
 
33
- **condition?**: [`EntityCondition`](../type-aliases/EntityCondition.md)\<`T`\>
35
+ ##### condition?
36
+
37
+ [`EntityCondition`](../type-aliases/EntityCondition.md)\<`T`\>
34
38
 
35
39
  The conditions to test.
36
40
 
@@ -54,11 +58,15 @@ See if the entity matches the conditions.
54
58
 
55
59
  #### Parameters
56
60
 
57
- **entity**: `T`
61
+ ##### entity
62
+
63
+ `T`
58
64
 
59
65
  The entity to test.
60
66
 
61
- **comparator**: [`IComparator`](../interfaces/IComparator.md)
67
+ ##### comparator
68
+
69
+ [`IComparator`](../interfaces/IComparator.md)
62
70
 
63
71
  The condition to test.
64
72
 
@@ -26,7 +26,9 @@ Get the schema for the specified object.
26
26
 
27
27
  #### Parameters
28
28
 
29
- **target**: `any`
29
+ ##### target
30
+
31
+ `any`
30
32
 
31
33
  The object to get the schema data for.
32
34
 
@@ -50,7 +52,9 @@ Get the primary key from the entity schema.
50
52
 
51
53
  #### Parameters
52
54
 
53
- **entitySchema**: [`IEntitySchema`](../interfaces/IEntitySchema.md)\<`T`\>
55
+ ##### entitySchema
56
+
57
+ [`IEntitySchema`](../interfaces/IEntitySchema.md)\<`T`\>
54
58
 
55
59
  The entity schema to find the primary key from.
56
60
 
@@ -78,7 +82,9 @@ Get the sort properties from the schema.
78
82
 
79
83
  #### Parameters
80
84
 
81
- **entitySchema**: [`IEntitySchema`](../interfaces/IEntitySchema.md)\<`T`\>
85
+ ##### entitySchema
86
+
87
+ [`IEntitySchema`](../interfaces/IEntitySchema.md)\<`T`\>
82
88
 
83
89
  The entity schema to find the primary key from.
84
90
 
@@ -102,11 +108,15 @@ Build sort properties from the schema and override if necessary.
102
108
 
103
109
  #### Parameters
104
110
 
105
- **entitySchema**: [`IEntitySchema`](../interfaces/IEntitySchema.md)\<`T`\>
111
+ ##### entitySchema
112
+
113
+ [`IEntitySchema`](../interfaces/IEntitySchema.md)\<`T`\>
106
114
 
107
115
  The entity schema to retrieve the default sort keys.
108
116
 
109
- **overrideSortKeys?**: `object`[]
117
+ ##### overrideSortKeys?
118
+
119
+ `object`[]
110
120
 
111
121
  The override sort keys.
112
122
 
@@ -115,3 +125,37 @@ The override sort keys.
115
125
  `undefined` \| [`IEntitySort`](../interfaces/IEntitySort.md)\<`T`\>[]
116
126
 
117
127
  The finalised sort keys.
128
+
129
+ ***
130
+
131
+ ### validateEntity()
132
+
133
+ > `static` **validateEntity**\<`T`\>(`entity`, `entitySchema`): `void`
134
+
135
+ Validate the entity against the schema.
136
+
137
+ #### Type Parameters
138
+
139
+ • **T**
140
+
141
+ #### Parameters
142
+
143
+ ##### entity
144
+
145
+ `T`
146
+
147
+ The entity to validate.
148
+
149
+ ##### entitySchema
150
+
151
+ [`IEntitySchema`](../interfaces/IEntitySchema.md)\<`T`\>
152
+
153
+ The schema to validate against.
154
+
155
+ #### Returns
156
+
157
+ `void`
158
+
159
+ #### Throws
160
+
161
+ If the entity is invalid.
@@ -26,11 +26,15 @@ Sort a list of entities using multiple keys and direction.
26
26
 
27
27
  #### Parameters
28
28
 
29
- **entities**: `T`[]
29
+ ##### entities
30
+
31
+ `T`[]
30
32
 
31
33
  The list of entities.
32
34
 
33
- **entitySorters?**: [`IEntitySort`](../interfaces/IEntitySort.md)\<`T`\>[]
35
+ ##### entitySorters?
36
+
37
+ [`IEntitySort`](../interfaces/IEntitySort.md)\<`T`\>[]
34
38
 
35
39
  The sort keys to use.
36
40
 
@@ -54,23 +58,33 @@ Compare two properties.
54
58
 
55
59
  #### Parameters
56
60
 
57
- **entity1**: `T`
61
+ ##### entity1
62
+
63
+ `T`
58
64
 
59
65
  The first entity.
60
66
 
61
- **entity2**: `T`
67
+ ##### entity2
68
+
69
+ `T`
62
70
 
63
71
  The second entity.
64
72
 
65
- **prop**: keyof `T`
73
+ ##### prop
74
+
75
+ keyof `T`
66
76
 
67
77
  The property to compare.
68
78
 
69
- **type**: [`EntitySchemaPropertyType`](../type-aliases/EntitySchemaPropertyType.md)
79
+ ##### type
80
+
81
+ [`EntitySchemaPropertyType`](../type-aliases/EntitySchemaPropertyType.md)
70
82
 
71
83
  The type of the property.
72
84
 
73
- **direction**: [`SortDirection`](../type-aliases/SortDirection.md) = `SortDirection.Ascending`
85
+ ##### direction
86
+
87
+ [`SortDirection`](../type-aliases/SortDirection.md) = `SortDirection.Ascending`
74
88
 
75
89
  The direction of the sort.
76
90
 
@@ -6,7 +6,9 @@ Decorator to produce schema data for entity.
6
6
 
7
7
  ## Parameters
8
8
 
9
- **options?**: [`IEntitySchemaOptions`](../interfaces/IEntitySchemaOptions.md)
9
+ ### options?
10
+
11
+ [`IEntitySchemaOptions`](../interfaces/IEntitySchemaOptions.md)
10
12
 
11
13
  The options for the entity.
12
14
 
@@ -6,7 +6,9 @@ Decorator to produce schema property data for entities.
6
6
 
7
7
  ## Parameters
8
8
 
9
- **options**: `Omit`\<[`IEntitySchemaProperty`](../interfaces/IEntitySchemaProperty.md)\<`unknown`\>, `"property"`\>
9
+ ### options
10
+
11
+ `Omit`\<[`IEntitySchemaProperty`](../interfaces/IEntitySchemaProperty.md)\<`unknown`\>, `"property"`\>
10
12
 
11
13
  The options for the property.
12
14
 
package/locales/en.json CHANGED
@@ -2,7 +2,11 @@
2
2
  "error": {
3
3
  "entitySchemaHelper": {
4
4
  "noIsPrimary": "Property \"entitySchema.properties\" must contain a value with isPrimary set",
5
- "multipleIsPrimary": "Property \"entitySchema.properties\" contains more than one property with isPrimary set"
5
+ "multipleIsPrimary": "Property \"entitySchema.properties\" contains more than one property with isPrimary set",
6
+ "invalidEntityProperties": "The schema has no properties defined, but the entity has properties",
7
+ "invalidEntityProperty": "The entity value of \"{value}\" does not match the type \"{type}\" for property \"{property}\"",
8
+ "invalidOptional": "The entity property \"{property}\" of type \"{type}\" is not optional, but no value has been provided",
9
+ "invalidEntityKeys": "The entity had additional properties that are not in the schema, \"{keys}\""
6
10
  }
7
11
  }
8
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/entity",
3
- "version": "0.0.1-next.5",
3
+ "version": "0.0.1-next.51",
4
4
  "description": "Helpers for defining and working with entities",
5
5
  "repository": {
6
6
  "type": "git",
@@ -14,7 +14,7 @@
14
14
  "node": ">=20.0.0"
15
15
  },
16
16
  "dependencies": {
17
- "@twin.org/core": "0.0.1-next.5",
17
+ "@twin.org/core": "0.0.1-next.51",
18
18
  "@twin.org/nameof": "next",
19
19
  "reflect-metadata": "0.2.2"
20
20
  },
@@ -23,11 +23,11 @@
23
23
  "types": "./dist/types/index.d.ts",
24
24
  "exports": {
25
25
  ".": {
26
+ "types": "./dist/types/index.d.ts",
26
27
  "require": "./dist/cjs/index.cjs",
27
- "import": "./dist/esm/index.mjs",
28
- "types": "./dist/types/index.d.ts"
28
+ "import": "./dist/esm/index.mjs"
29
29
  },
30
- "./locales": "./locales"
30
+ "./locales/*.json": "./locales/*.json"
31
31
  },
32
32
  "files": [
33
33
  "dist/cjs",