@twin.org/entity 0.10.0 → 0.10.1-next.10

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.
Files changed (34) hide show
  1. package/dist/es/decorators/propertyDecorator.js +25 -0
  2. package/dist/es/decorators/propertyDecorator.js.map +1 -1
  3. package/dist/es/index.js +1 -0
  4. package/dist/es/index.js.map +1 -1
  5. package/dist/es/models/IEntitySchemaProperty.js.map +1 -1
  6. package/dist/es/models/IEntitySchemaPropertyIndex.js +2 -0
  7. package/dist/es/models/IEntitySchemaPropertyIndex.js.map +1 -0
  8. package/dist/es/models/comparisonOperator.js +6 -1
  9. package/dist/es/models/comparisonOperator.js.map +1 -1
  10. package/dist/es/utils/entityConditions.js +122 -125
  11. package/dist/es/utils/entityConditions.js.map +1 -1
  12. package/dist/es/utils/entitySchemaDiffHelper.js +27 -1
  13. package/dist/es/utils/entitySchemaDiffHelper.js.map +1 -1
  14. package/dist/es/utils/entitySchemaHelper.js +65 -0
  15. package/dist/es/utils/entitySchemaHelper.js.map +1 -1
  16. package/dist/es/utils/entitySorter.js +2 -2
  17. package/dist/es/utils/entitySorter.js.map +1 -1
  18. package/dist/types/decorators/propertyDecorator.d.ts +2 -0
  19. package/dist/types/index.d.ts +1 -0
  20. package/dist/types/models/IEntitySchemaProperty.d.ts +7 -0
  21. package/dist/types/models/IEntitySchemaPropertyIndex.d.ts +18 -0
  22. package/dist/types/models/comparisonOperator.d.ts +6 -1
  23. package/dist/types/utils/entitySchemaDiffHelper.d.ts +1 -1
  24. package/dist/types/utils/entitySchemaHelper.d.ts +17 -1
  25. package/docs/changelog.md +263 -0
  26. package/docs/reference/classes/EntitySchemaDiffHelper.md +1 -1
  27. package/docs/reference/classes/EntitySchemaHelper.md +36 -0
  28. package/docs/reference/functions/property.md +8 -0
  29. package/docs/reference/index.md +1 -0
  30. package/docs/reference/interfaces/IEntitySchemaProperty.md +10 -0
  31. package/docs/reference/interfaces/IEntitySchemaPropertyIndex.md +27 -0
  32. package/docs/reference/variables/ComparisonOperator.md +8 -1
  33. package/locales/en.json +8 -0
  34. package/package.json +3 -3
@@ -3,14 +3,39 @@
3
3
  /* eslint-disable @typescript-eslint/no-explicit-any */
4
4
  import "reflect-metadata";
5
5
  import "tslib";
6
+ import { GeneralError, Is } from "@twin.org/core";
7
+ import { EntitySchemaPropertyType } from "../models/entitySchemaPropertyType.js";
6
8
  import { DecoratorHelper } from "../utils/decoratorHelper.js";
7
9
  /**
8
10
  * Decorator to produce schema property data for entities.
9
11
  * @param options The options for the property.
10
12
  * @returns The property decorator.
13
+ * @throws GeneralError if an index group is declared on an object or array property.
14
+ * @throws GeneralError if the same index group name is declared more than once for the property.
11
15
  */
12
16
  export function property(options) {
13
17
  return (target, propertyKey) => {
18
+ if (Is.arrayValue(options.indexGroup)) {
19
+ if (options.type === EntitySchemaPropertyType.Object ||
20
+ options.type === EntitySchemaPropertyType.Array) {
21
+ throw new GeneralError("propertyDecorator", "indexGroupTypeNotSupported", {
22
+ property: propertyKey,
23
+ type: options.type
24
+ });
25
+ }
26
+ const seenGroups = new Set();
27
+ for (const propertyIndex of options.indexGroup) {
28
+ if (Is.stringValue(propertyIndex?.name)) {
29
+ if (seenGroups.has(propertyIndex.name)) {
30
+ throw new GeneralError("propertyDecorator", "duplicateIndexGroup", {
31
+ property: propertyKey,
32
+ group: propertyIndex.name
33
+ });
34
+ }
35
+ seenGroups.add(propertyIndex.name);
36
+ }
37
+ }
38
+ }
14
39
  const entitySchema = DecoratorHelper.getSchema(target);
15
40
  entitySchema.properties ??= [];
16
41
  const idx = entitySchema.properties.findIndex(p => p.property === propertyKey);
@@ -1 +1 @@
1
- {"version":3,"file":"propertyDecorator.js","sourceRoot":"","sources":["../../../src/decorators/propertyDecorator.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,uDAAuD;AACvD,OAAO,kBAAkB,CAAC;AAC1B,OAAO,OAAO,CAAC;AAEf,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;GAIG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAgD;IACxE,OAAO,CAAC,MAAW,EAAE,WAAmB,EAAE,EAAE;QAC3C,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAM,MAAM,CAAC,CAAC;QAC5D,YAAY,CAAC,UAAU,KAAK,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,WAAW,CAAC,CAAC;QAC/E,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YAChB,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG;gBAC9B,GAAG,OAAO;gBACV,QAAQ,EAAE,WAAW;aACrB,CAAC;QACH,CAAC;aAAM,CAAC;YACP,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC;gBAC5B,GAAG,OAAO;gBACV,QAAQ,EAAE,WAAW;aACrB,CAAC,CAAC;QACJ,CAAC;QACD,eAAe,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjD,CAAC,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport \"reflect-metadata\";\nimport \"tslib\";\nimport type { IEntitySchemaProperty } from \"../models/IEntitySchemaProperty.js\";\nimport { DecoratorHelper } from \"../utils/decoratorHelper.js\";\n\n/**\n * Decorator to produce schema property data for entities.\n * @param options The options for the property.\n * @returns The property decorator.\n */\nexport function property(options: Omit<IEntitySchemaProperty, \"property\">): any {\n\treturn (target: any, propertyKey: string) => {\n\t\tconst entitySchema = DecoratorHelper.getSchema<any>(target);\n\t\tentitySchema.properties ??= [];\n\t\tconst idx = entitySchema.properties.findIndex(p => p.property === propertyKey);\n\t\tif (idx !== -1) {\n\t\t\tentitySchema.properties[idx] = {\n\t\t\t\t...options,\n\t\t\t\tproperty: propertyKey\n\t\t\t};\n\t\t} else {\n\t\t\tentitySchema.properties.push({\n\t\t\t\t...options,\n\t\t\t\tproperty: propertyKey\n\t\t\t});\n\t\t}\n\t\tDecoratorHelper.setSchema(target, entitySchema);\n\t};\n}\n"]}
1
+ {"version":3,"file":"propertyDecorator.js","sourceRoot":"","sources":["../../../src/decorators/propertyDecorator.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,uDAAuD;AACvD,OAAO,kBAAkB,CAAC;AAC1B,OAAO,OAAO,CAAC;AACf,OAAO,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,gBAAgB,CAAC;AAClD,OAAO,EAAE,wBAAwB,EAAE,MAAM,uCAAuC,CAAC;AAEjF,OAAO,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;;;;;GAMG;AACH,MAAM,UAAU,QAAQ,CAAC,OAAgD;IACxE,OAAO,CAAC,MAAW,EAAE,WAAmB,EAAE,EAAE;QAC3C,IAAI,EAAE,CAAC,UAAU,CAAC,OAAO,CAAC,UAAU,CAAC,EAAE,CAAC;YACvC,IACC,OAAO,CAAC,IAAI,KAAK,wBAAwB,CAAC,MAAM;gBAChD,OAAO,CAAC,IAAI,KAAK,wBAAwB,CAAC,KAAK,EAC9C,CAAC;gBACF,MAAM,IAAI,YAAY,CAAC,mBAAmB,EAAE,4BAA4B,EAAE;oBACzE,QAAQ,EAAE,WAAW;oBACrB,IAAI,EAAE,OAAO,CAAC,IAAI;iBAClB,CAAC,CAAC;YACJ,CAAC;YAED,MAAM,UAAU,GAAG,IAAI,GAAG,EAAU,CAAC;YACrC,KAAK,MAAM,aAAa,IAAI,OAAO,CAAC,UAAU,EAAE,CAAC;gBAChD,IAAI,EAAE,CAAC,WAAW,CAAC,aAAa,EAAE,IAAI,CAAC,EAAE,CAAC;oBACzC,IAAI,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,EAAE,CAAC;wBACxC,MAAM,IAAI,YAAY,CAAC,mBAAmB,EAAE,qBAAqB,EAAE;4BAClE,QAAQ,EAAE,WAAW;4BACrB,KAAK,EAAE,aAAa,CAAC,IAAI;yBACzB,CAAC,CAAC;oBACJ,CAAC;oBACD,UAAU,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC;gBACpC,CAAC;YACF,CAAC;QACF,CAAC;QAED,MAAM,YAAY,GAAG,eAAe,CAAC,SAAS,CAAM,MAAM,CAAC,CAAC;QAC5D,YAAY,CAAC,UAAU,KAAK,EAAE,CAAC;QAC/B,MAAM,GAAG,GAAG,YAAY,CAAC,UAAU,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,WAAW,CAAC,CAAC;QAC/E,IAAI,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;YAChB,YAAY,CAAC,UAAU,CAAC,GAAG,CAAC,GAAG;gBAC9B,GAAG,OAAO;gBACV,QAAQ,EAAE,WAAW;aACrB,CAAC;QACH,CAAC;aAAM,CAAC;YACP,YAAY,CAAC,UAAU,CAAC,IAAI,CAAC;gBAC5B,GAAG,OAAO;gBACV,QAAQ,EAAE,WAAW;aACrB,CAAC,CAAC;QACJ,CAAC;QACD,eAAe,CAAC,SAAS,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IACjD,CAAC,CAAC;AACH,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n/* eslint-disable @typescript-eslint/no-explicit-any */\nimport \"reflect-metadata\";\nimport \"tslib\";\nimport { GeneralError, Is } from \"@twin.org/core\";\nimport { EntitySchemaPropertyType } from \"../models/entitySchemaPropertyType.js\";\nimport type { IEntitySchemaProperty } from \"../models/IEntitySchemaProperty.js\";\nimport { DecoratorHelper } from \"../utils/decoratorHelper.js\";\n\n/**\n * Decorator to produce schema property data for entities.\n * @param options The options for the property.\n * @returns The property decorator.\n * @throws GeneralError if an index group is declared on an object or array property.\n * @throws GeneralError if the same index group name is declared more than once for the property.\n */\nexport function property(options: Omit<IEntitySchemaProperty, \"property\">): any {\n\treturn (target: any, propertyKey: string) => {\n\t\tif (Is.arrayValue(options.indexGroup)) {\n\t\t\tif (\n\t\t\t\toptions.type === EntitySchemaPropertyType.Object ||\n\t\t\t\toptions.type === EntitySchemaPropertyType.Array\n\t\t\t) {\n\t\t\t\tthrow new GeneralError(\"propertyDecorator\", \"indexGroupTypeNotSupported\", {\n\t\t\t\t\tproperty: propertyKey,\n\t\t\t\t\ttype: options.type\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tconst seenGroups = new Set<string>();\n\t\t\tfor (const propertyIndex of options.indexGroup) {\n\t\t\t\tif (Is.stringValue(propertyIndex?.name)) {\n\t\t\t\t\tif (seenGroups.has(propertyIndex.name)) {\n\t\t\t\t\t\tthrow new GeneralError(\"propertyDecorator\", \"duplicateIndexGroup\", {\n\t\t\t\t\t\t\tproperty: propertyKey,\n\t\t\t\t\t\t\tgroup: propertyIndex.name\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tseenGroups.add(propertyIndex.name);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tconst entitySchema = DecoratorHelper.getSchema<any>(target);\n\t\tentitySchema.properties ??= [];\n\t\tconst idx = entitySchema.properties.findIndex(p => p.property === propertyKey);\n\t\tif (idx !== -1) {\n\t\t\tentitySchema.properties[idx] = {\n\t\t\t\t...options,\n\t\t\t\tproperty: propertyKey\n\t\t\t};\n\t\t} else {\n\t\t\tentitySchema.properties.push({\n\t\t\t\t...options,\n\t\t\t\tproperty: propertyKey\n\t\t\t});\n\t\t}\n\t\tDecoratorHelper.setSchema(target, entitySchema);\n\t};\n}\n"]}
package/dist/es/index.js CHANGED
@@ -12,6 +12,7 @@ export * from "./models/IComparatorGroup.js";
12
12
  export * from "./models/IEntitySchema.js";
13
13
  export * from "./models/IEntitySchemaOptions.js";
14
14
  export * from "./models/IEntitySchemaProperty.js";
15
+ export * from "./models/IEntitySchemaPropertyIndex.js";
15
16
  export * from "./models/IEntitySort.js";
16
17
  export * from "./models/logicalOperator.js";
17
18
  export * from "./models/sortDirection.js";
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wCAAwC,CAAC;AACvD,cAAc,sCAAsC,CAAC;AACrD,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,yBAAyB,CAAC;AACxC,cAAc,+BAA+B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./decorators/entityDecorator.js\";\nexport * from \"./decorators/propertyDecorator.js\";\nexport * from \"./factories/entitySchemaFactory.js\";\nexport * from \"./models/comparisonOperator.js\";\nexport * from \"./models/entityCondition.js\";\nexport * from \"./models/entitySchemaPropertyFormat.js\";\nexport * from \"./models/entitySchemaPropertyType.js\";\nexport * from \"./models/IComparator.js\";\nexport * from \"./models/IComparatorGroup.js\";\nexport * from \"./models/IEntitySchema.js\";\nexport * from \"./models/IEntitySchemaOptions.js\";\nexport * from \"./models/IEntitySchemaProperty.js\";\nexport * from \"./models/IEntitySort.js\";\nexport * from \"./models/logicalOperator.js\";\nexport * from \"./models/sortDirection.js\";\nexport * from \"./utils/decoratorHelper.js\";\nexport * from \"./utils/entityConditions.js\";\nexport * from \"./utils/entitySchemaHelper.js\";\nexport * from \"./utils/entitySchemaDiffHelper.js\";\nexport * from \"./utils/entitySorter.js\";\nexport * from \"./models/IEntitySchemaDiff.js\";\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,cAAc,iCAAiC,CAAC;AAChD,cAAc,mCAAmC,CAAC;AAClD,cAAc,oCAAoC,CAAC;AACnD,cAAc,gCAAgC,CAAC;AAC/C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wCAAwC,CAAC;AACvD,cAAc,sCAAsC,CAAC;AACrD,cAAc,yBAAyB,CAAC;AACxC,cAAc,8BAA8B,CAAC;AAC7C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,kCAAkC,CAAC;AACjD,cAAc,mCAAmC,CAAC;AAClD,cAAc,wCAAwC,CAAC;AACvD,cAAc,yBAAyB,CAAC;AACxC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,2BAA2B,CAAC;AAC1C,cAAc,4BAA4B,CAAC;AAC3C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+BAA+B,CAAC;AAC9C,cAAc,mCAAmC,CAAC;AAClD,cAAc,yBAAyB,CAAC;AACxC,cAAc,+BAA+B,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./decorators/entityDecorator.js\";\nexport * from \"./decorators/propertyDecorator.js\";\nexport * from \"./factories/entitySchemaFactory.js\";\nexport * from \"./models/comparisonOperator.js\";\nexport * from \"./models/entityCondition.js\";\nexport * from \"./models/entitySchemaPropertyFormat.js\";\nexport * from \"./models/entitySchemaPropertyType.js\";\nexport * from \"./models/IComparator.js\";\nexport * from \"./models/IComparatorGroup.js\";\nexport * from \"./models/IEntitySchema.js\";\nexport * from \"./models/IEntitySchemaOptions.js\";\nexport * from \"./models/IEntitySchemaProperty.js\";\nexport * from \"./models/IEntitySchemaPropertyIndex.js\";\nexport * from \"./models/IEntitySort.js\";\nexport * from \"./models/logicalOperator.js\";\nexport * from \"./models/sortDirection.js\";\nexport * from \"./utils/decoratorHelper.js\";\nexport * from \"./utils/entityConditions.js\";\nexport * from \"./utils/entitySchemaHelper.js\";\nexport * from \"./utils/entitySchemaDiffHelper.js\";\nexport * from \"./utils/entitySorter.js\";\nexport * from \"./models/IEntitySchemaDiff.js\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IEntitySchemaProperty.js","sourceRoot":"","sources":["../../../src/models/IEntitySchemaProperty.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { EntitySchemaPropertyFormat } from \"./entitySchemaPropertyFormat.js\";\nimport type { EntitySchemaPropertyType } from \"./entitySchemaPropertyType.js\";\nimport type { SortDirection } from \"./sortDirection.js\";\n\n/**\n * Definition for an entity schema property.\n */\nexport interface IEntitySchemaProperty<T = unknown> {\n\t/**\n\t * The property name from the entity.\n\t */\n\tproperty: keyof T;\n\n\t/**\n\t * The type of the property.\n\t */\n\ttype: EntitySchemaPropertyType;\n\n\t/**\n\t * The format of the property.\n\t */\n\tformat?: EntitySchemaPropertyFormat;\n\n\t/**\n\t * The maximum length of the property value i.e. for text fields.\n\t */\n\tmaxLength?: number;\n\n\t/**\n\t * Is this the primary index property.\n\t */\n\tisPrimary?: boolean;\n\n\t/**\n\t * Is this a secondary index property.\n\t */\n\tisSecondary?: boolean;\n\n\t/**\n\t * Is this property used as the optimistic-lock version token.\n\t * When true, connectors automatically manage the field: the value is\n\t * incremented on every successful write, and a write is rejected with a\n\t * ConflictError when the submitted value does not match the stored value.\n\t * Must be an integer property.\n\t */\n\tisVersion?: boolean;\n\n\t/**\n\t * Default sort direction for this field, leave empty if not sortable.\n\t */\n\tsortDirection?: SortDirection;\n\n\t/**\n\t * Is the property optional.\n\t */\n\toptional?: boolean;\n\n\t/**\n\t * The type of the item (only applies when type is `array`).\n\t */\n\titemType?: EntitySchemaPropertyType;\n\n\t/**\n\t * The type ref of the item (only applies when type is either `array` or `object`).\n\t */\n\titemTypeRef?: string;\n\n\t/**\n\t * Description of the object.\n\t */\n\tdescription?: string;\n\n\t/**\n\t * Examples of the property values.\n\t */\n\texamples?: unknown[];\n\n\t/**\n\t * A default value which can be used in migrations when the property value is not provided.\n\t */\n\tdefaultValue?: unknown;\n}\n"]}
1
+ {"version":3,"file":"IEntitySchemaProperty.js","sourceRoot":"","sources":["../../../src/models/IEntitySchemaProperty.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { EntitySchemaPropertyFormat } from \"./entitySchemaPropertyFormat.js\";\nimport type { EntitySchemaPropertyType } from \"./entitySchemaPropertyType.js\";\nimport type { IEntitySchemaPropertyIndex } from \"./IEntitySchemaPropertyIndex.js\";\nimport type { SortDirection } from \"./sortDirection.js\";\n\n/**\n * Definition for an entity schema property.\n */\nexport interface IEntitySchemaProperty<T = unknown> {\n\t/**\n\t * The property name from the entity.\n\t */\n\tproperty: keyof T;\n\n\t/**\n\t * The type of the property.\n\t */\n\ttype: EntitySchemaPropertyType;\n\n\t/**\n\t * The format of the property.\n\t */\n\tformat?: EntitySchemaPropertyFormat;\n\n\t/**\n\t * The maximum length of the property value i.e. for text fields.\n\t */\n\tmaxLength?: number;\n\n\t/**\n\t * Is this the primary index property.\n\t */\n\tisPrimary?: boolean;\n\n\t/**\n\t * Is this a secondary index property.\n\t */\n\tisSecondary?: boolean;\n\n\t/**\n\t * The composite indexes this property is part of.\n\t * Connectors can use these to build a composite index for each group name,\n\t * combining all the properties which share that name, ordered by their index.\n\t */\n\tindexGroup?: IEntitySchemaPropertyIndex[];\n\n\t/**\n\t * Is this property used as the optimistic-lock version token.\n\t * When true, connectors automatically manage the field: the value is\n\t * incremented on every successful write, and a write is rejected with a\n\t * ConflictError when the submitted value does not match the stored value.\n\t * Must be an integer property.\n\t */\n\tisVersion?: boolean;\n\n\t/**\n\t * Default sort direction for this field, leave empty if not sortable.\n\t */\n\tsortDirection?: SortDirection;\n\n\t/**\n\t * Is the property optional.\n\t */\n\toptional?: boolean;\n\n\t/**\n\t * The type of the item (only applies when type is `array`).\n\t */\n\titemType?: EntitySchemaPropertyType;\n\n\t/**\n\t * The type ref of the item (only applies when type is either `array` or `object`).\n\t */\n\titemTypeRef?: string;\n\n\t/**\n\t * Description of the object.\n\t */\n\tdescription?: string;\n\n\t/**\n\t * Examples of the property values.\n\t */\n\texamples?: unknown[];\n\n\t/**\n\t * A default value which can be used in migrations when the property value is not provided.\n\t */\n\tdefaultValue?: unknown;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IEntitySchemaPropertyIndex.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IEntitySchemaPropertyIndex.js","sourceRoot":"","sources":["../../../src/models/IEntitySchemaPropertyIndex.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { SortDirection } from \"./sortDirection.js\";\n\n/**\n * Definition of a composite index that a property is part of.\n */\nexport interface IEntitySchemaPropertyIndex {\n\t/**\n\t * The name of the composite index group.\n\t */\n\tname: string;\n\n\t/**\n\t * The sort direction for the property within the index.\n\t */\n\tdirection: SortDirection;\n\n\t/**\n\t * The position of the property within the index, ordered ascending.\n\t */\n\tindex: number;\n}\n"]}
@@ -31,7 +31,7 @@ export const ComparisonOperator = {
31
31
  LessThanOrEqual: "less-than-or-equal",
32
32
  /**
33
33
  * Includes.
34
- * A string in a substring.
34
+ * A string in a substring, this is a scan and cannot use an index.
35
35
  * A set contains an element.
36
36
  * A list contains an element.
37
37
  */
@@ -43,6 +43,11 @@ export const ComparisonOperator = {
43
43
  * A list does not contain an element.
44
44
  */
45
45
  NotIncludes: "not-includes",
46
+ /**
47
+ * Starts With.
48
+ * A string begins with a prefix, the anchored match can use an index.
49
+ */
50
+ StartsWith: "starts-with",
46
51
  /**
47
52
  * In.
48
53
  * A element is in a set.
@@ -1 +1 @@
1
- {"version":3,"file":"comparisonOperator.js","sourceRoot":"","sources":["../../../src/models/comparisonOperator.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AAEvC;;GAEG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG;IACjC;;OAEG;IACH,MAAM,EAAE,QAAQ;IAEhB;;OAEG;IACH,SAAS,EAAE,YAAY;IAEvB;;OAEG;IACH,WAAW,EAAE,cAAc;IAE3B;;OAEG;IACH,kBAAkB,EAAE,uBAAuB;IAE3C;;OAEG;IACH,QAAQ,EAAE,WAAW;IAErB;;OAEG;IACH,eAAe,EAAE,oBAAoB;IAErC;;;;;OAKG;IACH,QAAQ,EAAE,UAAU;IAEpB;;;;;OAKG;IACH,WAAW,EAAE,cAAc;IAE3B;;;OAGG;IACH,EAAE,EAAE,IAAI;CACC,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The types of comparisons.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const ComparisonOperator = {\n\t/**\n\t * Equals.\n\t */\n\tEquals: \"equals\",\n\n\t/**\n\t * Not Equals.\n\t */\n\tNotEquals: \"not-equals\",\n\n\t/**\n\t * Greater Than.\n\t */\n\tGreaterThan: \"greater-than\",\n\n\t/**\n\t * Greater Than Or Equal.\n\t */\n\tGreaterThanOrEqual: \"greater-than-or-equal\",\n\n\t/**\n\t * Less Than.\n\t */\n\tLessThan: \"less-than\",\n\n\t/**\n\t * Less Than Or Equal.\n\t */\n\tLessThanOrEqual: \"less-than-or-equal\",\n\n\t/**\n\t * Includes.\n\t * A string in a substring.\n\t * A set contains an element.\n\t * A list contains an element.\n\t */\n\tIncludes: \"includes\",\n\n\t/**\n\t * Not Includes.\n\t * A string not in a substring.\n\t * A set does not contain an element.\n\t * A list does not contain an element.\n\t */\n\tNotIncludes: \"not-includes\",\n\n\t/**\n\t * In.\n\t * A element is in a set.\n\t */\n\tIn: \"in\"\n} as const;\n\n/**\n * The types of comparisons.\n */\nexport type ComparisonOperator = (typeof ComparisonOperator)[keyof typeof ComparisonOperator];\n"]}
1
+ {"version":3,"file":"comparisonOperator.js","sourceRoot":"","sources":["../../../src/models/comparisonOperator.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AAEvC;;GAEG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,kBAAkB,GAAG;IACjC;;OAEG;IACH,MAAM,EAAE,QAAQ;IAEhB;;OAEG;IACH,SAAS,EAAE,YAAY;IAEvB;;OAEG;IACH,WAAW,EAAE,cAAc;IAE3B;;OAEG;IACH,kBAAkB,EAAE,uBAAuB;IAE3C;;OAEG;IACH,QAAQ,EAAE,WAAW;IAErB;;OAEG;IACH,eAAe,EAAE,oBAAoB;IAErC;;;;;OAKG;IACH,QAAQ,EAAE,UAAU;IAEpB;;;;;OAKG;IACH,WAAW,EAAE,cAAc;IAE3B;;;OAGG;IACH,UAAU,EAAE,aAAa;IAEzB;;;OAGG;IACH,EAAE,EAAE,IAAI;CACC,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The types of comparisons.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const ComparisonOperator = {\n\t/**\n\t * Equals.\n\t */\n\tEquals: \"equals\",\n\n\t/**\n\t * Not Equals.\n\t */\n\tNotEquals: \"not-equals\",\n\n\t/**\n\t * Greater Than.\n\t */\n\tGreaterThan: \"greater-than\",\n\n\t/**\n\t * Greater Than Or Equal.\n\t */\n\tGreaterThanOrEqual: \"greater-than-or-equal\",\n\n\t/**\n\t * Less Than.\n\t */\n\tLessThan: \"less-than\",\n\n\t/**\n\t * Less Than Or Equal.\n\t */\n\tLessThanOrEqual: \"less-than-or-equal\",\n\n\t/**\n\t * Includes.\n\t * A string in a substring, this is a scan and cannot use an index.\n\t * A set contains an element.\n\t * A list contains an element.\n\t */\n\tIncludes: \"includes\",\n\n\t/**\n\t * Not Includes.\n\t * A string not in a substring.\n\t * A set does not contain an element.\n\t * A list does not contain an element.\n\t */\n\tNotIncludes: \"not-includes\",\n\n\t/**\n\t * Starts With.\n\t * A string begins with a prefix, the anchored match can use an index.\n\t */\n\tStartsWith: \"starts-with\",\n\n\t/**\n\t * In.\n\t * A element is in a set.\n\t */\n\tIn: \"in\"\n} as const;\n\n/**\n * The types of comparisons.\n */\nexport type ComparisonOperator = (typeof ComparisonOperator)[keyof typeof ComparisonOperator];\n"]}
@@ -55,136 +55,133 @@ export class EntityConditions {
55
55
  * @returns True if the entity matches.
56
56
  */
57
57
  static compare(entity, comparator) {
58
- const val = ObjectHelper.propertyGet(entity, comparator.property);
58
+ const value = ObjectHelper.propertyGet(entity, comparator.property);
59
59
  const conditionValue = comparator.value;
60
+ const comparison = comparator.comparison;
60
61
  if (Is.undefined(conditionValue)) {
61
- const valUndefined = Is.undefined(val);
62
- if (valUndefined && comparator.comparison === ComparisonOperator.Equals) {
63
- return true;
64
- }
65
- else if (!valUndefined && comparator.comparison === ComparisonOperator.NotEquals) {
66
- return true;
67
- }
68
- return false;
62
+ return EntityConditions.matchEquality(Is.undefined(value), comparison);
69
63
  }
70
- else if (conditionValue === null) {
71
- const valNull = val === null;
72
- if (valNull && comparator.comparison === ComparisonOperator.Equals) {
73
- return true;
74
- }
75
- else if (!valNull && comparator.comparison === ComparisonOperator.NotEquals) {
76
- return true;
77
- }
78
- return false;
79
- }
80
- else if (Is.string(val)) {
81
- if (Is.string(conditionValue)) {
82
- if (!((comparator.comparison === ComparisonOperator.Equals && val === conditionValue) ||
83
- (comparator.comparison === ComparisonOperator.NotEquals && val !== conditionValue) ||
84
- (comparator.comparison === ComparisonOperator.GreaterThan && val > conditionValue) ||
85
- (comparator.comparison === ComparisonOperator.LessThan && val < conditionValue) ||
86
- (comparator.comparison === ComparisonOperator.GreaterThanOrEqual &&
87
- val >= conditionValue) ||
88
- (comparator.comparison === ComparisonOperator.LessThanOrEqual && val <= conditionValue) ||
89
- (comparator.comparison === ComparisonOperator.Includes && val.includes(conditionValue)) ||
90
- (comparator.comparison === ComparisonOperator.NotIncludes &&
91
- !val.includes(conditionValue)))) {
92
- return false;
93
- }
94
- return true;
95
- }
96
- else if (Is.array(conditionValue)) {
97
- if (!(comparator.comparison === ComparisonOperator.In && conditionValue.includes(val))) {
98
- return false;
99
- }
100
- return true;
101
- }
102
- return false;
103
- }
104
- else if (Is.number(val)) {
105
- if (Is.number(conditionValue)) {
106
- if (!((comparator.comparison === ComparisonOperator.Equals && val === conditionValue) ||
107
- (comparator.comparison === ComparisonOperator.NotEquals && val !== conditionValue) ||
108
- (comparator.comparison === ComparisonOperator.GreaterThan && val > conditionValue) ||
109
- (comparator.comparison === ComparisonOperator.LessThan && val < conditionValue) ||
110
- (comparator.comparison === ComparisonOperator.GreaterThanOrEqual &&
111
- val >= conditionValue) ||
112
- (comparator.comparison === ComparisonOperator.LessThanOrEqual && val <= conditionValue))) {
113
- return false;
114
- }
115
- return true;
116
- }
117
- else if (Is.array(conditionValue)) {
118
- if (!(comparator.comparison === ComparisonOperator.In && conditionValue.includes(val))) {
119
- return false;
120
- }
121
- return true;
122
- }
123
- return false;
64
+ if (conditionValue === null) {
65
+ return EntityConditions.matchEquality(value === null, comparison);
124
66
  }
125
- else if (Is.boolean(val)) {
126
- if (Is.boolean(conditionValue)) {
127
- if (!((comparator.comparison === ComparisonOperator.Equals && val === conditionValue) ||
128
- (comparator.comparison === ComparisonOperator.NotEquals && val !== conditionValue))) {
129
- return false;
130
- }
131
- return true;
132
- }
133
- return false;
134
- }
135
- else if (Is.array(val)) {
136
- if (Is.array(conditionValue)) {
137
- if (comparator.comparison === ComparisonOperator.Equals ||
138
- comparator.comparison === ComparisonOperator.NotEquals) {
139
- const matches = ArrayHelper.matches(val, conditionValue);
140
- if (!((comparator.comparison === ComparisonOperator.Equals && matches) ||
141
- (comparator.comparison === ComparisonOperator.NotEquals && !matches))) {
142
- return false;
143
- }
144
- return true;
145
- }
146
- return false;
147
- }
148
- else if (Is.number(conditionValue) || Is.string(conditionValue)) {
149
- if (comparator.comparison === ComparisonOperator.Includes ||
150
- comparator.comparison === ComparisonOperator.NotIncludes ||
151
- comparator.comparison === ComparisonOperator.In) {
152
- const includes = val.includes(conditionValue);
153
- if (!((comparator.comparison === ComparisonOperator.Includes && includes) ||
154
- (comparator.comparison === ComparisonOperator.NotIncludes && !includes) ||
155
- (comparator.comparison === ComparisonOperator.In && includes))) {
156
- return false;
157
- }
158
- return true;
159
- }
160
- return false;
161
- }
162
- else if (Is.object(conditionValue)) {
163
- if (comparator.comparison === ComparisonOperator.Includes) {
164
- for (const v of val) {
165
- if (ObjectHelper.equal(v, conditionValue)) {
166
- return true;
167
- }
168
- }
169
- }
170
- else if (comparator.comparison === ComparisonOperator.NotIncludes) {
171
- for (const v of val) {
172
- if (ObjectHelper.equal(v, conditionValue)) {
173
- return false;
174
- }
175
- }
176
- return true;
177
- }
178
- }
179
- return false;
67
+ if (Is.string(value)) {
68
+ return Is.string(conditionValue)
69
+ ? EntityConditions.compareString(value, conditionValue, comparison)
70
+ : EntityConditions.compareIn(value, conditionValue, comparison);
180
71
  }
181
- else if (Is.object(val)) {
182
- if (comparator.comparison === ComparisonOperator.Equals) {
183
- return ObjectHelper.equal(val, conditionValue);
184
- }
185
- else if (comparator.comparison === ComparisonOperator.NotEquals) {
186
- return !ObjectHelper.equal(val, conditionValue);
187
- }
72
+ if (Is.number(value)) {
73
+ return Is.number(conditionValue)
74
+ ? EntityConditions.compareOrdered(value, conditionValue, comparison)
75
+ : EntityConditions.compareIn(value, conditionValue, comparison);
76
+ }
77
+ if (Is.boolean(value)) {
78
+ return Is.boolean(conditionValue)
79
+ ? EntityConditions.matchEquality(value === conditionValue, comparison)
80
+ : EntityConditions.compareIn(value, conditionValue, comparison);
81
+ }
82
+ if (Is.array(value)) {
83
+ return EntityConditions.compareArray(value, conditionValue, comparison);
84
+ }
85
+ if (Is.object(value)) {
86
+ return EntityConditions.matchEquality(ObjectHelper.equal(value, conditionValue), comparison);
87
+ }
88
+ return false;
89
+ }
90
+ /**
91
+ * Resolve an equality result for the equals and not equals operators.
92
+ * @param isEqual Whether the two values are equal.
93
+ * @param comparison The comparison to perform.
94
+ * @returns True if the comparison is satisfied.
95
+ * @internal
96
+ */
97
+ static matchEquality(isEqual, comparison) {
98
+ return ((comparison === ComparisonOperator.Equals && isEqual) ||
99
+ (comparison === ComparisonOperator.NotEquals && !isEqual));
100
+ }
101
+ /**
102
+ * Resolve an inclusion result for the includes and not includes operators.
103
+ * @param isIncluded Whether the value is included.
104
+ * @param comparison The comparison to perform.
105
+ * @returns True if the comparison is satisfied.
106
+ * @internal
107
+ */
108
+ static matchInclusion(isIncluded, comparison) {
109
+ return ((comparison === ComparisonOperator.Includes && isIncluded) ||
110
+ (comparison === ComparisonOperator.NotIncludes && !isIncluded));
111
+ }
112
+ /**
113
+ * Compare a value with the in operator against a list of condition values.
114
+ * @param value The value to test.
115
+ * @param conditionValue The condition value which should be a list.
116
+ * @param comparison The comparison to perform.
117
+ * @returns True if the comparison is satisfied.
118
+ * @internal
119
+ */
120
+ static compareIn(value, conditionValue, comparison) {
121
+ return (comparison === ComparisonOperator.In &&
122
+ Is.array(conditionValue) &&
123
+ conditionValue.includes(value));
124
+ }
125
+ /**
126
+ * Compare two values which support ordering.
127
+ * @param value The value to test.
128
+ * @param conditionValue The condition value to test against.
129
+ * @param comparison The comparison to perform.
130
+ * @returns True if the comparison is satisfied.
131
+ * @internal
132
+ */
133
+ static compareOrdered(value, conditionValue, comparison) {
134
+ switch (comparison) {
135
+ case ComparisonOperator.GreaterThan:
136
+ return value > conditionValue;
137
+ case ComparisonOperator.LessThan:
138
+ return value < conditionValue;
139
+ case ComparisonOperator.GreaterThanOrEqual:
140
+ return value >= conditionValue;
141
+ case ComparisonOperator.LessThanOrEqual:
142
+ return value <= conditionValue;
143
+ default:
144
+ return EntityConditions.matchEquality(value === conditionValue, comparison);
145
+ }
146
+ }
147
+ /**
148
+ * Compare two string values.
149
+ * @param value The value to test.
150
+ * @param conditionValue The condition value to test against.
151
+ * @param comparison The comparison to perform.
152
+ * @returns True if the comparison is satisfied.
153
+ * @internal
154
+ */
155
+ static compareString(value, conditionValue, comparison) {
156
+ if (comparison === ComparisonOperator.StartsWith) {
157
+ return value.startsWith(conditionValue);
158
+ }
159
+ if (comparison === ComparisonOperator.Includes ||
160
+ comparison === ComparisonOperator.NotIncludes) {
161
+ return EntityConditions.matchInclusion(value.includes(conditionValue), comparison);
162
+ }
163
+ return EntityConditions.compareOrdered(value, conditionValue, comparison);
164
+ }
165
+ /**
166
+ * Compare an array value against a condition value.
167
+ * @param value The array to test.
168
+ * @param conditionValue The condition value to test against.
169
+ * @param comparison The comparison to perform.
170
+ * @returns True if the comparison is satisfied.
171
+ * @internal
172
+ */
173
+ static compareArray(value, conditionValue, comparison) {
174
+ if (Is.array(conditionValue)) {
175
+ return EntityConditions.matchEquality(ArrayHelper.matches(value, conditionValue), comparison);
176
+ }
177
+ if (Is.string(conditionValue) || Is.number(conditionValue)) {
178
+ const isIncluded = value.includes(conditionValue);
179
+ return comparison === ComparisonOperator.In
180
+ ? isIncluded
181
+ : EntityConditions.matchInclusion(isIncluded, comparison);
182
+ }
183
+ if (Is.object(conditionValue)) {
184
+ return EntityConditions.matchInclusion(value.some(v => ObjectHelper.equal(v, conditionValue)), comparison);
188
185
  }
189
186
  return false;
190
187
  }
@@ -1 +1 @@
1
- {"version":3,"file":"entityConditions.js","sourceRoot":"","sources":["../../../src/utils/entityConditions.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ArrayHelper, Is, ObjectHelper } from \"@twin.org/core\";\nimport { ComparisonOperator } from \"../models/comparisonOperator.js\";\nimport type { EntityCondition } from \"../models/entityCondition.js\";\nimport type { IComparator } from \"../models/IComparator.js\";\nimport { LogicalOperator } from \"../models/logicalOperator.js\";\n\n/**\n * Class to perform condition checks.\n */\nexport class EntityConditions {\n\t/**\n\t * See if the entity matches the conditions.\n\t * @param entity The entity to test.\n\t * @param condition The conditions to test.\n\t * @returns True if the entity matches.\n\t */\n\tpublic static check<T>(entity: T, condition?: EntityCondition<T>): boolean {\n\t\t// If no conditions are defined then it's a match\n\t\tif (Is.undefined(condition)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (\"conditions\" in condition) {\n\t\t\t// It's a group of comparisons, so check the individual items and combine with the logical operator\n\t\t\tconst results: boolean[] = condition.conditions.map(c => EntityConditions.check(entity, c));\n\t\t\tif ((condition.logicalOperator ?? LogicalOperator.And) === LogicalOperator.And) {\n\t\t\t\treturn results.every(Boolean);\n\t\t\t}\n\t\t\treturn results.some(Boolean);\n\t\t}\n\n\t\tif (condition.property.includes(\".\")) {\n\t\t\t// It's a child property comparison, so evaluate the child property\n\t\t\t// and then compare it to the conditions\n\t\t\tconst path = condition.property.split(\".\");\n\n\t\t\tconst child = ObjectHelper.propertyGet(entity, path[0]);\n\n\t\t\t// If the child is an array then check each item\n\t\t\tif (Is.array(child)) {\n\t\t\t\tfor (const c of child) {\n\t\t\t\t\tconst check = EntityConditions.check(c, {\n\t\t\t\t\t\t...condition,\n\t\t\t\t\t\tproperty: path.slice(1).join(\".\")\n\t\t\t\t\t});\n\t\t\t\t\tif (check) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// It's a single value so just check the condition\n\t\treturn EntityConditions.compare(entity, condition);\n\t}\n\n\t/**\n\t * See if the entity matches the conditions.\n\t * @param entity The entity to test.\n\t * @param comparator The condition to test.\n\t * @returns True if the entity matches.\n\t */\n\tpublic static compare<T>(entity: T, comparator: IComparator): boolean {\n\t\tconst val = ObjectHelper.propertyGet(entity, comparator.property);\n\t\tconst conditionValue = comparator.value;\n\n\t\tif (Is.undefined(conditionValue)) {\n\t\t\tconst valUndefined = Is.undefined(val);\n\t\t\tif (valUndefined && comparator.comparison === ComparisonOperator.Equals) {\n\t\t\t\treturn true;\n\t\t\t} else if (!valUndefined && comparator.comparison === ComparisonOperator.NotEquals) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t} else if (conditionValue === null) {\n\t\t\tconst valNull = val === null;\n\t\t\tif (valNull && comparator.comparison === ComparisonOperator.Equals) {\n\t\t\t\treturn true;\n\t\t\t} else if (!valNull && comparator.comparison === ComparisonOperator.NotEquals) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t} else if (Is.string(val)) {\n\t\t\tif (Is.string(conditionValue)) {\n\t\t\t\tif (!(\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.Equals && val === conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.NotEquals && val !== conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.GreaterThan && val > conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.LessThan && val < conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.GreaterThanOrEqual &&\n\t\t\t\t\t\tval >= conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.LessThanOrEqual && val <= conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.Includes && val.includes(conditionValue)) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.NotIncludes &&\n\t\t\t\t\t\t!val.includes(conditionValue))\n\t\t\t\t)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t} else if (Is.array(conditionValue)) {\n\t\t\t\tif (!(comparator.comparison === ComparisonOperator.In && conditionValue.includes(val))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t} else if (Is.number(val)) {\n\t\t\tif (Is.number(conditionValue)) {\n\t\t\t\tif (!(\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.Equals && val === conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.NotEquals && val !== conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.GreaterThan && val > conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.LessThan && val < conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.GreaterThanOrEqual &&\n\t\t\t\t\t\tval >= conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.LessThanOrEqual && val <= conditionValue)\n\t\t\t\t)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t} else if (Is.array(conditionValue)) {\n\t\t\t\tif (!(comparator.comparison === ComparisonOperator.In && conditionValue.includes(val))) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t} else if (Is.boolean(val)) {\n\t\t\tif (Is.boolean(conditionValue)) {\n\t\t\t\tif (!(\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.Equals && val === conditionValue) ||\n\t\t\t\t\t(comparator.comparison === ComparisonOperator.NotEquals && val !== conditionValue)\n\t\t\t\t)) {\n\t\t\t\t\treturn false;\n\t\t\t\t}\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\treturn false;\n\t\t} else if (Is.array(val)) {\n\t\t\tif (Is.array(conditionValue)) {\n\t\t\t\tif (\n\t\t\t\t\tcomparator.comparison === ComparisonOperator.Equals ||\n\t\t\t\t\tcomparator.comparison === ComparisonOperator.NotEquals\n\t\t\t\t) {\n\t\t\t\t\tconst matches = ArrayHelper.matches(val, conditionValue);\n\t\t\t\t\tif (!(\n\t\t\t\t\t\t(comparator.comparison === ComparisonOperator.Equals && matches) ||\n\t\t\t\t\t\t(comparator.comparison === ComparisonOperator.NotEquals && !matches)\n\t\t\t\t\t)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t} else if (Is.number(conditionValue) || Is.string(conditionValue)) {\n\t\t\t\tif (\n\t\t\t\t\tcomparator.comparison === ComparisonOperator.Includes ||\n\t\t\t\t\tcomparator.comparison === ComparisonOperator.NotIncludes ||\n\t\t\t\t\tcomparator.comparison === ComparisonOperator.In\n\t\t\t\t) {\n\t\t\t\t\tconst includes = val.includes(conditionValue);\n\t\t\t\t\tif (!(\n\t\t\t\t\t\t(comparator.comparison === ComparisonOperator.Includes && includes) ||\n\t\t\t\t\t\t(comparator.comparison === ComparisonOperator.NotIncludes && !includes) ||\n\t\t\t\t\t\t(comparator.comparison === ComparisonOperator.In && includes)\n\t\t\t\t\t)) {\n\t\t\t\t\t\treturn false;\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t} else if (Is.object(conditionValue)) {\n\t\t\t\tif (comparator.comparison === ComparisonOperator.Includes) {\n\t\t\t\t\tfor (const v of val) {\n\t\t\t\t\t\tif (ObjectHelper.equal(v, conditionValue)) {\n\t\t\t\t\t\t\treturn true;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else if (comparator.comparison === ComparisonOperator.NotIncludes) {\n\t\t\t\t\tfor (const v of val) {\n\t\t\t\t\t\tif (ObjectHelper.equal(v, conditionValue)) {\n\t\t\t\t\t\t\treturn false;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\treturn true;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn false;\n\t\t} else if (Is.object(val)) {\n\t\t\tif (comparator.comparison === ComparisonOperator.Equals) {\n\t\t\t\treturn ObjectHelper.equal(val, conditionValue);\n\t\t\t} else if (comparator.comparison === ComparisonOperator.NotEquals) {\n\t\t\t\treturn !ObjectHelper.equal(val, conditionValue);\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n}\n"]}
1
+ {"version":3,"file":"entityConditions.js","sourceRoot":"","sources":["../../../src/utils/entityConditions.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ArrayHelper, Is, ObjectHelper } from \"@twin.org/core\";\nimport { ComparisonOperator } from \"../models/comparisonOperator.js\";\nimport type { EntityCondition } from \"../models/entityCondition.js\";\nimport type { IComparator } from \"../models/IComparator.js\";\nimport { LogicalOperator } from \"../models/logicalOperator.js\";\n\n/**\n * Class to perform condition checks.\n */\nexport class EntityConditions {\n\t/**\n\t * See if the entity matches the conditions.\n\t * @param entity The entity to test.\n\t * @param condition The conditions to test.\n\t * @returns True if the entity matches.\n\t */\n\tpublic static check<T>(entity: T, condition?: EntityCondition<T>): boolean {\n\t\t// If no conditions are defined then it's a match\n\t\tif (Is.undefined(condition)) {\n\t\t\treturn true;\n\t\t}\n\n\t\tif (\"conditions\" in condition) {\n\t\t\t// It's a group of comparisons, so check the individual items and combine with the logical operator\n\t\t\tconst results: boolean[] = condition.conditions.map(c => EntityConditions.check(entity, c));\n\t\t\tif ((condition.logicalOperator ?? LogicalOperator.And) === LogicalOperator.And) {\n\t\t\t\treturn results.every(Boolean);\n\t\t\t}\n\t\t\treturn results.some(Boolean);\n\t\t}\n\n\t\tif (condition.property.includes(\".\")) {\n\t\t\t// It's a child property comparison, so evaluate the child property\n\t\t\t// and then compare it to the conditions\n\t\t\tconst path = condition.property.split(\".\");\n\n\t\t\tconst child = ObjectHelper.propertyGet(entity, path[0]);\n\n\t\t\t// If the child is an array then check each item\n\t\t\tif (Is.array(child)) {\n\t\t\t\tfor (const c of child) {\n\t\t\t\t\tconst check = EntityConditions.check(c, {\n\t\t\t\t\t\t...condition,\n\t\t\t\t\t\tproperty: path.slice(1).join(\".\")\n\t\t\t\t\t});\n\t\t\t\t\tif (check) {\n\t\t\t\t\t\treturn true;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\t// It's a single value so just check the condition\n\t\treturn EntityConditions.compare(entity, condition);\n\t}\n\n\t/**\n\t * See if the entity matches the conditions.\n\t * @param entity The entity to test.\n\t * @param comparator The condition to test.\n\t * @returns True if the entity matches.\n\t */\n\tpublic static compare<T>(entity: T, comparator: IComparator): boolean {\n\t\tconst value = ObjectHelper.propertyGet(entity, comparator.property);\n\t\tconst conditionValue = comparator.value;\n\t\tconst comparison = comparator.comparison;\n\n\t\tif (Is.undefined(conditionValue)) {\n\t\t\treturn EntityConditions.matchEquality(Is.undefined(value), comparison);\n\t\t}\n\n\t\tif (conditionValue === null) {\n\t\t\treturn EntityConditions.matchEquality(value === null, comparison);\n\t\t}\n\n\t\tif (Is.string(value)) {\n\t\t\treturn Is.string(conditionValue)\n\t\t\t\t? EntityConditions.compareString(value, conditionValue, comparison)\n\t\t\t\t: EntityConditions.compareIn(value, conditionValue, comparison);\n\t\t}\n\n\t\tif (Is.number(value)) {\n\t\t\treturn Is.number(conditionValue)\n\t\t\t\t? EntityConditions.compareOrdered(value, conditionValue, comparison)\n\t\t\t\t: EntityConditions.compareIn(value, conditionValue, comparison);\n\t\t}\n\n\t\tif (Is.boolean(value)) {\n\t\t\treturn Is.boolean(conditionValue)\n\t\t\t\t? EntityConditions.matchEquality(value === conditionValue, comparison)\n\t\t\t\t: EntityConditions.compareIn(value, conditionValue, comparison);\n\t\t}\n\n\t\tif (Is.array(value)) {\n\t\t\treturn EntityConditions.compareArray(value, conditionValue, comparison);\n\t\t}\n\n\t\tif (Is.object(value)) {\n\t\t\treturn EntityConditions.matchEquality(ObjectHelper.equal(value, conditionValue), comparison);\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t * Resolve an equality result for the equals and not equals operators.\n\t * @param isEqual Whether the two values are equal.\n\t * @param comparison The comparison to perform.\n\t * @returns True if the comparison is satisfied.\n\t * @internal\n\t */\n\tprivate static matchEquality(isEqual: boolean, comparison: ComparisonOperator): boolean {\n\t\treturn (\n\t\t\t(comparison === ComparisonOperator.Equals && isEqual) ||\n\t\t\t(comparison === ComparisonOperator.NotEquals && !isEqual)\n\t\t);\n\t}\n\n\t/**\n\t * Resolve an inclusion result for the includes and not includes operators.\n\t * @param isIncluded Whether the value is included.\n\t * @param comparison The comparison to perform.\n\t * @returns True if the comparison is satisfied.\n\t * @internal\n\t */\n\tprivate static matchInclusion(isIncluded: boolean, comparison: ComparisonOperator): boolean {\n\t\treturn (\n\t\t\t(comparison === ComparisonOperator.Includes && isIncluded) ||\n\t\t\t(comparison === ComparisonOperator.NotIncludes && !isIncluded)\n\t\t);\n\t}\n\n\t/**\n\t * Compare a value with the in operator against a list of condition values.\n\t * @param value The value to test.\n\t * @param conditionValue The condition value which should be a list.\n\t * @param comparison The comparison to perform.\n\t * @returns True if the comparison is satisfied.\n\t * @internal\n\t */\n\tprivate static compareIn(\n\t\tvalue: unknown,\n\t\tconditionValue: unknown,\n\t\tcomparison: ComparisonOperator\n\t): boolean {\n\t\treturn (\n\t\t\tcomparison === ComparisonOperator.In &&\n\t\t\tIs.array(conditionValue) &&\n\t\t\tconditionValue.includes(value)\n\t\t);\n\t}\n\n\t/**\n\t * Compare two values which support ordering.\n\t * @param value The value to test.\n\t * @param conditionValue The condition value to test against.\n\t * @param comparison The comparison to perform.\n\t * @returns True if the comparison is satisfied.\n\t * @internal\n\t */\n\tprivate static compareOrdered<U extends string | number>(\n\t\tvalue: U,\n\t\tconditionValue: U,\n\t\tcomparison: ComparisonOperator\n\t): boolean {\n\t\tswitch (comparison) {\n\t\t\tcase ComparisonOperator.GreaterThan:\n\t\t\t\treturn value > conditionValue;\n\t\t\tcase ComparisonOperator.LessThan:\n\t\t\t\treturn value < conditionValue;\n\t\t\tcase ComparisonOperator.GreaterThanOrEqual:\n\t\t\t\treturn value >= conditionValue;\n\t\t\tcase ComparisonOperator.LessThanOrEqual:\n\t\t\t\treturn value <= conditionValue;\n\t\t\tdefault:\n\t\t\t\treturn EntityConditions.matchEquality(value === conditionValue, comparison);\n\t\t}\n\t}\n\n\t/**\n\t * Compare two string values.\n\t * @param value The value to test.\n\t * @param conditionValue The condition value to test against.\n\t * @param comparison The comparison to perform.\n\t * @returns True if the comparison is satisfied.\n\t * @internal\n\t */\n\tprivate static compareString(\n\t\tvalue: string,\n\t\tconditionValue: string,\n\t\tcomparison: ComparisonOperator\n\t): boolean {\n\t\tif (comparison === ComparisonOperator.StartsWith) {\n\t\t\treturn value.startsWith(conditionValue);\n\t\t}\n\n\t\tif (\n\t\t\tcomparison === ComparisonOperator.Includes ||\n\t\t\tcomparison === ComparisonOperator.NotIncludes\n\t\t) {\n\t\t\treturn EntityConditions.matchInclusion(value.includes(conditionValue), comparison);\n\t\t}\n\n\t\treturn EntityConditions.compareOrdered(value, conditionValue, comparison);\n\t}\n\n\t/**\n\t * Compare an array value against a condition value.\n\t * @param value The array to test.\n\t * @param conditionValue The condition value to test against.\n\t * @param comparison The comparison to perform.\n\t * @returns True if the comparison is satisfied.\n\t * @internal\n\t */\n\tprivate static compareArray(\n\t\tvalue: unknown[],\n\t\tconditionValue: unknown,\n\t\tcomparison: ComparisonOperator\n\t): boolean {\n\t\tif (Is.array(conditionValue)) {\n\t\t\treturn EntityConditions.matchEquality(ArrayHelper.matches(value, conditionValue), comparison);\n\t\t}\n\n\t\tif (Is.string(conditionValue) || Is.number(conditionValue)) {\n\t\t\tconst isIncluded = value.includes(conditionValue);\n\t\t\treturn comparison === ComparisonOperator.In\n\t\t\t\t? isIncluded\n\t\t\t\t: EntityConditions.matchInclusion(isIncluded, comparison);\n\t\t}\n\n\t\tif (Is.object(conditionValue)) {\n\t\t\treturn EntityConditions.matchInclusion(\n\t\t\t\tvalue.some(v => ObjectHelper.equal(v, conditionValue)),\n\t\t\t\tcomparison\n\t\t\t);\n\t\t}\n\n\t\treturn false;\n\t}\n}\n"]}
@@ -12,7 +12,7 @@ export class EntitySchemaDiffHelper {
12
12
  /**
13
13
  * Compare two arrays of entity schema properties and return a structured diff.
14
14
  *
15
- * Properties are matched by their `property` key name. A property is considered modified when any structural field differs: `type`, `format`, `isPrimary`, `isSecondary`, `isVersion`, `sortDirection`, `optional`, `itemType`, or `itemTypeRef`.
15
+ * Properties are matched by their `property` key name. A property is considered modified when any structural field differs: `type`, `format`, `isPrimary`, `isSecondary`, `indexGroup`, `isVersion`, `sortDirection`, `optional`, `itemType`, or `itemTypeRef`.
16
16
  * Documentation-only fields (`description`, `examples`) are intentionally excluded from the comparison to avoid spurious diffs.
17
17
  *
18
18
  * Because a pure name change cannot be detected automatically, callers may supply a `renames` list mapping old names to new names. Renamed properties appear in `modified` (never in `added` or `removed`) even when no other fields changed. Rename lookups take priority over direct same-name matches, which allows swap renames to work correctly and prevents a renamed source from silently disappearing when the target name already existed in the old schema. Self-renames (`from === to`) are ignored and the property is classified normally.
@@ -131,11 +131,37 @@ export class EntitySchemaDiffHelper {
131
131
  schema1.maxLength === schema2.maxLength &&
132
132
  schema1.isPrimary === schema2.isPrimary &&
133
133
  schema1.isSecondary === schema2.isSecondary &&
134
+ EntitySchemaDiffHelper.indexGroupsEqual(schema1.indexGroup, schema2.indexGroup) &&
134
135
  schema1.isVersion === schema2.isVersion &&
135
136
  schema1.sortDirection === schema2.sortDirection &&
136
137
  schema1.optional === schema2.optional &&
137
138
  schema1.itemType === schema2.itemType &&
138
139
  schema1.itemTypeRef === schema2.itemTypeRef);
139
140
  }
141
+ /**
142
+ * Compare two index group lists, treating them as unordered sets of indexes.
143
+ * The order a property declares its indexes in has no effect on the indexes a connector builds.
144
+ * @param indexGroup1 The first index group list.
145
+ * @param indexGroup2 The second index group list.
146
+ * @returns True if both lists contain the same name, direction and index values.
147
+ * @internal
148
+ */
149
+ static indexGroupsEqual(indexGroup1, indexGroup2) {
150
+ const groups1 = indexGroup1 ?? [];
151
+ const unmatchedGroups2 = [...(indexGroup2 ?? [])];
152
+ if (groups1.length !== unmatchedGroups2.length) {
153
+ return false;
154
+ }
155
+ for (const group1 of groups1) {
156
+ const matchIndex = unmatchedGroups2.findIndex(group2 => group2?.name === group1?.name &&
157
+ group2?.direction === group1?.direction &&
158
+ group2?.index === group1?.index);
159
+ if (matchIndex === -1) {
160
+ return false;
161
+ }
162
+ unmatchedGroups2.splice(matchIndex, 1);
163
+ }
164
+ return unmatchedGroups2.length === 0;
165
+ }
140
166
  }
141
167
  //# sourceMappingURL=entitySchemaDiffHelper.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"entitySchemaDiffHelper.js","sourceRoot":"","sources":["../../../src/utils/entitySchemaDiffHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Guards } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IEntitySchemaDiff } from \"../models/IEntitySchemaDiff.js\";\nimport type { IEntitySchemaProperty } from \"../models/IEntitySchemaProperty.js\";\n\n/**\n * Helper class for comparing entity schemas and generating diffs.\n */\nexport class EntitySchemaDiffHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntitySchemaDiffHelper>();\n\n\t/**\n\t * Compare two arrays of entity schema properties and return a structured diff.\n\t *\n\t * Properties are matched by their `property` key name. A property is considered modified when any structural field differs: `type`, `format`, `isPrimary`, `isSecondary`, `isVersion`, `sortDirection`, `optional`, `itemType`, or `itemTypeRef`.\n\t * Documentation-only fields (`description`, `examples`) are intentionally excluded from the comparison to avoid spurious diffs.\n\t *\n\t * Because a pure name change cannot be detected automatically, callers may supply a `renames` list mapping old names to new names. Renamed properties appear in `modified` (never in `added` or `removed`) even when no other fields changed. Rename lookups take priority over direct same-name matches, which allows swap renames to work correctly and prevents a renamed source from silently disappearing when the target name already existed in the old schema. Self-renames (`from === to`) are ignored and the property is classified normally.\n\t *\n\t * When `renames` contains duplicate entries: if two entries share the same target, the last definition wins and the first source is treated as removed; if two entries share the same source, the first target wins and the second target is treated as added. Both cases are deterministic but callers should avoid them.\n\t * @param oldProperties The property descriptors from the current (live) schema.\n\t * @param newProperties The property descriptors from the target (new) schema.\n\t * @param renames Optional list of property renames `{ from, to }` where `from` is the old name and `to` is the new name.\n\t * @returns A diff object with `added`, `removed`, `modified`, and `unchanged` arrays, each containing full `IEntitySchemaProperty` descriptors.\n\t * @throws `GeneralError` if either input array contains duplicate property keys.\n\t */\n\tpublic static diff<T, U = T>(\n\t\toldProperties: IEntitySchemaProperty<T>[],\n\t\tnewProperties: IEntitySchemaProperty<U>[],\n\t\trenames?: { from: string; to: string }[]\n\t): IEntitySchemaDiff<T, U> {\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(oldProperties), oldProperties);\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(newProperties), newProperties);\n\n\t\tconst added: IEntitySchemaProperty<U>[] = [];\n\t\tconst removed: IEntitySchemaProperty<T>[] = [];\n\t\tconst modified: IEntitySchemaDiff<T, U>[\"modified\"] = [];\n\t\tconst unchanged: IEntitySchemaProperty<T | U>[] = [];\n\n\t\tconst oldMap = new Map<string, IEntitySchemaProperty<T>>();\n\t\tfor (const prop of oldProperties) {\n\t\t\tconst propKey = prop.property as string;\n\t\t\tif (oldMap.has(propKey)) {\n\t\t\t\tthrow new GeneralError(EntitySchemaDiffHelper.CLASS_NAME, \"duplicateOldProperty\", {\n\t\t\t\t\tproperty: propKey\n\t\t\t\t});\n\t\t\t}\n\t\t\toldMap.set(propKey, prop);\n\t\t}\n\n\t\tconst newMap = new Map<string, IEntitySchemaProperty<U>>();\n\t\tfor (const prop of newProperties) {\n\t\t\tconst propKey = prop.property as string;\n\t\t\tif (newMap.has(propKey)) {\n\t\t\t\tthrow new GeneralError(EntitySchemaDiffHelper.CLASS_NAME, \"duplicateNewProperty\", {\n\t\t\t\t\tproperty: propKey\n\t\t\t\t});\n\t\t\t}\n\t\t\tnewMap.set(propKey, prop);\n\t\t}\n\n\t\t// new-name → old-name, used when iterating newProperties\n\t\tconst renameToFrom = new Map<string, string>();\n\t\tif (renames) {\n\t\t\tfor (const rename of renames) {\n\t\t\t\trenameToFrom.set(rename.to, rename.from);\n\t\t\t}\n\t\t}\n\n\t\t// Old names that were consumed by a rename (prevents double-use of the same source).\n\t\tconst consumedByRename = new Set<string>();\n\t\t// New names matched via rename path (their same-named old prop is not their direct match).\n\t\tconst newKeyMatchedViaRename = new Set<string>();\n\n\t\tfor (const newProp of newProperties) {\n\t\t\tconst key = newProp.property as string;\n\n\t\t\t// Rename lookup takes priority over a direct same-name match so that:\n\t\t\t// - swap renames work (both names exist in old and new)\n\t\t\t// - a renamed source does not vanish when the target name already existed in old\n\t\t\t// Self-renames (fromKey === key) are skipped so the property is classified normally.\n\t\t\tconst fromKey = renameToFrom.get(key);\n\t\t\tconst renamedSource = fromKey !== undefined ? oldMap.get(fromKey) : undefined;\n\n\t\t\tif (\n\t\t\t\tfromKey !== undefined &&\n\t\t\t\tfromKey !== key &&\n\t\t\t\trenamedSource !== undefined &&\n\t\t\t\t!consumedByRename.has(fromKey)\n\t\t\t) {\n\t\t\t\tmodified.push({ from: renamedSource, to: newProp });\n\t\t\t\tconsumedByRename.add(fromKey);\n\t\t\t\tnewKeyMatchedViaRename.add(key);\n\t\t\t} else {\n\t\t\t\tconst oldProp = oldMap.get(key);\n\t\t\t\tif (oldProp !== undefined) {\n\t\t\t\t\tif (!EntitySchemaDiffHelper.schemaPropertiesEqual(oldProp, newProp)) {\n\t\t\t\t\t\tmodified.push({ from: oldProp, to: newProp });\n\t\t\t\t\t} else {\n\t\t\t\t\t\tunchanged.push(newProp as IEntitySchemaProperty<T | U>);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tadded.push(newProp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const oldProp of oldProperties) {\n\t\t\tconst key = oldProp.property as string;\n\t\t\t// Removed when absent from new, or when its same-named new prop was claimed by a rename\n\t\t\t// (meaning this old prop was not the match for that new prop).\n\t\t\t// Exception: skip if this old prop was itself consumed as a rename source.\n\t\t\tif ((!newMap.has(key) || newKeyMatchedViaRename.has(key)) && !consumedByRename.has(key)) {\n\t\t\t\tremoved.push(oldProp);\n\t\t\t}\n\t\t}\n\n\t\treturn { added, removed, modified, unchanged };\n\t}\n\n\t/**\n\t * Returns true when the diff contains at least one added, removed, or modified property.\n\t * @param diff The diff to check.\n\t * @returns True if the diff has any structural changes.\n\t */\n\tpublic static hasChanges<T, U>(diff: IEntitySchemaDiff<T, U>): boolean {\n\t\tGuards.object(EntitySchemaDiffHelper.CLASS_NAME, nameof(diff), diff);\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(diff.added), diff.added);\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(diff.removed), diff.removed);\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(diff.modified), diff.modified);\n\t\treturn diff.added.length > 0 || diff.removed.length > 0 || diff.modified.length > 0;\n\t}\n\n\t/**\n\t * Compare two property descriptors for structural equality.\n\t * The `property` name field and documentation fields (`description`, `examples`) are intentionally excluded - callers match by name before invoking this method.\n\t * @param schema1 The first property descriptor.\n\t * @param schema2 The second property descriptor.\n\t * @returns True if all structural fields are equal.\n\t */\n\tpublic static schemaPropertiesEqual<T, U>(\n\t\tschema1: IEntitySchemaProperty<T>,\n\t\tschema2: IEntitySchemaProperty<U>\n\t): boolean {\n\t\tGuards.object(EntitySchemaDiffHelper.CLASS_NAME, nameof(schema1), schema1);\n\t\tGuards.object(EntitySchemaDiffHelper.CLASS_NAME, nameof(schema2), schema2);\n\t\treturn (\n\t\t\tschema1.type === schema2.type &&\n\t\t\tschema1.format === schema2.format &&\n\t\t\tschema1.maxLength === schema2.maxLength &&\n\t\t\tschema1.isPrimary === schema2.isPrimary &&\n\t\t\tschema1.isSecondary === schema2.isSecondary &&\n\t\t\tschema1.isVersion === schema2.isVersion &&\n\t\t\tschema1.sortDirection === schema2.sortDirection &&\n\t\t\tschema1.optional === schema2.optional &&\n\t\t\tschema1.itemType === schema2.itemType &&\n\t\t\tschema1.itemTypeRef === schema2.itemTypeRef\n\t\t);\n\t}\n}\n"]}
1
+ {"version":3,"file":"entitySchemaDiffHelper.js","sourceRoot":"","sources":["../../../src/utils/entitySchemaDiffHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { GeneralError, Guards } from \"@twin.org/core\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { IEntitySchemaDiff } from \"../models/IEntitySchemaDiff.js\";\nimport type { IEntitySchemaProperty } from \"../models/IEntitySchemaProperty.js\";\nimport type { IEntitySchemaPropertyIndex } from \"../models/IEntitySchemaPropertyIndex.js\";\n\n/**\n * Helper class for comparing entity schemas and generating diffs.\n */\nexport class EntitySchemaDiffHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntitySchemaDiffHelper>();\n\n\t/**\n\t * Compare two arrays of entity schema properties and return a structured diff.\n\t *\n\t * Properties are matched by their `property` key name. A property is considered modified when any structural field differs: `type`, `format`, `isPrimary`, `isSecondary`, `indexGroup`, `isVersion`, `sortDirection`, `optional`, `itemType`, or `itemTypeRef`.\n\t * Documentation-only fields (`description`, `examples`) are intentionally excluded from the comparison to avoid spurious diffs.\n\t *\n\t * Because a pure name change cannot be detected automatically, callers may supply a `renames` list mapping old names to new names. Renamed properties appear in `modified` (never in `added` or `removed`) even when no other fields changed. Rename lookups take priority over direct same-name matches, which allows swap renames to work correctly and prevents a renamed source from silently disappearing when the target name already existed in the old schema. Self-renames (`from === to`) are ignored and the property is classified normally.\n\t *\n\t * When `renames` contains duplicate entries: if two entries share the same target, the last definition wins and the first source is treated as removed; if two entries share the same source, the first target wins and the second target is treated as added. Both cases are deterministic but callers should avoid them.\n\t * @param oldProperties The property descriptors from the current (live) schema.\n\t * @param newProperties The property descriptors from the target (new) schema.\n\t * @param renames Optional list of property renames `{ from, to }` where `from` is the old name and `to` is the new name.\n\t * @returns A diff object with `added`, `removed`, `modified`, and `unchanged` arrays, each containing full `IEntitySchemaProperty` descriptors.\n\t * @throws `GeneralError` if either input array contains duplicate property keys.\n\t */\n\tpublic static diff<T, U = T>(\n\t\toldProperties: IEntitySchemaProperty<T>[],\n\t\tnewProperties: IEntitySchemaProperty<U>[],\n\t\trenames?: { from: string; to: string }[]\n\t): IEntitySchemaDiff<T, U> {\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(oldProperties), oldProperties);\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(newProperties), newProperties);\n\n\t\tconst added: IEntitySchemaProperty<U>[] = [];\n\t\tconst removed: IEntitySchemaProperty<T>[] = [];\n\t\tconst modified: IEntitySchemaDiff<T, U>[\"modified\"] = [];\n\t\tconst unchanged: IEntitySchemaProperty<T | U>[] = [];\n\n\t\tconst oldMap = new Map<string, IEntitySchemaProperty<T>>();\n\t\tfor (const prop of oldProperties) {\n\t\t\tconst propKey = prop.property as string;\n\t\t\tif (oldMap.has(propKey)) {\n\t\t\t\tthrow new GeneralError(EntitySchemaDiffHelper.CLASS_NAME, \"duplicateOldProperty\", {\n\t\t\t\t\tproperty: propKey\n\t\t\t\t});\n\t\t\t}\n\t\t\toldMap.set(propKey, prop);\n\t\t}\n\n\t\tconst newMap = new Map<string, IEntitySchemaProperty<U>>();\n\t\tfor (const prop of newProperties) {\n\t\t\tconst propKey = prop.property as string;\n\t\t\tif (newMap.has(propKey)) {\n\t\t\t\tthrow new GeneralError(EntitySchemaDiffHelper.CLASS_NAME, \"duplicateNewProperty\", {\n\t\t\t\t\tproperty: propKey\n\t\t\t\t});\n\t\t\t}\n\t\t\tnewMap.set(propKey, prop);\n\t\t}\n\n\t\t// new-name → old-name, used when iterating newProperties\n\t\tconst renameToFrom = new Map<string, string>();\n\t\tif (renames) {\n\t\t\tfor (const rename of renames) {\n\t\t\t\trenameToFrom.set(rename.to, rename.from);\n\t\t\t}\n\t\t}\n\n\t\t// Old names that were consumed by a rename (prevents double-use of the same source).\n\t\tconst consumedByRename = new Set<string>();\n\t\t// New names matched via rename path (their same-named old prop is not their direct match).\n\t\tconst newKeyMatchedViaRename = new Set<string>();\n\n\t\tfor (const newProp of newProperties) {\n\t\t\tconst key = newProp.property as string;\n\n\t\t\t// Rename lookup takes priority over a direct same-name match so that:\n\t\t\t// - swap renames work (both names exist in old and new)\n\t\t\t// - a renamed source does not vanish when the target name already existed in old\n\t\t\t// Self-renames (fromKey === key) are skipped so the property is classified normally.\n\t\t\tconst fromKey = renameToFrom.get(key);\n\t\t\tconst renamedSource = fromKey !== undefined ? oldMap.get(fromKey) : undefined;\n\n\t\t\tif (\n\t\t\t\tfromKey !== undefined &&\n\t\t\t\tfromKey !== key &&\n\t\t\t\trenamedSource !== undefined &&\n\t\t\t\t!consumedByRename.has(fromKey)\n\t\t\t) {\n\t\t\t\tmodified.push({ from: renamedSource, to: newProp });\n\t\t\t\tconsumedByRename.add(fromKey);\n\t\t\t\tnewKeyMatchedViaRename.add(key);\n\t\t\t} else {\n\t\t\t\tconst oldProp = oldMap.get(key);\n\t\t\t\tif (oldProp !== undefined) {\n\t\t\t\t\tif (!EntitySchemaDiffHelper.schemaPropertiesEqual(oldProp, newProp)) {\n\t\t\t\t\t\tmodified.push({ from: oldProp, to: newProp });\n\t\t\t\t\t} else {\n\t\t\t\t\t\tunchanged.push(newProp as IEntitySchemaProperty<T | U>);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tadded.push(newProp);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfor (const oldProp of oldProperties) {\n\t\t\tconst key = oldProp.property as string;\n\t\t\t// Removed when absent from new, or when its same-named new prop was claimed by a rename\n\t\t\t// (meaning this old prop was not the match for that new prop).\n\t\t\t// Exception: skip if this old prop was itself consumed as a rename source.\n\t\t\tif ((!newMap.has(key) || newKeyMatchedViaRename.has(key)) && !consumedByRename.has(key)) {\n\t\t\t\tremoved.push(oldProp);\n\t\t\t}\n\t\t}\n\n\t\treturn { added, removed, modified, unchanged };\n\t}\n\n\t/**\n\t * Returns true when the diff contains at least one added, removed, or modified property.\n\t * @param diff The diff to check.\n\t * @returns True if the diff has any structural changes.\n\t */\n\tpublic static hasChanges<T, U>(diff: IEntitySchemaDiff<T, U>): boolean {\n\t\tGuards.object(EntitySchemaDiffHelper.CLASS_NAME, nameof(diff), diff);\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(diff.added), diff.added);\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(diff.removed), diff.removed);\n\t\tGuards.array(EntitySchemaDiffHelper.CLASS_NAME, nameof(diff.modified), diff.modified);\n\t\treturn diff.added.length > 0 || diff.removed.length > 0 || diff.modified.length > 0;\n\t}\n\n\t/**\n\t * Compare two property descriptors for structural equality.\n\t * The `property` name field and documentation fields (`description`, `examples`) are intentionally excluded - callers match by name before invoking this method.\n\t * @param schema1 The first property descriptor.\n\t * @param schema2 The second property descriptor.\n\t * @returns True if all structural fields are equal.\n\t */\n\tpublic static schemaPropertiesEqual<T, U>(\n\t\tschema1: IEntitySchemaProperty<T>,\n\t\tschema2: IEntitySchemaProperty<U>\n\t): boolean {\n\t\tGuards.object(EntitySchemaDiffHelper.CLASS_NAME, nameof(schema1), schema1);\n\t\tGuards.object(EntitySchemaDiffHelper.CLASS_NAME, nameof(schema2), schema2);\n\t\treturn (\n\t\t\tschema1.type === schema2.type &&\n\t\t\tschema1.format === schema2.format &&\n\t\t\tschema1.maxLength === schema2.maxLength &&\n\t\t\tschema1.isPrimary === schema2.isPrimary &&\n\t\t\tschema1.isSecondary === schema2.isSecondary &&\n\t\t\tEntitySchemaDiffHelper.indexGroupsEqual(schema1.indexGroup, schema2.indexGroup) &&\n\t\t\tschema1.isVersion === schema2.isVersion &&\n\t\t\tschema1.sortDirection === schema2.sortDirection &&\n\t\t\tschema1.optional === schema2.optional &&\n\t\t\tschema1.itemType === schema2.itemType &&\n\t\t\tschema1.itemTypeRef === schema2.itemTypeRef\n\t\t);\n\t}\n\n\t/**\n\t * Compare two index group lists, treating them as unordered sets of indexes.\n\t * The order a property declares its indexes in has no effect on the indexes a connector builds.\n\t * @param indexGroup1 The first index group list.\n\t * @param indexGroup2 The second index group list.\n\t * @returns True if both lists contain the same name, direction and index values.\n\t * @internal\n\t */\n\tprivate static indexGroupsEqual(\n\t\tindexGroup1?: IEntitySchemaPropertyIndex[],\n\t\tindexGroup2?: IEntitySchemaPropertyIndex[]\n\t): boolean {\n\t\tconst groups1 = indexGroup1 ?? [];\n\t\tconst unmatchedGroups2 = [...(indexGroup2 ?? [])];\n\n\t\tif (groups1.length !== unmatchedGroups2.length) {\n\t\t\treturn false;\n\t\t}\n\n\t\tfor (const group1 of groups1) {\n\t\t\tconst matchIndex = unmatchedGroups2.findIndex(\n\t\t\t\tgroup2 =>\n\t\t\t\t\tgroup2?.name === group1?.name &&\n\t\t\t\t\tgroup2?.direction === group1?.direction &&\n\t\t\t\t\tgroup2?.index === group1?.index\n\t\t\t);\n\t\t\tif (matchIndex === -1) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t\tunmatchedGroups2.splice(matchIndex, 1);\n\t\t}\n\t\treturn unmatchedGroups2.length === 0;\n\t}\n}\n"]}