@twin.org/entity-storage-models 0.9.1-next.5 → 0.9.1-next.7

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.
@@ -1,7 +1,7 @@
1
1
  // Copyright 2026 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  import { GeneralError, Is, ObjectHelper } from "@twin.org/core";
4
- import { ComparisonOperator, EntitySchemaHelper } from "@twin.org/entity";
4
+ import { ComparisonOperator, EntitySchemaHelper, EntitySchemaPropertyType } from "@twin.org/entity";
5
5
  /**
6
6
  * Helper class for performing schema migrations between two connectors.
7
7
  */
@@ -10,6 +10,18 @@ export class EntityStorageHelper {
10
10
  * Runtime name for the class.
11
11
  */
12
12
  static CLASS_NAME = "EntityStorageHelper";
13
+ /**
14
+ * Maximum allowed length for each dot-notation path segment.
15
+ * @internal
16
+ */
17
+ static _MAX_PATH_SEGMENT_LENGTH = 128;
18
+ /**
19
+ * Regex matching characters/sequences disallowed in a dot-notation path segment:
20
+ * SQL metacharacters (single-quote, double-quote, semicolon, hash, backslash),
21
+ * comment sequences (double-dash, slash-star, star-slash) and ASCII control characters.
22
+ * @internal
23
+ */
24
+ static _INVALID_PATH_SEGMENT = /['";#\\]|--|\/\*|\*\/|[\x00-\x1F\x7F]/;
13
25
  /**
14
26
  * Prepare the entity by handling undefined and null values and validating it against the schema.
15
27
  * @param entity The entity to handle undefined and null values for.
@@ -70,7 +82,7 @@ export class EntityStorageHelper {
70
82
  static validateSortProperties(schema, sortProperties) {
71
83
  if (Is.arrayValue(sortProperties)) {
72
84
  for (const sortProperty of sortProperties) {
73
- const propertySchema = schema.properties?.find(p => p.property === sortProperty.property);
85
+ const propertySchema = (schema.properties ?? []).find(p => p.property === sortProperty.property);
74
86
  if (Is.undefined(propertySchema) ||
75
87
  (!propertySchema.isPrimary &&
76
88
  !propertySchema.isSecondary &&
@@ -92,7 +104,7 @@ export class EntityStorageHelper {
92
104
  static validateProperties(schema, properties) {
93
105
  if (Is.arrayValue(properties)) {
94
106
  for (const property of properties) {
95
- const propertySchema = schema.properties?.find(p => p.property === property);
107
+ const propertySchema = (schema.properties ?? []).find(p => p.property === property);
96
108
  if (Is.undefined(propertySchema)) {
97
109
  throw new GeneralError(EntityStorageHelper.CLASS_NAME, "propertyNotInSchema", {
98
110
  property
@@ -101,6 +113,69 @@ export class EntityStorageHelper {
101
113
  }
102
114
  }
103
115
  }
116
+ /**
117
+ * Validate that every leaf property in an EntityCondition tree is a recognised schema property.
118
+ * The root part of a dot-notation path must exist in the schema and be of type object or array.
119
+ * @param schema The entity schema to validate against.
120
+ * @param condition The condition tree to validate, may be undefined.
121
+ * @throws GeneralError with message key "unknownPropertyInConditionProperty" if the root property is not in the schema.
122
+ * @throws GeneralError with message key "invalidConditionPropertyPath" if dot-notation is used on a non-object/array property.
123
+ */
124
+ static validateConditionProperties(schema, condition) {
125
+ if (Is.empty(condition)) {
126
+ return;
127
+ }
128
+ if ("conditions" in condition) {
129
+ for (const child of condition.conditions) {
130
+ EntityStorageHelper.validateConditionProperties(schema, child);
131
+ }
132
+ return;
133
+ }
134
+ const propertyStr = String(condition.property);
135
+ const parts = propertyStr.split(".");
136
+ const rootPropName = parts[0];
137
+ const rootSchema = (schema.properties ?? []).find(p => String(p.property) === rootPropName);
138
+ if (Is.undefined(rootSchema)) {
139
+ throw new GeneralError(EntityStorageHelper.CLASS_NAME, "unknownPropertyInConditionProperty", {
140
+ property: condition.property
141
+ });
142
+ }
143
+ if (parts.length > 1) {
144
+ if (rootSchema.type !== EntitySchemaPropertyType.Object &&
145
+ rootSchema.type !== EntitySchemaPropertyType.Array) {
146
+ throw new GeneralError(EntityStorageHelper.CLASS_NAME, "invalidConditionPropertyPath", {
147
+ property: condition.property
148
+ });
149
+ }
150
+ for (const part of parts.slice(1)) {
151
+ if (part.length > EntityStorageHelper._MAX_PATH_SEGMENT_LENGTH ||
152
+ EntityStorageHelper._INVALID_PATH_SEGMENT.test(part)) {
153
+ throw new GeneralError(EntityStorageHelper.CLASS_NAME, "invalidConditionPropertyPath", {
154
+ property: condition.property
155
+ });
156
+ }
157
+ }
158
+ }
159
+ }
160
+ /**
161
+ * Validate that every property in a conditions array is a recognised schema property.
162
+ * @param schema The entity schema to validate against.
163
+ * @param conditions The conditions array to validate, may be undefined.
164
+ * @throws GeneralError with message key "unknownPropertyInConditions" if validation fails.
165
+ */
166
+ static validateConditions(schema, conditions) {
167
+ if (!Is.arrayValue(conditions)) {
168
+ return;
169
+ }
170
+ for (const condition of conditions) {
171
+ const isInSchema = (schema.properties ?? []).some(p => p.property === condition.property);
172
+ if (!isInSchema) {
173
+ throw new GeneralError(EntityStorageHelper.CLASS_NAME, "unknownPropertyInConditions", {
174
+ property: condition.property
175
+ });
176
+ }
177
+ }
178
+ }
104
179
  /**
105
180
  * Deep-clone condition tree and normalise null/undefined to undefined on Equals/NotEquals leaves
106
181
  * so in-memory evaluation matches stored-absent semantics (optional absent props are omitted/undefined).
@@ -1 +1 @@
1
- {"version":3,"file":"entityStorageHelper.js","sourceRoot":"","sources":["../../../src/helpers/entityStorageHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Is, ObjectHelper } from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntitySchemaHelper,\n\ttype IEntitySchema,\n\ttype SortDirection\n} from \"@twin.org/entity\";\nimport { nameof } from \"@twin.org/nameof\";\n\n/**\n * Helper class for performing schema migrations between two connectors.\n */\nexport class EntityStorageHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntityStorageHelper>();\n\n\t/**\n\t * Prepare the entity by handling undefined and null values and validating it against the schema.\n\t * @param entity The entity to handle undefined and null values for.\n\t * @param schema The schema to validate the entity against.\n\t * @param additionalProperties Optional list of additional properties to set on the entity.\n\t * @param options Options controlling how null/undefined optional properties are stored.\n\t * @param options.nullBehavior \"omit\" strips null/undefined optional properties before writing\n\t * (NoSQL — avoids index-key type errors). \"nullify\" converts undefined to null (SQL — the default).\n\t * @returns The entity with undefined and null values handled.\n\t */\n\tpublic static prepareEntity<T>(\n\t\tentity: T,\n\t\tschema: IEntitySchema<T>,\n\t\tadditionalProperties?: { property: string; value: unknown }[],\n\t\toptions?: { nullBehavior?: \"omit\" | \"nullify\" }\n\t): T {\n\t\tconst entityForValidation = ObjectHelper.clone(entity);\n\t\tEntitySchemaHelper.validateEntity(entityForValidation, schema);\n\n\t\tif (Is.arrayValue(schema.properties)) {\n\t\t\tfor (const property of schema.properties) {\n\t\t\t\tif (property.optional ?? false) {\n\t\t\t\t\tconst propValue = entityForValidation[property.property];\n\t\t\t\t\tif (options?.nullBehavior === \"omit\") {\n\t\t\t\t\t\tif (propValue === undefined || propValue === null) {\n\t\t\t\t\t\t\tObjectHelper.propertyDelete(entityForValidation, property.property as string);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (propValue === undefined) {\n\t\t\t\t\t\tObjectHelper.propertySet(entityForValidation, property.property as string, null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (Is.arrayValue(additionalProperties)) {\n\t\t\tfor (const additionalProperty of additionalProperties) {\n\t\t\t\tObjectHelper.propertySet(\n\t\t\t\t\tentityForValidation,\n\t\t\t\t\tadditionalProperty.property,\n\t\t\t\t\tadditionalProperty.value\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn entityForValidation;\n\t}\n\n\t/**\n\t * Un-prepare the entity by removing null values.\n\t * @param entity The entity to handle undefined and null values for.\n\t * @param removeProperties Optional list of properties to remove from the entity.\n\t * @returns The entity with undefined and null values handled.\n\t */\n\tpublic static unPrepareEntity<T>(entity: Partial<T> | undefined, removeProperties?: string[]): T {\n\t\tconst nonNullEntity = ObjectHelper.removeEmptyProperties(entity, { removeNull: true });\n\n\t\tif (Is.arrayValue(removeProperties)) {\n\t\t\tfor (const property of removeProperties) {\n\t\t\t\tObjectHelper.propertyDelete(nonNullEntity, property);\n\t\t\t}\n\t\t}\n\n\t\treturn nonNullEntity as T;\n\t}\n\n\t/**\n\t * Validate that every sort property in the list is indexed in the schema (isPrimary, isSecondary,\n\t * or has a default sortDirection), throwing sortNotIndexed for the first violation found.\n\t * @param schema The entity schema to validate against.\n\t * @param sortProperties The sort properties to check.\n\t * @throws GeneralError If a sort property is not indexed in the schema.\n\t */\n\tpublic static validateSortProperties<T>(\n\t\tschema: IEntitySchema<T>,\n\t\tsortProperties?: { property: keyof T; sortDirection: SortDirection }[]\n\t): void {\n\t\tif (Is.arrayValue(sortProperties)) {\n\t\t\tfor (const sortProperty of sortProperties) {\n\t\t\t\tconst propertySchema = schema.properties?.find(p => p.property === sortProperty.property);\n\t\t\t\tif (\n\t\t\t\t\tIs.undefined(propertySchema) ||\n\t\t\t\t\t(!propertySchema.isPrimary &&\n\t\t\t\t\t\t!propertySchema.isSecondary &&\n\t\t\t\t\t\tIs.empty(propertySchema.sortDirection))\n\t\t\t\t) {\n\t\t\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"sortNotIndexed\", {\n\t\t\t\t\t\tproperty: sortProperty.property\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate that every property in the list exists in the schema, throwing propertyNotInSchema\n\t * for the first property that is not found.\n\t * @param schema The entity schema to validate against.\n\t * @param properties The properties to check.\n\t * @throws GeneralError If a property does not exist in the schema.\n\t */\n\tpublic static validateProperties<T>(schema: IEntitySchema<T>, properties?: (keyof T)[]): void {\n\t\tif (Is.arrayValue(properties)) {\n\t\t\tfor (const property of properties) {\n\t\t\t\tconst propertySchema = schema.properties?.find(p => p.property === property);\n\t\t\t\tif (Is.undefined(propertySchema)) {\n\t\t\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"propertyNotInSchema\", {\n\t\t\t\t\t\tproperty\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Deep-clone condition tree and normalise null/undefined to undefined on Equals/NotEquals leaves\n\t * so in-memory evaluation matches stored-absent semantics (optional absent props are omitted/undefined).\n\t * @param condition The user-supplied condition (not mutated).\n\t * @returns A clone safe to pass to check.\n\t */\n\tpublic static normalizeConditionValues<T>(condition: EntityCondition<T>): EntityCondition<T> {\n\t\tif (\"conditions\" in condition) {\n\t\t\treturn {\n\t\t\t\t...condition,\n\t\t\t\tconditions: condition.conditions.map(c => EntityStorageHelper.normalizeConditionValues(c))\n\t\t\t};\n\t\t}\n\n\t\tconst leaf = condition;\n\t\tif (\n\t\t\t(leaf.comparison === ComparisonOperator.Equals ||\n\t\t\t\tleaf.comparison === ComparisonOperator.NotEquals) &&\n\t\t\t(leaf.value === undefined || leaf.value === null)\n\t\t) {\n\t\t\treturn { ...leaf, value: undefined };\n\t\t}\n\t\treturn { ...leaf };\n\t}\n}\n"]}
1
+ {"version":3,"file":"entityStorageHelper.js","sourceRoot":"","sources":["../../../src/helpers/entityStorageHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Is, ObjectHelper } from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntitySchemaHelper,\n\tEntitySchemaPropertyType,\n\ttype IEntitySchema,\n\ttype SortDirection\n} from \"@twin.org/entity\";\nimport { nameof } from \"@twin.org/nameof\";\n\n/**\n * Helper class for performing schema migrations between two connectors.\n */\nexport class EntityStorageHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntityStorageHelper>();\n\n\t/**\n\t * Maximum allowed length for each dot-notation path segment.\n\t * @internal\n\t */\n\tprivate static readonly _MAX_PATH_SEGMENT_LENGTH: number = 128;\n\n\t/**\n\t * Regex matching characters/sequences disallowed in a dot-notation path segment:\n\t * SQL metacharacters (single-quote, double-quote, semicolon, hash, backslash),\n\t * comment sequences (double-dash, slash-star, star-slash) and ASCII control characters.\n\t * @internal\n\t */\n\tprivate static readonly _INVALID_PATH_SEGMENT: RegExp = /['\";#\\\\]|--|\\/\\*|\\*\\/|[\\x00-\\x1F\\x7F]/;\n\n\t/**\n\t * Prepare the entity by handling undefined and null values and validating it against the schema.\n\t * @param entity The entity to handle undefined and null values for.\n\t * @param schema The schema to validate the entity against.\n\t * @param additionalProperties Optional list of additional properties to set on the entity.\n\t * @param options Options controlling how null/undefined optional properties are stored.\n\t * @param options.nullBehavior \"omit\" strips null/undefined optional properties before writing\n\t * (NoSQL — avoids index-key type errors). \"nullify\" converts undefined to null (SQL — the default).\n\t * @returns The entity with undefined and null values handled.\n\t */\n\tpublic static prepareEntity<T>(\n\t\tentity: T,\n\t\tschema: IEntitySchema<T>,\n\t\tadditionalProperties?: { property: string; value: unknown }[],\n\t\toptions?: { nullBehavior?: \"omit\" | \"nullify\" }\n\t): T {\n\t\tconst entityForValidation = ObjectHelper.clone(entity);\n\t\tEntitySchemaHelper.validateEntity(entityForValidation, schema);\n\n\t\tif (Is.arrayValue(schema.properties)) {\n\t\t\tfor (const property of schema.properties) {\n\t\t\t\tif (property.optional ?? false) {\n\t\t\t\t\tconst propValue = entityForValidation[property.property];\n\t\t\t\t\tif (options?.nullBehavior === \"omit\") {\n\t\t\t\t\t\tif (propValue === undefined || propValue === null) {\n\t\t\t\t\t\t\tObjectHelper.propertyDelete(entityForValidation, property.property as string);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (propValue === undefined) {\n\t\t\t\t\t\tObjectHelper.propertySet(entityForValidation, property.property as string, null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (Is.arrayValue(additionalProperties)) {\n\t\t\tfor (const additionalProperty of additionalProperties) {\n\t\t\t\tObjectHelper.propertySet(\n\t\t\t\t\tentityForValidation,\n\t\t\t\t\tadditionalProperty.property,\n\t\t\t\t\tadditionalProperty.value\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn entityForValidation;\n\t}\n\n\t/**\n\t * Un-prepare the entity by removing null values.\n\t * @param entity The entity to handle undefined and null values for.\n\t * @param removeProperties Optional list of properties to remove from the entity.\n\t * @returns The entity with undefined and null values handled.\n\t */\n\tpublic static unPrepareEntity<T>(entity: Partial<T> | undefined, removeProperties?: string[]): T {\n\t\tconst nonNullEntity = ObjectHelper.removeEmptyProperties(entity, { removeNull: true });\n\n\t\tif (Is.arrayValue(removeProperties)) {\n\t\t\tfor (const property of removeProperties) {\n\t\t\t\tObjectHelper.propertyDelete(nonNullEntity, property);\n\t\t\t}\n\t\t}\n\n\t\treturn nonNullEntity as T;\n\t}\n\n\t/**\n\t * Validate that every sort property in the list is indexed in the schema (isPrimary, isSecondary,\n\t * or has a default sortDirection), throwing sortNotIndexed for the first violation found.\n\t * @param schema The entity schema to validate against.\n\t * @param sortProperties The sort properties to check.\n\t * @throws GeneralError If a sort property is not indexed in the schema.\n\t */\n\tpublic static validateSortProperties<T>(\n\t\tschema: IEntitySchema<T>,\n\t\tsortProperties?: { property: keyof T; sortDirection: SortDirection }[]\n\t): void {\n\t\tif (Is.arrayValue(sortProperties)) {\n\t\t\tfor (const sortProperty of sortProperties) {\n\t\t\t\tconst propertySchema = (schema.properties ?? []).find(\n\t\t\t\t\tp => p.property === sortProperty.property\n\t\t\t\t);\n\t\t\t\tif (\n\t\t\t\t\tIs.undefined(propertySchema) ||\n\t\t\t\t\t(!propertySchema.isPrimary &&\n\t\t\t\t\t\t!propertySchema.isSecondary &&\n\t\t\t\t\t\tIs.empty(propertySchema.sortDirection))\n\t\t\t\t) {\n\t\t\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"sortNotIndexed\", {\n\t\t\t\t\t\tproperty: sortProperty.property\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate that every property in the list exists in the schema, throwing propertyNotInSchema\n\t * for the first property that is not found.\n\t * @param schema The entity schema to validate against.\n\t * @param properties The properties to check.\n\t * @throws GeneralError If a property does not exist in the schema.\n\t */\n\tpublic static validateProperties<T>(schema: IEntitySchema<T>, properties?: (keyof T)[]): void {\n\t\tif (Is.arrayValue(properties)) {\n\t\t\tfor (const property of properties) {\n\t\t\t\tconst propertySchema = (schema.properties ?? []).find(p => p.property === property);\n\t\t\t\tif (Is.undefined(propertySchema)) {\n\t\t\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"propertyNotInSchema\", {\n\t\t\t\t\t\tproperty\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate that every leaf property in an EntityCondition tree is a recognised schema property.\n\t * The root part of a dot-notation path must exist in the schema and be of type object or array.\n\t * @param schema The entity schema to validate against.\n\t * @param condition The condition tree to validate, may be undefined.\n\t * @throws GeneralError with message key \"unknownPropertyInConditionProperty\" if the root property is not in the schema.\n\t * @throws GeneralError with message key \"invalidConditionPropertyPath\" if dot-notation is used on a non-object/array property.\n\t */\n\tpublic static validateConditionProperties<T>(\n\t\tschema: IEntitySchema<T>,\n\t\tcondition: EntityCondition<T> | undefined\n\t): void {\n\t\tif (Is.empty(condition)) {\n\t\t\treturn;\n\t\t}\n\t\tif (\"conditions\" in condition) {\n\t\t\tfor (const child of condition.conditions) {\n\t\t\t\tEntityStorageHelper.validateConditionProperties(schema, child);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\tconst propertyStr = String(condition.property);\n\t\tconst parts = propertyStr.split(\".\");\n\t\tconst rootPropName = parts[0];\n\n\t\tconst rootSchema = (schema.properties ?? []).find(p => String(p.property) === rootPropName);\n\n\t\tif (Is.undefined(rootSchema)) {\n\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"unknownPropertyInConditionProperty\", {\n\t\t\t\tproperty: condition.property\n\t\t\t});\n\t\t}\n\n\t\tif (parts.length > 1) {\n\t\t\tif (\n\t\t\t\trootSchema.type !== EntitySchemaPropertyType.Object &&\n\t\t\t\trootSchema.type !== EntitySchemaPropertyType.Array\n\t\t\t) {\n\t\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"invalidConditionPropertyPath\", {\n\t\t\t\t\tproperty: condition.property\n\t\t\t\t});\n\t\t\t}\n\t\t\tfor (const part of parts.slice(1)) {\n\t\t\t\tif (\n\t\t\t\t\tpart.length > EntityStorageHelper._MAX_PATH_SEGMENT_LENGTH ||\n\t\t\t\t\tEntityStorageHelper._INVALID_PATH_SEGMENT.test(part)\n\t\t\t\t) {\n\t\t\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"invalidConditionPropertyPath\", {\n\t\t\t\t\t\tproperty: condition.property\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate that every property in a conditions array is a recognised schema property.\n\t * @param schema The entity schema to validate against.\n\t * @param conditions The conditions array to validate, may be undefined.\n\t * @throws GeneralError with message key \"unknownPropertyInConditions\" if validation fails.\n\t */\n\tpublic static validateConditions<T>(\n\t\tschema: IEntitySchema<T>,\n\t\tconditions: { property: keyof T; value: unknown }[] | undefined\n\t): void {\n\t\tif (!Is.arrayValue(conditions)) {\n\t\t\treturn;\n\t\t}\n\t\tfor (const condition of conditions) {\n\t\t\tconst isInSchema = (schema.properties ?? []).some(p => p.property === condition.property);\n\n\t\t\tif (!isInSchema) {\n\t\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"unknownPropertyInConditions\", {\n\t\t\t\t\tproperty: condition.property\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Deep-clone condition tree and normalise null/undefined to undefined on Equals/NotEquals leaves\n\t * so in-memory evaluation matches stored-absent semantics (optional absent props are omitted/undefined).\n\t * @param condition The user-supplied condition (not mutated).\n\t * @returns A clone safe to pass to check.\n\t */\n\tpublic static normalizeConditionValues<T>(condition: EntityCondition<T>): EntityCondition<T> {\n\t\tif (\"conditions\" in condition) {\n\t\t\treturn {\n\t\t\t\t...condition,\n\t\t\t\tconditions: condition.conditions.map(c => EntityStorageHelper.normalizeConditionValues(c))\n\t\t\t};\n\t\t}\n\n\t\tconst leaf = condition;\n\t\tif (\n\t\t\t(leaf.comparison === ComparisonOperator.Equals ||\n\t\t\t\tleaf.comparison === ComparisonOperator.NotEquals) &&\n\t\t\t(leaf.value === undefined || leaf.value === null)\n\t\t) {\n\t\t\treturn { ...leaf, value: undefined };\n\t\t}\n\t\treturn { ...leaf };\n\t}\n}\n"]}
@@ -49,6 +49,25 @@ export declare class EntityStorageHelper {
49
49
  * @throws GeneralError If a property does not exist in the schema.
50
50
  */
51
51
  static validateProperties<T>(schema: IEntitySchema<T>, properties?: (keyof T)[]): void;
52
+ /**
53
+ * Validate that every leaf property in an EntityCondition tree is a recognised schema property.
54
+ * The root part of a dot-notation path must exist in the schema and be of type object or array.
55
+ * @param schema The entity schema to validate against.
56
+ * @param condition The condition tree to validate, may be undefined.
57
+ * @throws GeneralError with message key "unknownPropertyInConditionProperty" if the root property is not in the schema.
58
+ * @throws GeneralError with message key "invalidConditionPropertyPath" if dot-notation is used on a non-object/array property.
59
+ */
60
+ static validateConditionProperties<T>(schema: IEntitySchema<T>, condition: EntityCondition<T> | undefined): void;
61
+ /**
62
+ * Validate that every property in a conditions array is a recognised schema property.
63
+ * @param schema The entity schema to validate against.
64
+ * @param conditions The conditions array to validate, may be undefined.
65
+ * @throws GeneralError with message key "unknownPropertyInConditions" if validation fails.
66
+ */
67
+ static validateConditions<T>(schema: IEntitySchema<T>, conditions: {
68
+ property: keyof T;
69
+ value: unknown;
70
+ }[] | undefined): void;
52
71
  /**
53
72
  * Deep-clone condition tree and normalise null/undefined to undefined on Equals/NotEquals leaves
54
73
  * so in-memory evaluation matches stored-absent semantics (optional absent props are omitted/undefined).
package/docs/changelog.md CHANGED
@@ -1,5 +1,19 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.1-next.7](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-models-v0.9.1-next.6...entity-storage-models-v0.9.1-next.7) (2026-07-02)
4
+
5
+
6
+ ### Miscellaneous Chores
7
+
8
+ * **entity-storage-models:** Synchronize repo versions
9
+
10
+ ## [0.9.1-next.6](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-models-v0.9.1-next.5...entity-storage-models-v0.9.1-next.6) (2026-07-01)
11
+
12
+
13
+ ### Features
14
+
15
+ * input validation ([#162](https://github.com/iotaledger/twin-entity-storage/issues/162)) ([3e1e428](https://github.com/iotaledger/twin-entity-storage/commit/3e1e42887955cf079efd5989e197ddf8e0fa8c47))
16
+
3
17
  ## [0.9.1-next.5](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-models-v0.9.1-next.4...entity-storage-models-v0.9.1-next.5) (2026-06-30)
4
18
 
5
19
 
@@ -181,6 +181,83 @@ GeneralError If a property does not exist in the schema.
181
181
 
182
182
  ***
183
183
 
184
+ ### validateConditionProperties() {#validateconditionproperties}
185
+
186
+ > `static` **validateConditionProperties**\<`T`\>(`schema`, `condition`): `void`
187
+
188
+ Validate that every leaf property in an EntityCondition tree is a recognised schema property.
189
+ The root part of a dot-notation path must exist in the schema and be of type object or array.
190
+
191
+ #### Type Parameters
192
+
193
+ ##### T
194
+
195
+ `T`
196
+
197
+ #### Parameters
198
+
199
+ ##### schema
200
+
201
+ `IEntitySchema`\<`T`\>
202
+
203
+ The entity schema to validate against.
204
+
205
+ ##### condition
206
+
207
+ `EntityCondition`\<`T`\> \| `undefined`
208
+
209
+ The condition tree to validate, may be undefined.
210
+
211
+ #### Returns
212
+
213
+ `void`
214
+
215
+ #### Throws
216
+
217
+ GeneralError with message key "unknownPropertyInConditionProperty" if the root property is not in the schema.
218
+
219
+ #### Throws
220
+
221
+ GeneralError with message key "invalidConditionPropertyPath" if dot-notation is used on a non-object/array property.
222
+
223
+ ***
224
+
225
+ ### validateConditions() {#validateconditions}
226
+
227
+ > `static` **validateConditions**\<`T`\>(`schema`, `conditions`): `void`
228
+
229
+ Validate that every property in a conditions array is a recognised schema property.
230
+
231
+ #### Type Parameters
232
+
233
+ ##### T
234
+
235
+ `T`
236
+
237
+ #### Parameters
238
+
239
+ ##### schema
240
+
241
+ `IEntitySchema`\<`T`\>
242
+
243
+ The entity schema to validate against.
244
+
245
+ ##### conditions
246
+
247
+ `object`[] \| `undefined`
248
+
249
+ The conditions array to validate, may be undefined.
250
+
251
+ #### Returns
252
+
253
+ `void`
254
+
255
+ #### Throws
256
+
257
+ GeneralError with message key "unknownPropertyInConditions" if validation fails.
258
+
259
+ ***
260
+
184
261
  ### normalizeConditionValues() {#normalizeconditionvalues}
185
262
 
186
263
  > `static` **normalizeConditionValues**\<`T`\>(`condition`): `EntityCondition`\<`T`\>
package/locales/en.json CHANGED
@@ -2,7 +2,10 @@
2
2
  "error": {
3
3
  "entityStorageHelper": {
4
4
  "sortNotIndexed": "The property \"{property}\" is not indexed and cannot be used for sorting",
5
- "propertyNotInSchema": "The property \"{property}\" does not exist in the schema and cannot be used for projection"
5
+ "propertyNotInSchema": "The property \"{property}\" does not exist in the schema and cannot be used for projection",
6
+ "unknownPropertyInConditionProperty": "The property \"{property}\" is not a recognised schema property and cannot be used as a condition property",
7
+ "unknownPropertyInConditions": "The property \"{property}\" is not a recognised schema property and cannot be used in conditions",
8
+ "invalidConditionPropertyPath": "The property \"{property}\" has an invalid path: the root must be an object or array type, segments must not exceed 128 characters, and must not contain SQL metacharacters or control characters"
6
9
  },
7
10
  "migrationHelper": {
8
11
  "migrateSchemaFailed": "Migration failed for schema \"{schemaName}\".",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twin.org/entity-storage-models",
3
- "version": "0.9.1-next.5",
3
+ "version": "0.9.1-next.7",
4
4
  "description": "Shared models for storage contracts, requests, responses and connector capabilities.",
5
5
  "repository": {
6
6
  "type": "git",