@twin.org/entity-storage-models 0.0.3-next.16 → 0.0.3-next.18

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.
@@ -5,26 +5,34 @@ import { ComparisonOperator, EntitySchemaHelper } from "@twin.org/entity";
5
5
  /**
6
6
  * Helper class for performing schema migrations between two connectors.
7
7
  */
8
- export class EntityHelper {
8
+ export class EntityStorageHelper {
9
9
  /**
10
10
  * Runtime name for the class.
11
11
  */
12
- static CLASS_NAME = "EntityHelper";
12
+ static CLASS_NAME = "EntityStorageHelper";
13
13
  /**
14
14
  * Prepare the entity by handling undefined and null values and validating it against the schema.
15
15
  * @param entity The entity to handle undefined and null values for.
16
16
  * @param schema The schema to validate the entity against.
17
17
  * @param additionalProperties Optional list of additional properties to set on the entity.
18
+ * @param options Options controlling how null/undefined optional properties are stored.
19
+ * @param options.nullBehavior "omit" strips null/undefined optional properties before writing
20
+ * (NoSQL — avoids index-key type errors). "nullify" converts undefined to null (SQL — the default).
18
21
  * @returns The entity with undefined and null values handled.
19
22
  */
20
- static prepareEntity(entity, schema, additionalProperties) {
23
+ static prepareEntity(entity, schema, additionalProperties, options) {
21
24
  const entityForValidation = ObjectHelper.clone(entity);
22
25
  EntitySchemaHelper.validateEntity(entityForValidation, schema);
23
26
  if (Is.arrayValue(schema.properties)) {
24
27
  for (const property of schema.properties) {
25
28
  if (property.optional ?? false) {
26
29
  const propValue = entityForValidation[property.property];
27
- if (propValue === undefined) {
30
+ if (options?.nullBehavior === "omit") {
31
+ if (propValue === undefined || propValue === null) {
32
+ ObjectHelper.propertyDelete(entityForValidation, property.property);
33
+ }
34
+ }
35
+ else if (propValue === undefined) {
28
36
  ObjectHelper.propertySet(entityForValidation, property.property, null);
29
37
  }
30
38
  }
@@ -53,8 +61,8 @@ export class EntityHelper {
53
61
  return nonNullEntity;
54
62
  }
55
63
  /**
56
- * Deep-clone condition tree and map `undefined` to `null` on Equals/NotEquals leaves
57
- * so in-memory evaluation matches stored-null semantics (optional absent props are stored as null).
64
+ * Deep-clone condition tree and normalise null/undefined to undefined on Equals/NotEquals leaves
65
+ * so in-memory evaluation matches stored-absent semantics (optional absent props are omitted/undefined).
58
66
  * @param condition The user-supplied condition (not mutated).
59
67
  * @returns A clone safe to pass to check.
60
68
  */
@@ -62,16 +70,16 @@ export class EntityHelper {
62
70
  if ("conditions" in condition) {
63
71
  return {
64
72
  ...condition,
65
- conditions: condition.conditions.map(c => EntityHelper.normalizeConditionValues(c))
73
+ conditions: condition.conditions.map(c => EntityStorageHelper.normalizeConditionValues(c))
66
74
  };
67
75
  }
68
76
  const leaf = condition;
69
77
  if ((leaf.comparison === ComparisonOperator.Equals ||
70
78
  leaf.comparison === ComparisonOperator.NotEquals) &&
71
- leaf.value === undefined) {
72
- return { ...leaf, value: null };
79
+ (leaf.value === undefined || leaf.value === null)) {
80
+ return { ...leaf, value: undefined };
73
81
  }
74
82
  return { ...leaf };
75
83
  }
76
84
  }
77
- //# sourceMappingURL=entityHelper.js.map
85
+ //# sourceMappingURL=entityStorageHelper.js.map
@@ -0,0 +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 { Is, ObjectHelper } from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntitySchemaHelper,\n\ttype IEntitySchema\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 * 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"]}
package/dist/es/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  // Copyright 2024 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  export * from "./factories/entityStorageConnectorFactory.js";
4
- export * from "./helpers/entityHelper.js";
4
+ export * from "./helpers/entityStorageHelper.js";
5
5
  export * from "./helpers/migrationHelper.js";
6
6
  export * from "./models/api/IEntityStorageCountRequest.js";
7
7
  export * from "./models/api/IEntityStorageCountResponse.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,8CAA8C,CAAC;AAC7D,cAAc,2BAA2B,CAAC;AAC1C,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4CAA4C,CAAC;AAC3D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,kDAAkD,CAAC;AACjE,cAAc,6CAA6C,CAAC;AAC5D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,+BAA+B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./factories/entityStorageConnectorFactory.js\";\nexport * from \"./helpers/entityHelper.js\";\nexport * from \"./helpers/migrationHelper.js\";\nexport * from \"./models/api/IEntityStorageCountRequest.js\";\nexport * from \"./models/api/IEntityStorageCountResponse.js\";\nexport * from \"./models/api/IEntityStorageEmptyRequest.js\";\nexport * from \"./models/api/IEntityStorageGetRequest.js\";\nexport * from \"./models/api/IEntityStorageGetResponse.js\";\nexport * from \"./models/api/IEntityStorageListRequest.js\";\nexport * from \"./models/api/IEntityStorageListResponse.js\";\nexport * from \"./models/api/IEntityStorageRemoveBatchRequest.js\";\nexport * from \"./models/api/IEntityStorageRemoveRequest.js\";\nexport * from \"./models/api/IEntityStorageSetBatchRequest.js\";\nexport * from \"./models/api/IEntityStorageSetRequest.js\";\nexport * from \"./models/IEntityStorageComponent.js\";\nexport * from \"./models/IEntityStorageConnector.js\";\nexport * from \"./models/IEntityStorageMigrationConnector.js\";\nexport * from \"./models/IMigrationOptions.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,8CAA8C,CAAC;AAC7D,cAAc,kCAAkC,CAAC;AACjD,cAAc,8BAA8B,CAAC;AAC7C,cAAc,4CAA4C,CAAC;AAC3D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,0CAA0C,CAAC;AACzD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,kDAAkD,CAAC;AACjE,cAAc,6CAA6C,CAAC;AAC5D,cAAc,+CAA+C,CAAC;AAC9D,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,+BAA+B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./factories/entityStorageConnectorFactory.js\";\nexport * from \"./helpers/entityStorageHelper.js\";\nexport * from \"./helpers/migrationHelper.js\";\nexport * from \"./models/api/IEntityStorageCountRequest.js\";\nexport * from \"./models/api/IEntityStorageCountResponse.js\";\nexport * from \"./models/api/IEntityStorageEmptyRequest.js\";\nexport * from \"./models/api/IEntityStorageGetRequest.js\";\nexport * from \"./models/api/IEntityStorageGetResponse.js\";\nexport * from \"./models/api/IEntityStorageListRequest.js\";\nexport * from \"./models/api/IEntityStorageListResponse.js\";\nexport * from \"./models/api/IEntityStorageRemoveBatchRequest.js\";\nexport * from \"./models/api/IEntityStorageRemoveRequest.js\";\nexport * from \"./models/api/IEntityStorageSetBatchRequest.js\";\nexport * from \"./models/api/IEntityStorageSetRequest.js\";\nexport * from \"./models/IEntityStorageComponent.js\";\nexport * from \"./models/IEntityStorageConnector.js\";\nexport * from \"./models/IEntityStorageMigrationConnector.js\";\nexport * from \"./models/IMigrationOptions.js\";\n"]}
@@ -2,7 +2,7 @@ import { type EntityCondition, type IEntitySchema } from "@twin.org/entity";
2
2
  /**
3
3
  * Helper class for performing schema migrations between two connectors.
4
4
  */
5
- export declare class EntityHelper {
5
+ export declare class EntityStorageHelper {
6
6
  /**
7
7
  * Runtime name for the class.
8
8
  */
@@ -12,12 +12,17 @@ export declare class EntityHelper {
12
12
  * @param entity The entity to handle undefined and null values for.
13
13
  * @param schema The schema to validate the entity against.
14
14
  * @param additionalProperties Optional list of additional properties to set on the entity.
15
+ * @param options Options controlling how null/undefined optional properties are stored.
16
+ * @param options.nullBehavior "omit" strips null/undefined optional properties before writing
17
+ * (NoSQL — avoids index-key type errors). "nullify" converts undefined to null (SQL — the default).
15
18
  * @returns The entity with undefined and null values handled.
16
19
  */
17
20
  static prepareEntity<T>(entity: T, schema: IEntitySchema<T>, additionalProperties?: {
18
21
  property: string;
19
22
  value: unknown;
20
- }[]): T;
23
+ }[], options?: {
24
+ nullBehavior?: "omit" | "nullify";
25
+ }): T;
21
26
  /**
22
27
  * Un-prepare the entity by removing null values.
23
28
  * @param entity The entity to handle undefined and null values for.
@@ -26,8 +31,8 @@ export declare class EntityHelper {
26
31
  */
27
32
  static unPrepareEntity<T>(entity: Partial<T> | undefined, removeProperties?: string[]): T;
28
33
  /**
29
- * Deep-clone condition tree and map `undefined` to `null` on Equals/NotEquals leaves
30
- * so in-memory evaluation matches stored-null semantics (optional absent props are stored as null).
34
+ * Deep-clone condition tree and normalise null/undefined to undefined on Equals/NotEquals leaves
35
+ * so in-memory evaluation matches stored-absent semantics (optional absent props are omitted/undefined).
31
36
  * @param condition The user-supplied condition (not mutated).
32
37
  * @returns A clone safe to pass to check.
33
38
  */
@@ -1,5 +1,5 @@
1
1
  export * from "./factories/entityStorageConnectorFactory.js";
2
- export * from "./helpers/entityHelper.js";
2
+ export * from "./helpers/entityStorageHelper.js";
3
3
  export * from "./helpers/migrationHelper.js";
4
4
  export * from "./models/api/IEntityStorageCountRequest.js";
5
5
  export * from "./models/api/IEntityStorageCountResponse.js";
package/docs/changelog.md CHANGED
@@ -1,5 +1,35 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.0.3-next.18](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-models-v0.0.3-next.17...entity-storage-models-v0.0.3-next.18) (2026-06-01)
4
+
5
+
6
+ ### Features
7
+
8
+ * add context id features ([#55](https://github.com/iotaledger/twin-entity-storage/issues/55)) ([99c15a2](https://github.com/iotaledger/twin-entity-storage/commit/99c15a257539b61d9da63649ce573ebf47699fc9))
9
+ * add production release automation ([1eb4c8e](https://github.com/iotaledger/twin-entity-storage/commit/1eb4c8ee3eb099defdfc2d063ae44935276dcae8))
10
+ * add validate-locales ([e66ef0d](https://github.com/iotaledger/twin-entity-storage/commit/e66ef0de26ca2f82b3fe89bb5c7a15a0978a9644))
11
+ * adding schema migration functionality to all the connectors ([#85](https://github.com/iotaledger/twin-entity-storage/issues/85)) ([fd1555a](https://github.com/iotaledger/twin-entity-storage/commit/fd1555a34380158214a577586dafae821e72a578))
12
+ * entity storage enhancements ([#86](https://github.com/iotaledger/twin-entity-storage/issues/86)) ([1279af4](https://github.com/iotaledger/twin-entity-storage/commit/1279af42615c6497bb06539842cee44842dd1f75))
13
+ * eslint migration to flat config ([f033b64](https://github.com/iotaledger/twin-entity-storage/commit/f033b64984c0e6a8129d929c9dd816dcc1b8dab0))
14
+ * remove includeNodeIdentity flag ([d88d1d0](https://github.com/iotaledger/twin-entity-storage/commit/d88d1d0694419b795dc860e0b712a0051c9a1c9e))
15
+ * typescript 6 update ([995a0c6](https://github.com/iotaledger/twin-entity-storage/commit/995a0c6fa9a6813bfdc7200779ce3664236e59e9))
16
+ * update dependencies ([7ccc0c4](https://github.com/iotaledger/twin-entity-storage/commit/7ccc0c429125d073dc60b3de6cf101abc8cc6cba))
17
+ * update framework core ([b59a380](https://github.com/iotaledger/twin-entity-storage/commit/b59a380bb7fba2b43610f69074dcdee24a4737da))
18
+ * use shared store mechanism ([#34](https://github.com/iotaledger/twin-entity-storage/issues/34)) ([68b6b71](https://github.com/iotaledger/twin-entity-storage/commit/68b6b71e7a96d7d016cd57bfff36775b56bf3f93))
19
+
20
+
21
+ ### Bug Fixes
22
+
23
+ * null secondary indexes ([#103](https://github.com/iotaledger/twin-entity-storage/issues/103)) ([5e44f11](https://github.com/iotaledger/twin-entity-storage/commit/5e44f11bb5af5bf2c27d6f1d56aba5851116ff89))
24
+ * query params force coercion ([dd6aa87](https://github.com/iotaledger/twin-entity-storage/commit/dd6aa87efdfb60bab7d6756a86888863c45c51a7))
25
+
26
+ ## [0.0.3-next.17](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-models-v0.0.3-next.16...entity-storage-models-v0.0.3-next.17) (2026-06-01)
27
+
28
+
29
+ ### Bug Fixes
30
+
31
+ * null secondary indexes ([#103](https://github.com/iotaledger/twin-entity-storage/issues/103)) ([5e44f11](https://github.com/iotaledger/twin-entity-storage/commit/5e44f11bb5af5bf2c27d6f1d56aba5851116ff89))
32
+
3
33
  ## [0.0.3-next.16](https://github.com/iotaledger/twin-entity-storage/compare/entity-storage-models-v0.0.3-next.15...entity-storage-models-v0.0.3-next.16) (2026-05-20)
4
34
 
5
35
 
@@ -1,4 +1,4 @@
1
- # Class: EntityHelper
1
+ # Class: EntityStorageHelper
2
2
 
3
3
  Helper class for performing schema migrations between two connectors.
4
4
 
@@ -6,11 +6,11 @@ Helper class for performing schema migrations between two connectors.
6
6
 
7
7
  ### Constructor
8
8
 
9
- > **new EntityHelper**(): `EntityHelper`
9
+ > **new EntityStorageHelper**(): `EntityStorageHelper`
10
10
 
11
11
  #### Returns
12
12
 
13
- `EntityHelper`
13
+ `EntityStorageHelper`
14
14
 
15
15
  ## Properties
16
16
 
@@ -24,7 +24,7 @@ Runtime name for the class.
24
24
 
25
25
  ### prepareEntity() {#prepareentity}
26
26
 
27
- > `static` **prepareEntity**\<`T`\>(`entity`, `schema`, `additionalProperties?`): `T`
27
+ > `static` **prepareEntity**\<`T`\>(`entity`, `schema`, `additionalProperties?`, `options?`): `T`
28
28
 
29
29
  Prepare the entity by handling undefined and null values and validating it against the schema.
30
30
 
@@ -54,6 +54,17 @@ The schema to validate the entity against.
54
54
 
55
55
  Optional list of additional properties to set on the entity.
56
56
 
57
+ ##### options?
58
+
59
+ Options controlling how null/undefined optional properties are stored.
60
+
61
+ ###### nullBehavior?
62
+
63
+ `"omit"` \| `"nullify"`
64
+
65
+ "omit" strips null/undefined optional properties before writing
66
+ (NoSQL — avoids index-key type errors). "nullify" converts undefined to null (SQL — the default).
67
+
57
68
  #### Returns
58
69
 
59
70
  `T`
@@ -100,8 +111,8 @@ The entity with undefined and null values handled.
100
111
 
101
112
  > `static` **normalizeConditionValues**\<`T`\>(`condition`): `EntityCondition`\<`T`\>
102
113
 
103
- Deep-clone condition tree and map `undefined` to `null` on Equals/NotEquals leaves
104
- so in-memory evaluation matches stored-null semantics (optional absent props are stored as null).
114
+ Deep-clone condition tree and normalise null/undefined to undefined on Equals/NotEquals leaves
115
+ so in-memory evaluation matches stored-absent semantics (optional absent props are omitted/undefined).
105
116
 
106
117
  #### Type Parameters
107
118
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## Classes
4
4
 
5
- - [EntityHelper](classes/EntityHelper.md)
5
+ - [EntityStorageHelper](classes/EntityStorageHelper.md)
6
6
  - [MigrationHelper](classes/MigrationHelper.md)
7
7
 
8
8
  ## Interfaces
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@twin.org/entity-storage-models",
3
- "version": "0.0.3-next.16",
3
+ "version": "0.0.3-next.18",
4
4
  "description": "Shared models for storage contracts, requests, responses and connector capabilities.",
5
5
  "repository": {
6
6
  "type": "git",
7
- "url": "git+https://github.com/iotaledger/entity-storage.git",
7
+ "url": "git+https://github.com/iotaledger/twin-entity-storage.git",
8
8
  "directory": "packages/entity-storage-models"
9
9
  },
10
10
  "author": "martyn.janes@iota.org",
@@ -51,7 +51,7 @@
51
51
  "schemas"
52
52
  ],
53
53
  "bugs": {
54
- "url": "git+https://github.com/iotaledger/entity-storage/issues"
54
+ "url": "git+https://github.com/iotaledger/twin-entity-storage/issues"
55
55
  },
56
56
  "homepage": "https://twindev.org"
57
57
  }
@@ -1 +0,0 @@
1
- {"version":3,"file":"entityHelper.js","sourceRoot":"","sources":["../../../src/helpers/entityHelper.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,EAAE,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EACN,kBAAkB,EAElB,kBAAkB,EAElB,MAAM,kBAAkB,CAAC;AAG1B;;GAEG;AACH,MAAM,OAAO,YAAY;IACxB;;OAEG;IACI,MAAM,CAAU,UAAU,kBAAkC;IAEnE;;;;;;OAMG;IACI,MAAM,CAAC,aAAa,CAC1B,MAAS,EACT,MAAwB,EACxB,oBAA6D;QAE7D,MAAM,mBAAmB,GAAG,YAAY,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC;QACvD,kBAAkB,CAAC,cAAc,CAAC,mBAAmB,EAAE,MAAM,CAAC,CAAC;QAE/D,IAAI,EAAE,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,EAAE,CAAC;YACtC,KAAK,MAAM,QAAQ,IAAI,MAAM,CAAC,UAAU,EAAE,CAAC;gBAC1C,IAAI,QAAQ,CAAC,QAAQ,IAAI,KAAK,EAAE,CAAC;oBAChC,MAAM,SAAS,GAAG,mBAAmB,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;oBACzD,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;wBAC7B,YAAY,CAAC,WAAW,CAAC,mBAAmB,EAAE,QAAQ,CAAC,QAAkB,EAAE,IAAI,CAAC,CAAC;oBAClF,CAAC;gBACF,CAAC;YACF,CAAC;QACF,CAAC;QAED,IAAI,EAAE,CAAC,UAAU,CAAC,oBAAoB,CAAC,EAAE,CAAC;YACzC,KAAK,MAAM,kBAAkB,IAAI,oBAAoB,EAAE,CAAC;gBACvD,YAAY,CAAC,WAAW,CACvB,mBAAmB,EACnB,kBAAkB,CAAC,QAAQ,EAC3B,kBAAkB,CAAC,KAAK,CACxB,CAAC;YACH,CAAC;QACF,CAAC;QAED,OAAO,mBAAmB,CAAC;IAC5B,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,eAAe,CAAI,MAA8B,EAAE,gBAA2B;QAC3F,MAAM,aAAa,GAAG,YAAY,CAAC,qBAAqB,CAAC,MAAM,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC,CAAC;QAEvF,IAAI,EAAE,CAAC,UAAU,CAAC,gBAAgB,CAAC,EAAE,CAAC;YACrC,KAAK,MAAM,QAAQ,IAAI,gBAAgB,EAAE,CAAC;gBACzC,YAAY,CAAC,cAAc,CAAC,aAAa,EAAE,QAAQ,CAAC,CAAC;YACtD,CAAC;QACF,CAAC;QAED,OAAO,aAAkB,CAAC;IAC3B,CAAC;IAED;;;;;OAKG;IACI,MAAM,CAAC,wBAAwB,CAAI,SAA6B;QACtE,IAAI,YAAY,IAAI,SAAS,EAAE,CAAC;YAC/B,OAAO;gBACN,GAAG,SAAS;gBACZ,UAAU,EAAE,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,YAAY,CAAC,wBAAwB,CAAC,CAAC,CAAC,CAAC;aACnF,CAAC;QACH,CAAC;QAED,MAAM,IAAI,GAAG,SAAS,CAAC;QACvB,IACC,CAAC,IAAI,CAAC,UAAU,KAAK,kBAAkB,CAAC,MAAM;YAC7C,IAAI,CAAC,UAAU,KAAK,kBAAkB,CAAC,SAAS,CAAC;YAClD,IAAI,CAAC,KAAK,KAAK,SAAS,EACvB,CAAC;YACF,OAAO,EAAE,GAAG,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC;QACjC,CAAC;QACD,OAAO,EAAE,GAAG,IAAI,EAAE,CAAC;IACpB,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Is, ObjectHelper } from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntitySchemaHelper,\n\ttype IEntitySchema\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 EntityHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntityHelper>();\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 * @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): 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 (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 * Deep-clone condition tree and map `undefined` to `null` on Equals/NotEquals leaves\n\t * so in-memory evaluation matches stored-null semantics (optional absent props are stored as null).\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 => EntityHelper.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\tleaf.value === undefined\n\t\t) {\n\t\t\treturn { ...leaf, value: null };\n\t\t}\n\t\treturn { ...leaf };\n\t}\n}\n"]}