@twin.org/entity-storage-models 0.0.3-next.3 → 0.0.3-next.31

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 (79) hide show
  1. package/README.md +2 -2
  2. package/dist/es/factories/schemaMigrationFactory.js +17 -0
  3. package/dist/es/factories/schemaMigrationFactory.js.map +1 -0
  4. package/dist/es/helpers/entityStorageHelper.js +126 -0
  5. package/dist/es/helpers/entityStorageHelper.js.map +1 -0
  6. package/dist/es/helpers/migrationHelper.js +232 -0
  7. package/dist/es/helpers/migrationHelper.js.map +1 -0
  8. package/dist/es/index.js +14 -1
  9. package/dist/es/index.js.map +1 -1
  10. package/dist/es/models/IEntityStorageComponent.js.map +1 -1
  11. package/dist/es/models/IEntityStorageConnector.js.map +1 -1
  12. package/dist/es/models/IEntityStorageMigrationConnector.js +2 -0
  13. package/dist/es/models/IEntityStorageMigrationConnector.js.map +1 -0
  14. package/dist/es/models/IMigrationOptions.js +4 -0
  15. package/dist/es/models/IMigrationOptions.js.map +1 -0
  16. package/dist/es/models/IResolvedMigrationStep.js +2 -0
  17. package/dist/es/models/IResolvedMigrationStep.js.map +1 -0
  18. package/dist/es/models/ISchemaMigration.js +2 -0
  19. package/dist/es/models/ISchemaMigration.js.map +1 -0
  20. package/dist/es/models/api/IEntityStorageCountRequest.js +4 -0
  21. package/dist/es/models/api/IEntityStorageCountRequest.js.map +1 -0
  22. package/dist/es/models/api/IEntityStorageCountResponse.js +4 -0
  23. package/dist/es/models/api/IEntityStorageCountResponse.js.map +1 -0
  24. package/dist/es/models/api/IEntityStorageEmptyRequest.js +4 -0
  25. package/dist/es/models/api/IEntityStorageEmptyRequest.js.map +1 -0
  26. package/dist/es/models/api/IEntityStorageGetRequest.js.map +1 -1
  27. package/dist/es/models/api/IEntityStorageRemoveBatchRequest.js +4 -0
  28. package/dist/es/models/api/IEntityStorageRemoveBatchRequest.js.map +1 -0
  29. package/dist/es/models/api/IEntityStorageRemoveRequest.js.map +1 -1
  30. package/dist/es/models/api/IEntityStorageSetBatchRequest.js +4 -0
  31. package/dist/es/models/api/IEntityStorageSetBatchRequest.js.map +1 -0
  32. package/dist/es/models/api/IEntityStorageSetRequest.js.map +1 -1
  33. package/dist/es/models/entityPropertyTransformer.js +2 -0
  34. package/dist/es/models/entityPropertyTransformer.js.map +1 -0
  35. package/dist/types/factories/schemaMigrationFactory.d.ts +14 -0
  36. package/dist/types/helpers/entityStorageHelper.d.ts +59 -0
  37. package/dist/types/helpers/migrationHelper.d.ts +65 -0
  38. package/dist/types/index.d.ts +13 -0
  39. package/dist/types/models/IEntityStorageComponent.d.ts +38 -3
  40. package/dist/types/models/IEntityStorageConnector.d.ts +23 -0
  41. package/dist/types/models/IEntityStorageMigrationConnector.d.ts +35 -0
  42. package/dist/types/models/IMigrationOptions.d.ts +17 -0
  43. package/dist/types/models/IResolvedMigrationStep.d.ts +38 -0
  44. package/dist/types/models/ISchemaMigration.d.ts +30 -0
  45. package/dist/types/models/api/IEntityStorageCountRequest.d.ts +14 -0
  46. package/dist/types/models/api/IEntityStorageCountResponse.d.ts +14 -0
  47. package/dist/types/models/api/IEntityStorageEmptyRequest.d.ts +5 -0
  48. package/dist/types/models/api/IEntityStorageGetRequest.d.ts +4 -0
  49. package/dist/types/models/api/IEntityStorageRemoveBatchRequest.d.ts +9 -0
  50. package/dist/types/models/api/IEntityStorageRemoveRequest.d.ts +9 -0
  51. package/dist/types/models/api/IEntityStorageSetBatchRequest.d.ts +9 -0
  52. package/dist/types/models/api/IEntityStorageSetRequest.d.ts +9 -0
  53. package/dist/types/models/entityPropertyTransformer.d.ts +6 -0
  54. package/docs/changelog.md +315 -44
  55. package/docs/examples.md +87 -1
  56. package/docs/reference/classes/EntityStorageHelper.md +209 -0
  57. package/docs/reference/classes/MigrationHelper.md +198 -0
  58. package/docs/reference/index.md +19 -0
  59. package/docs/reference/interfaces/IEntityStorageComponent.md +105 -7
  60. package/docs/reference/interfaces/IEntityStorageConnector.md +89 -5
  61. package/docs/reference/interfaces/IEntityStorageCountRequest.md +17 -0
  62. package/docs/reference/interfaces/IEntityStorageCountResponse.md +17 -0
  63. package/docs/reference/interfaces/IEntityStorageEmptyRequest.md +3 -0
  64. package/docs/reference/interfaces/IEntityStorageGetRequest.md +10 -4
  65. package/docs/reference/interfaces/IEntityStorageGetResponse.md +1 -1
  66. package/docs/reference/interfaces/IEntityStorageListRequest.md +8 -8
  67. package/docs/reference/interfaces/IEntityStorageListResponse.md +2 -2
  68. package/docs/reference/interfaces/IEntityStorageMigrationConnector.md +402 -0
  69. package/docs/reference/interfaces/IEntityStorageRemoveBatchRequest.md +11 -0
  70. package/docs/reference/interfaces/IEntityStorageRemoveRequest.md +15 -1
  71. package/docs/reference/interfaces/IEntityStorageSetBatchRequest.md +11 -0
  72. package/docs/reference/interfaces/IEntityStorageSetRequest.md +15 -1
  73. package/docs/reference/interfaces/IMigrationOptions.md +49 -0
  74. package/docs/reference/interfaces/IResolvedMigrationStep.md +78 -0
  75. package/docs/reference/interfaces/ISchemaMigration.md +62 -0
  76. package/docs/reference/type-aliases/EntityPropertyTransformer.md +34 -0
  77. package/docs/reference/variables/SchemaMigrationFactory.md +13 -0
  78. package/locales/en.json +18 -1
  79. package/package.json +6 -4
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
- # TWIN Entity Storage Models
1
+ # Entity Storage Models
2
2
 
3
- Models which define the structure of the entity storage contracts and connectors.
3
+ This package defines the shared domain models that standardise storage contracts, requests, responses and connector capabilities. It is designed to work with the wider storage ecosystem so applications can keep behaviour consistent across connectors and environments.
4
4
 
5
5
  ## Installation
6
6
 
@@ -0,0 +1,17 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { Factory } from "@twin.org/core";
4
+ /**
5
+ * Factory for optional per-step migration overrides.
6
+ *
7
+ * Only register an entry when a version step requires property renames or a custom
8
+ * transform hook. For purely structural changes (add/remove/type-change) the
9
+ * SchemaVersionService diffs the two versioned schema classes automatically
10
+ * without needing any factory entry.
11
+ *
12
+ * Keys follow the convention "BaseSchemaName_fromVersion_toVersion",
13
+ * for example "MyEntity_0_1" for the step that migrates MyEntity from version 0 to 1.
14
+ */
15
+ // eslint-disable-next-line @typescript-eslint/naming-convention
16
+ export const SchemaMigrationFactory = Factory.createFactory("schema-migration");
17
+ //# sourceMappingURL=schemaMigrationFactory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemaMigrationFactory.js","sourceRoot":"","sources":["../../../src/factories/schemaMigrationFactory.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAC;AAGzC;;;;;;;;;;GAUG;AACH,gEAAgE;AAChE,MAAM,CAAC,MAAM,sBAAsB,GAAG,OAAO,CAAC,aAAa,CAAmB,kBAAkB,CAAC,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { Factory } from \"@twin.org/core\";\nimport type { ISchemaMigration } from \"../models/ISchemaMigration.js\";\n\n/**\n * Factory for optional per-step migration overrides.\n *\n * Only register an entry when a version step requires property renames or a custom\n * transform hook. For purely structural changes (add/remove/type-change) the\n * SchemaVersionService diffs the two versioned schema classes automatically\n * without needing any factory entry.\n *\n * Keys follow the convention \"BaseSchemaName_fromVersion_toVersion\",\n * for example \"MyEntity_0_1\" for the step that migrates MyEntity from version 0 to 1.\n */\n// eslint-disable-next-line @typescript-eslint/naming-convention\nexport const SchemaMigrationFactory = Factory.createFactory<ISchemaMigration>(\"schema-migration\");\n"]}
@@ -0,0 +1,126 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { GeneralError, Is, ObjectHelper } from "@twin.org/core";
4
+ import { ComparisonOperator, EntitySchemaHelper } from "@twin.org/entity";
5
+ /**
6
+ * Helper class for performing schema migrations between two connectors.
7
+ */
8
+ export class EntityStorageHelper {
9
+ /**
10
+ * Runtime name for the class.
11
+ */
12
+ static CLASS_NAME = "EntityStorageHelper";
13
+ /**
14
+ * Prepare the entity by handling undefined and null values and validating it against the schema.
15
+ * @param entity The entity to handle undefined and null values for.
16
+ * @param schema The schema to validate the entity against.
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).
21
+ * @returns The entity with undefined and null values handled.
22
+ */
23
+ static prepareEntity(entity, schema, additionalProperties, options) {
24
+ const entityForValidation = ObjectHelper.clone(entity);
25
+ EntitySchemaHelper.validateEntity(entityForValidation, schema);
26
+ if (Is.arrayValue(schema.properties)) {
27
+ for (const property of schema.properties) {
28
+ if (property.optional ?? false) {
29
+ const propValue = entityForValidation[property.property];
30
+ if (options?.nullBehavior === "omit") {
31
+ if (propValue === undefined || propValue === null) {
32
+ ObjectHelper.propertyDelete(entityForValidation, property.property);
33
+ }
34
+ }
35
+ else if (propValue === undefined) {
36
+ ObjectHelper.propertySet(entityForValidation, property.property, null);
37
+ }
38
+ }
39
+ }
40
+ }
41
+ if (Is.arrayValue(additionalProperties)) {
42
+ for (const additionalProperty of additionalProperties) {
43
+ ObjectHelper.propertySet(entityForValidation, additionalProperty.property, additionalProperty.value);
44
+ }
45
+ }
46
+ return entityForValidation;
47
+ }
48
+ /**
49
+ * Un-prepare the entity by removing null values.
50
+ * @param entity The entity to handle undefined and null values for.
51
+ * @param removeProperties Optional list of properties to remove from the entity.
52
+ * @returns The entity with undefined and null values handled.
53
+ */
54
+ static unPrepareEntity(entity, removeProperties) {
55
+ const nonNullEntity = ObjectHelper.removeEmptyProperties(entity, { removeNull: true });
56
+ if (Is.arrayValue(removeProperties)) {
57
+ for (const property of removeProperties) {
58
+ ObjectHelper.propertyDelete(nonNullEntity, property);
59
+ }
60
+ }
61
+ return nonNullEntity;
62
+ }
63
+ /**
64
+ * Validate that every sort property in the list is indexed in the schema (isPrimary, isSecondary,
65
+ * or has a default sortDirection), throwing sortNotIndexed for the first violation found.
66
+ * @param schema The entity schema to validate against.
67
+ * @param sortProperties The sort properties to check.
68
+ * @throws GeneralError If a sort property is not indexed in the schema.
69
+ */
70
+ static validateSortProperties(schema, sortProperties) {
71
+ if (Is.arrayValue(sortProperties)) {
72
+ for (const sortProperty of sortProperties) {
73
+ const propertySchema = schema.properties?.find(p => p.property === sortProperty.property);
74
+ if (Is.undefined(propertySchema) ||
75
+ (!propertySchema.isPrimary &&
76
+ !propertySchema.isSecondary &&
77
+ Is.empty(propertySchema.sortDirection))) {
78
+ throw new GeneralError(EntityStorageHelper.CLASS_NAME, "sortNotIndexed", {
79
+ property: sortProperty.property
80
+ });
81
+ }
82
+ }
83
+ }
84
+ }
85
+ /**
86
+ * Validate that every property in the list exists in the schema, throwing propertyNotInSchema
87
+ * for the first property that is not found.
88
+ * @param schema The entity schema to validate against.
89
+ * @param properties The properties to check.
90
+ * @throws GeneralError If a property does not exist in the schema.
91
+ */
92
+ static validateProperties(schema, properties) {
93
+ if (Is.arrayValue(properties)) {
94
+ for (const property of properties) {
95
+ const propertySchema = schema.properties?.find(p => p.property === property);
96
+ if (Is.undefined(propertySchema)) {
97
+ throw new GeneralError(EntityStorageHelper.CLASS_NAME, "propertyNotInSchema", {
98
+ property
99
+ });
100
+ }
101
+ }
102
+ }
103
+ }
104
+ /**
105
+ * Deep-clone condition tree and normalise null/undefined to undefined on Equals/NotEquals leaves
106
+ * so in-memory evaluation matches stored-absent semantics (optional absent props are omitted/undefined).
107
+ * @param condition The user-supplied condition (not mutated).
108
+ * @returns A clone safe to pass to check.
109
+ */
110
+ static normalizeConditionValues(condition) {
111
+ if ("conditions" in condition) {
112
+ return {
113
+ ...condition,
114
+ conditions: condition.conditions.map(c => EntityStorageHelper.normalizeConditionValues(c))
115
+ };
116
+ }
117
+ const leaf = condition;
118
+ if ((leaf.comparison === ComparisonOperator.Equals ||
119
+ leaf.comparison === ComparisonOperator.NotEquals) &&
120
+ (leaf.value === undefined || leaf.value === null)) {
121
+ return { ...leaf, value: undefined };
122
+ }
123
+ return { ...leaf };
124
+ }
125
+ }
126
+ //# 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 { GeneralError, Is, ObjectHelper } from \"@twin.org/core\";\nimport {\n\tComparisonOperator,\n\ttype EntityCondition,\n\tEntitySchemaHelper,\n\ttype IEntitySchema,\n\ttype SortDirection\n} from \"@twin.org/entity\";\nimport { nameof } from \"@twin.org/nameof\";\n\n/**\n * Helper class for performing schema migrations between two connectors.\n */\nexport class EntityStorageHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<EntityStorageHelper>();\n\n\t/**\n\t * Prepare the entity by handling undefined and null values and validating it against the schema.\n\t * @param entity The entity to handle undefined and null values for.\n\t * @param schema The schema to validate the entity against.\n\t * @param additionalProperties Optional list of additional properties to set on the entity.\n\t * @param options Options controlling how null/undefined optional properties are stored.\n\t * @param options.nullBehavior \"omit\" strips null/undefined optional properties before writing\n\t * (NoSQL — avoids index-key type errors). \"nullify\" converts undefined to null (SQL — the default).\n\t * @returns The entity with undefined and null values handled.\n\t */\n\tpublic static prepareEntity<T>(\n\t\tentity: T,\n\t\tschema: IEntitySchema<T>,\n\t\tadditionalProperties?: { property: string; value: unknown }[],\n\t\toptions?: { nullBehavior?: \"omit\" | \"nullify\" }\n\t): T {\n\t\tconst entityForValidation = ObjectHelper.clone(entity);\n\t\tEntitySchemaHelper.validateEntity(entityForValidation, schema);\n\n\t\tif (Is.arrayValue(schema.properties)) {\n\t\t\tfor (const property of schema.properties) {\n\t\t\t\tif (property.optional ?? false) {\n\t\t\t\t\tconst propValue = entityForValidation[property.property];\n\t\t\t\t\tif (options?.nullBehavior === \"omit\") {\n\t\t\t\t\t\tif (propValue === undefined || propValue === null) {\n\t\t\t\t\t\t\tObjectHelper.propertyDelete(entityForValidation, property.property as string);\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if (propValue === undefined) {\n\t\t\t\t\t\tObjectHelper.propertySet(entityForValidation, property.property as string, null);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif (Is.arrayValue(additionalProperties)) {\n\t\t\tfor (const additionalProperty of additionalProperties) {\n\t\t\t\tObjectHelper.propertySet(\n\t\t\t\t\tentityForValidation,\n\t\t\t\t\tadditionalProperty.property,\n\t\t\t\t\tadditionalProperty.value\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn entityForValidation;\n\t}\n\n\t/**\n\t * Un-prepare the entity by removing null values.\n\t * @param entity The entity to handle undefined and null values for.\n\t * @param removeProperties Optional list of properties to remove from the entity.\n\t * @returns The entity with undefined and null values handled.\n\t */\n\tpublic static unPrepareEntity<T>(entity: Partial<T> | undefined, removeProperties?: string[]): T {\n\t\tconst nonNullEntity = ObjectHelper.removeEmptyProperties(entity, { removeNull: true });\n\n\t\tif (Is.arrayValue(removeProperties)) {\n\t\t\tfor (const property of removeProperties) {\n\t\t\t\tObjectHelper.propertyDelete(nonNullEntity, property);\n\t\t\t}\n\t\t}\n\n\t\treturn nonNullEntity as T;\n\t}\n\n\t/**\n\t * Validate that every sort property in the list is indexed in the schema (isPrimary, isSecondary,\n\t * or has a default sortDirection), throwing sortNotIndexed for the first violation found.\n\t * @param schema The entity schema to validate against.\n\t * @param sortProperties The sort properties to check.\n\t * @throws GeneralError If a sort property is not indexed in the schema.\n\t */\n\tpublic static validateSortProperties<T>(\n\t\tschema: IEntitySchema<T>,\n\t\tsortProperties?: { property: keyof T; sortDirection: SortDirection }[]\n\t): void {\n\t\tif (Is.arrayValue(sortProperties)) {\n\t\t\tfor (const sortProperty of sortProperties) {\n\t\t\t\tconst propertySchema = schema.properties?.find(p => p.property === sortProperty.property);\n\t\t\t\tif (\n\t\t\t\t\tIs.undefined(propertySchema) ||\n\t\t\t\t\t(!propertySchema.isPrimary &&\n\t\t\t\t\t\t!propertySchema.isSecondary &&\n\t\t\t\t\t\tIs.empty(propertySchema.sortDirection))\n\t\t\t\t) {\n\t\t\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"sortNotIndexed\", {\n\t\t\t\t\t\tproperty: sortProperty.property\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Validate that every property in the list exists in the schema, throwing propertyNotInSchema\n\t * for the first property that is not found.\n\t * @param schema The entity schema to validate against.\n\t * @param properties The properties to check.\n\t * @throws GeneralError If a property does not exist in the schema.\n\t */\n\tpublic static validateProperties<T>(schema: IEntitySchema<T>, properties?: (keyof T)[]): void {\n\t\tif (Is.arrayValue(properties)) {\n\t\t\tfor (const property of properties) {\n\t\t\t\tconst propertySchema = schema.properties?.find(p => p.property === property);\n\t\t\t\tif (Is.undefined(propertySchema)) {\n\t\t\t\t\tthrow new GeneralError(EntityStorageHelper.CLASS_NAME, \"propertyNotInSchema\", {\n\t\t\t\t\t\tproperty\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Deep-clone condition tree and normalise null/undefined to undefined on Equals/NotEquals leaves\n\t * so in-memory evaluation matches stored-absent semantics (optional absent props are omitted/undefined).\n\t * @param condition The user-supplied condition (not mutated).\n\t * @returns A clone safe to pass to check.\n\t */\n\tpublic static normalizeConditionValues<T>(condition: EntityCondition<T>): EntityCondition<T> {\n\t\tif (\"conditions\" in condition) {\n\t\t\treturn {\n\t\t\t\t...condition,\n\t\t\t\tconditions: condition.conditions.map(c => EntityStorageHelper.normalizeConditionValues(c))\n\t\t\t};\n\t\t}\n\n\t\tconst leaf = condition;\n\t\tif (\n\t\t\t(leaf.comparison === ComparisonOperator.Equals ||\n\t\t\t\tleaf.comparison === ComparisonOperator.NotEquals) &&\n\t\t\t(leaf.value === undefined || leaf.value === null)\n\t\t) {\n\t\t\treturn { ...leaf, value: undefined };\n\t\t}\n\t\treturn { ...leaf };\n\t}\n}\n"]}
@@ -0,0 +1,232 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ import { ContextIdStore } from "@twin.org/context";
4
+ import { BaseError, Coerce, ComponentFactory, GeneralError, Is, ObjectHelper } from "@twin.org/core";
5
+ import { EntitySchemaDiffHelper, EntitySchemaPropertyType } from "@twin.org/entity";
6
+ /**
7
+ * Helper class for performing entity schema migrations between two connectors.
8
+ * The chain-based API (migrateWithChain / applyEntityChain) is the single migration
9
+ * path: a chain of one step covers the same case as a traditional single-step migration.
10
+ */
11
+ export class MigrationHelper {
12
+ /**
13
+ * Runtime name for the class.
14
+ */
15
+ static CLASS_NAME = "MigrationHelper";
16
+ /**
17
+ * Performs a chain migration in a single connector swap, regardless of how many version
18
+ * steps the chain spans. Creates one target connector, reads all source entities, applies
19
+ * applyEntityChain to each, writes them to the target, then finalizes the migration.
20
+ * A chain of one step is equivalent to a traditional single-step migration.
21
+ * @param sourceConnector The connector holding data at the stored schema version.
22
+ * @param targetSchemaName The schema name for the current version (used to create the target connector).
23
+ * @param steps Ordered, fully-resolved migration steps from stored to current version.
24
+ * @param options Optional migration options.
25
+ * @param loggingComponentType The optional component type to use for logging the migration progress.
26
+ * @returns The finalized connector and the count of migrated entities.
27
+ */
28
+ static async migrateWithChain(sourceConnector, targetSchemaName, steps, options, loggingComponentType) {
29
+ let targetConnector;
30
+ const logging = ComponentFactory.getIfExists(loggingComponentType);
31
+ try {
32
+ await logging?.log({
33
+ source: MigrationHelper.CLASS_NAME,
34
+ level: "info",
35
+ message: "migrateSchemaStarting",
36
+ data: {
37
+ schemaName: targetSchemaName
38
+ }
39
+ });
40
+ targetConnector = await sourceConnector.createTargetConnector(targetSchemaName);
41
+ await MigrationHelper.startupConnector(sourceConnector, loggingComponentType);
42
+ await MigrationHelper.startupConnector(targetConnector, loggingComponentType);
43
+ let partitionContextIds = await sourceConnector.getPartitionContextIds();
44
+ if (!Is.arrayValue(partitionContextIds)) {
45
+ partitionContextIds ??= [];
46
+ partitionContextIds.push({});
47
+ }
48
+ let migrated = 0;
49
+ const effectivePartitions = partitionContextIds.length > 0 ? partitionContextIds : [{}];
50
+ await options?.onProgress?.("partitionStart", effectivePartitions.length, 0);
51
+ const resolvedTarget = targetConnector;
52
+ for (let i = 0; i < effectivePartitions.length; i++) {
53
+ await options?.onProgress?.("partitionProgress", effectivePartitions.length, i);
54
+ await ContextIdStore.run(effectivePartitions[i], async () => {
55
+ migrated += await MigrationHelper.migratePartitionWithChain(sourceConnector, resolvedTarget, steps, options);
56
+ });
57
+ }
58
+ await options?.onProgress?.("partitionEnd", effectivePartitions.length, effectivePartitions.length);
59
+ await logging?.log({
60
+ source: MigrationHelper.CLASS_NAME,
61
+ level: "info",
62
+ message: "migrateSchemaFinalizing",
63
+ data: {
64
+ schemaName: targetSchemaName
65
+ }
66
+ });
67
+ const finalConnector = await sourceConnector.finalizeMigration(targetConnector, options, loggingComponentType);
68
+ await logging?.log({
69
+ source: MigrationHelper.CLASS_NAME,
70
+ level: "info",
71
+ message: "migrateSchemaComplete",
72
+ data: {
73
+ schemaName: targetSchemaName
74
+ }
75
+ });
76
+ return { finalConnector, migrated };
77
+ }
78
+ catch (error) {
79
+ await sourceConnector.cleanupMigration(targetConnector, options, loggingComponentType);
80
+ await logging?.log({
81
+ source: MigrationHelper.CLASS_NAME,
82
+ level: "error",
83
+ message: "migrateSchemaFailed",
84
+ data: {
85
+ schemaName: targetSchemaName
86
+ },
87
+ error: BaseError.fromError(error)
88
+ });
89
+ throw new GeneralError(MigrationHelper.CLASS_NAME, "migrateSchemaFailed", { schemaName: targetSchemaName }, error);
90
+ }
91
+ }
92
+ /**
93
+ * Reads all entities from one partition of the source connector, applies the migration
94
+ * chain to each entity, and writes the results to the target connector.
95
+ * @param source The connector to read from (already bootstrapped).
96
+ * @param target The connector to write to (already bootstrapped).
97
+ * @param steps Ordered, fully-resolved migration steps.
98
+ * @param options Optional migration options (batchSize, progress callbacks, transformEntityProperty).
99
+ * @returns The number of entities migrated.
100
+ */
101
+ static async migratePartitionWithChain(source, target, steps, options) {
102
+ let migrated = 0;
103
+ let cursor;
104
+ const totalEntities = await source.count();
105
+ if (totalEntities > 0) {
106
+ await options?.onProgress?.("partitionItemsStart", totalEntities, 0);
107
+ do {
108
+ const page = await source.query(undefined, undefined, undefined, cursor, options?.batchSize);
109
+ cursor = page.cursor;
110
+ if (Is.arrayValue(page.entities)) {
111
+ const transformedBatch = page.entities.map(entity => MigrationHelper.applyEntityChain(entity, steps));
112
+ await target.setBatch(transformedBatch);
113
+ migrated += transformedBatch.length;
114
+ }
115
+ await options?.onProgress?.("partitionItemsProgress", totalEntities, migrated);
116
+ } while (Is.stringValue(cursor));
117
+ await options?.onProgress?.("partitionItemsEnd", totalEntities, totalEntities);
118
+ }
119
+ return migrated;
120
+ }
121
+ /**
122
+ * Transforms a single entity through an ordered chain of fully-resolved migration steps.
123
+ * For each step the method diffs fromProperties against toProperties, then applies
124
+ * applyEntityTransform. Each step's output feeds the next step's input so that
125
+ * per-step transformEntityProperty hooks are honoured throughout the chain.
126
+ * @param entity The entity to transform (at the shape described by steps[0].fromProperties).
127
+ * @param steps Ordered, fully-resolved migration steps from stored version to current version.
128
+ * Each step's fromProperties and toProperties are resolved by the caller before invocation.
129
+ * @returns The entity transformed to the shape described by steps[last].toProperties.
130
+ */
131
+ static applyEntityChain(entity, steps) {
132
+ let current = entity;
133
+ for (const step of steps) {
134
+ const diff = EntitySchemaDiffHelper.diff(step.fromProperties, step.toProperties, step.renames);
135
+ current = MigrationHelper.applyEntityTransform(current, diff, step.transformEntityProperty);
136
+ }
137
+ return current;
138
+ }
139
+ /**
140
+ * Applies the entity transformation for a single diff, handling added, removed, and
141
+ * modified properties according to the provided schema diff and optional transform hook.
142
+ * @param entity The entity to transform.
143
+ * @param schemaDiff The schema diff between the old and new schemas.
144
+ * @param transformEntityProperty Optional per-property transform hook for object/array properties.
145
+ * @returns The transformed entity ready to be written to the new schema.
146
+ * @throws GeneralError if a transformation is required for an object or array property but no transformEntityProperty function is provided.
147
+ * @throws GeneralError if coercion of a modified property results in undefined for a non-optional target property.
148
+ */
149
+ static applyEntityTransform(entity, schemaDiff, transformEntityProperty) {
150
+ const newEntity = {};
151
+ for (const property of schemaDiff.unchanged) {
152
+ ObjectHelper.propertySet(newEntity, property.property, ObjectHelper.propertyGet(entity, property.property));
153
+ }
154
+ for (const change of schemaDiff.added) {
155
+ let defValue;
156
+ if (!(change.optional ?? false)) {
157
+ if (change.type === EntitySchemaPropertyType.Boolean) {
158
+ defValue = change.defaultValue ?? false;
159
+ }
160
+ else if (change.type === EntitySchemaPropertyType.Number ||
161
+ change.type === EntitySchemaPropertyType.Integer) {
162
+ defValue = change.defaultValue ?? 0;
163
+ }
164
+ else if (change.type === EntitySchemaPropertyType.String) {
165
+ defValue = change.defaultValue ?? "";
166
+ }
167
+ else if (change.type === EntitySchemaPropertyType.Array) {
168
+ defValue = change.defaultValue ?? [];
169
+ }
170
+ else if (change.type === EntitySchemaPropertyType.Object) {
171
+ defValue = change.defaultValue ?? {};
172
+ }
173
+ }
174
+ if (Is.notEmpty(defValue)) {
175
+ ObjectHelper.propertySet(newEntity, change.property, defValue);
176
+ }
177
+ }
178
+ for (const change of schemaDiff.modified) {
179
+ const currentValue = ObjectHelper.propertyGet(entity, change.from.property);
180
+ let newValue;
181
+ if (change.to.type === EntitySchemaPropertyType.Boolean) {
182
+ newValue = Coerce.boolean(currentValue);
183
+ }
184
+ else if (change.to.type === EntitySchemaPropertyType.Number ||
185
+ change.to.type === EntitySchemaPropertyType.Integer) {
186
+ newValue = Coerce.number(currentValue);
187
+ }
188
+ else if (change.to.type === EntitySchemaPropertyType.String) {
189
+ newValue = Coerce.string(currentValue);
190
+ }
191
+ else if (change.to.type === EntitySchemaPropertyType.Array ||
192
+ change.to.type === EntitySchemaPropertyType.Object) {
193
+ if (!Is.function(transformEntityProperty)) {
194
+ throw new GeneralError(MigrationHelper.CLASS_NAME, "transformRequiredForProperty", {
195
+ from: change.from.property,
196
+ to: change.to.property,
197
+ type: change.from.type
198
+ });
199
+ }
200
+ newValue = transformEntityProperty(change.from, change.to, currentValue);
201
+ }
202
+ if (newValue === undefined && !(change.to.optional ?? false)) {
203
+ throw new GeneralError(MigrationHelper.CLASS_NAME, "coercionProducedUndefined", {
204
+ property: change.to.property,
205
+ type: change.to.type
206
+ });
207
+ }
208
+ if (Is.notEmpty(newValue)) {
209
+ ObjectHelper.propertySet(newEntity, change.to.property, newValue);
210
+ }
211
+ }
212
+ // Removed properties are simply dropped.
213
+ return newEntity;
214
+ }
215
+ /**
216
+ * Starts the connector by calling bootstrap and start if they are defined.
217
+ * @param connector The connector to start.
218
+ * @param loggingComponentType The optional component type to use for logging the migration progress.
219
+ * @internal
220
+ */
221
+ static async startupConnector(connector, loggingComponentType) {
222
+ const bootstrap = connector.bootstrap?.bind(connector);
223
+ if (Is.function(bootstrap)) {
224
+ await bootstrap(loggingComponentType);
225
+ }
226
+ const start = connector.start?.bind(connector);
227
+ if (Is.function(start)) {
228
+ await start(loggingComponentType);
229
+ }
230
+ }
231
+ }
232
+ //# sourceMappingURL=migrationHelper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"migrationHelper.js","sourceRoot":"","sources":["../../../src/helpers/migrationHelper.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport { ContextIdStore } from \"@twin.org/context\";\nimport {\n\tBaseError,\n\tCoerce,\n\tComponentFactory,\n\tGeneralError,\n\tIs,\n\tObjectHelper\n} from \"@twin.org/core\";\nimport {\n\tEntitySchemaDiffHelper,\n\tEntitySchemaPropertyType,\n\ttype IEntitySchemaDiff\n} from \"@twin.org/entity\";\nimport type { ILoggingComponent } from \"@twin.org/logging-models\";\nimport { nameof } from \"@twin.org/nameof\";\nimport type { EntityPropertyTransformer } from \"../models/entityPropertyTransformer.js\";\nimport type { IEntityStorageConnector } from \"../models/IEntityStorageConnector.js\";\nimport type { IEntityStorageMigrationConnector } from \"../models/IEntityStorageMigrationConnector.js\";\nimport type { IMigrationOptions } from \"../models/IMigrationOptions.js\";\nimport type { IResolvedMigrationStep } from \"../models/IResolvedMigrationStep.js\";\n\n/**\n * Helper class for performing entity schema migrations between two connectors.\n * The chain-based API (migrateWithChain / applyEntityChain) is the single migration\n * path: a chain of one step covers the same case as a traditional single-step migration.\n */\nexport class MigrationHelper {\n\t/**\n\t * Runtime name for the class.\n\t */\n\tpublic static readonly CLASS_NAME: string = nameof<MigrationHelper>();\n\n\t/**\n\t * Performs a chain migration in a single connector swap, regardless of how many version\n\t * steps the chain spans. Creates one target connector, reads all source entities, applies\n\t * applyEntityChain to each, writes them to the target, then finalizes the migration.\n\t * A chain of one step is equivalent to a traditional single-step migration.\n\t * @param sourceConnector The connector holding data at the stored schema version.\n\t * @param targetSchemaName The schema name for the current version (used to create the target connector).\n\t * @param steps Ordered, fully-resolved migration steps from stored to current version.\n\t * @param options Optional migration options.\n\t * @param loggingComponentType The optional component type to use for logging the migration progress.\n\t * @returns The finalized connector and the count of migrated entities.\n\t */\n\tpublic static async migrateWithChain(\n\t\tsourceConnector: IEntityStorageMigrationConnector,\n\t\ttargetSchemaName: string,\n\t\tsteps: IResolvedMigrationStep[],\n\t\toptions?: IMigrationOptions,\n\t\tloggingComponentType?: string\n\t): Promise<{\n\t\tfinalConnector: IEntityStorageConnector;\n\t\tmigrated: number;\n\t}> {\n\t\tlet targetConnector: IEntityStorageConnector | undefined;\n\t\tconst logging = ComponentFactory.getIfExists<ILoggingComponent>(loggingComponentType);\n\n\t\ttry {\n\t\t\tawait logging?.log({\n\t\t\t\tsource: MigrationHelper.CLASS_NAME,\n\t\t\t\tlevel: \"info\",\n\t\t\t\tmessage: \"migrateSchemaStarting\",\n\t\t\t\tdata: {\n\t\t\t\t\tschemaName: targetSchemaName\n\t\t\t\t}\n\t\t\t});\n\n\t\t\ttargetConnector = await sourceConnector.createTargetConnector(targetSchemaName);\n\n\t\t\tawait MigrationHelper.startupConnector(sourceConnector, loggingComponentType);\n\t\t\tawait MigrationHelper.startupConnector(targetConnector, loggingComponentType);\n\n\t\t\tlet partitionContextIds = await sourceConnector.getPartitionContextIds();\n\t\t\tif (!Is.arrayValue(partitionContextIds)) {\n\t\t\t\tpartitionContextIds ??= [];\n\t\t\t\tpartitionContextIds.push({});\n\t\t\t}\n\n\t\t\tlet migrated = 0;\n\t\t\tconst effectivePartitions = partitionContextIds.length > 0 ? partitionContextIds : [{}];\n\n\t\t\tawait options?.onProgress?.(\"partitionStart\", effectivePartitions.length, 0);\n\n\t\t\tconst resolvedTarget = targetConnector;\n\t\t\tfor (let i = 0; i < effectivePartitions.length; i++) {\n\t\t\t\tawait options?.onProgress?.(\"partitionProgress\", effectivePartitions.length, i);\n\n\t\t\t\tawait ContextIdStore.run(effectivePartitions[i], async () => {\n\t\t\t\t\tmigrated += await MigrationHelper.migratePartitionWithChain(\n\t\t\t\t\t\tsourceConnector,\n\t\t\t\t\t\tresolvedTarget,\n\t\t\t\t\t\tsteps,\n\t\t\t\t\t\toptions\n\t\t\t\t\t);\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tawait options?.onProgress?.(\n\t\t\t\t\"partitionEnd\",\n\t\t\t\teffectivePartitions.length,\n\t\t\t\teffectivePartitions.length\n\t\t\t);\n\n\t\t\tawait logging?.log({\n\t\t\t\tsource: MigrationHelper.CLASS_NAME,\n\t\t\t\tlevel: \"info\",\n\t\t\t\tmessage: \"migrateSchemaFinalizing\",\n\t\t\t\tdata: {\n\t\t\t\t\tschemaName: targetSchemaName\n\t\t\t\t}\n\t\t\t});\n\n\t\t\tconst finalConnector = await sourceConnector.finalizeMigration(\n\t\t\t\ttargetConnector,\n\t\t\t\toptions,\n\t\t\t\tloggingComponentType\n\t\t\t);\n\n\t\t\tawait logging?.log({\n\t\t\t\tsource: MigrationHelper.CLASS_NAME,\n\t\t\t\tlevel: \"info\",\n\t\t\t\tmessage: \"migrateSchemaComplete\",\n\t\t\t\tdata: {\n\t\t\t\t\tschemaName: targetSchemaName\n\t\t\t\t}\n\t\t\t});\n\n\t\t\treturn { finalConnector, migrated };\n\t\t} catch (error) {\n\t\t\tawait sourceConnector.cleanupMigration(targetConnector, options, loggingComponentType);\n\n\t\t\tawait logging?.log({\n\t\t\t\tsource: MigrationHelper.CLASS_NAME,\n\t\t\t\tlevel: \"error\",\n\t\t\t\tmessage: \"migrateSchemaFailed\",\n\t\t\t\tdata: {\n\t\t\t\t\tschemaName: targetSchemaName\n\t\t\t\t},\n\t\t\t\terror: BaseError.fromError(error)\n\t\t\t});\n\t\t\tthrow new GeneralError(\n\t\t\t\tMigrationHelper.CLASS_NAME,\n\t\t\t\t\"migrateSchemaFailed\",\n\t\t\t\t{ schemaName: targetSchemaName },\n\t\t\t\terror\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Reads all entities from one partition of the source connector, applies the migration\n\t * chain to each entity, and writes the results to the target connector.\n\t * @param source The connector to read from (already bootstrapped).\n\t * @param target The connector to write to (already bootstrapped).\n\t * @param steps Ordered, fully-resolved migration steps.\n\t * @param options Optional migration options (batchSize, progress callbacks, transformEntityProperty).\n\t * @returns The number of entities migrated.\n\t */\n\tpublic static async migratePartitionWithChain(\n\t\tsource: IEntityStorageMigrationConnector,\n\t\ttarget: IEntityStorageConnector,\n\t\tsteps: IResolvedMigrationStep[],\n\t\toptions?: IMigrationOptions\n\t): Promise<number> {\n\t\tlet migrated = 0;\n\t\tlet cursor: string | undefined;\n\t\tconst totalEntities = await source.count();\n\n\t\tif (totalEntities > 0) {\n\t\t\tawait options?.onProgress?.(\"partitionItemsStart\", totalEntities, 0);\n\n\t\t\tdo {\n\t\t\t\tconst page = await source.query(\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tundefined,\n\t\t\t\t\tcursor,\n\t\t\t\t\toptions?.batchSize\n\t\t\t\t);\n\t\t\t\tcursor = page.cursor;\n\n\t\t\t\tif (Is.arrayValue(page.entities)) {\n\t\t\t\t\tconst transformedBatch: unknown[] = page.entities.map(entity =>\n\t\t\t\t\t\tMigrationHelper.applyEntityChain(entity, steps)\n\t\t\t\t\t);\n\t\t\t\t\tawait target.setBatch(transformedBatch);\n\t\t\t\t\tmigrated += transformedBatch.length;\n\t\t\t\t}\n\n\t\t\t\tawait options?.onProgress?.(\"partitionItemsProgress\", totalEntities, migrated);\n\t\t\t} while (Is.stringValue(cursor));\n\n\t\t\tawait options?.onProgress?.(\"partitionItemsEnd\", totalEntities, totalEntities);\n\t\t}\n\n\t\treturn migrated;\n\t}\n\n\t/**\n\t * Transforms a single entity through an ordered chain of fully-resolved migration steps.\n\t * For each step the method diffs fromProperties against toProperties, then applies\n\t * applyEntityTransform. Each step's output feeds the next step's input so that\n\t * per-step transformEntityProperty hooks are honoured throughout the chain.\n\t * @param entity The entity to transform (at the shape described by steps[0].fromProperties).\n\t * @param steps Ordered, fully-resolved migration steps from stored version to current version.\n\t * Each step's fromProperties and toProperties are resolved by the caller before invocation.\n\t * @returns The entity transformed to the shape described by steps[last].toProperties.\n\t */\n\tpublic static applyEntityChain(entity: unknown, steps: IResolvedMigrationStep[]): unknown {\n\t\tlet current: unknown = entity;\n\t\tfor (const step of steps) {\n\t\t\tconst diff = EntitySchemaDiffHelper.diff(\n\t\t\t\tstep.fromProperties,\n\t\t\t\tstep.toProperties,\n\t\t\t\tstep.renames\n\t\t\t);\n\t\t\tcurrent = MigrationHelper.applyEntityTransform(\n\t\t\t\tcurrent as Partial<unknown>,\n\t\t\t\tdiff,\n\t\t\t\tstep.transformEntityProperty\n\t\t\t);\n\t\t}\n\t\treturn current;\n\t}\n\n\t/**\n\t * Applies the entity transformation for a single diff, handling added, removed, and\n\t * modified properties according to the provided schema diff and optional transform hook.\n\t * @param entity The entity to transform.\n\t * @param schemaDiff The schema diff between the old and new schemas.\n\t * @param transformEntityProperty Optional per-property transform hook for object/array properties.\n\t * @returns The transformed entity ready to be written to the new schema.\n\t * @throws GeneralError if a transformation is required for an object or array property but no transformEntityProperty function is provided.\n\t * @throws GeneralError if coercion of a modified property results in undefined for a non-optional target property.\n\t */\n\tpublic static applyEntityTransform<T = unknown, U = unknown>(\n\t\tentity: Partial<T>,\n\t\tschemaDiff: IEntitySchemaDiff<T, U>,\n\t\ttransformEntityProperty?: EntityPropertyTransformer<T, U>\n\t): U {\n\t\tconst newEntity = {} as U;\n\n\t\tfor (const property of schemaDiff.unchanged) {\n\t\t\tObjectHelper.propertySet(\n\t\t\t\tnewEntity,\n\t\t\t\tproperty.property as string,\n\t\t\t\tObjectHelper.propertyGet(entity, property.property as string)\n\t\t\t);\n\t\t}\n\n\t\tfor (const change of schemaDiff.added) {\n\t\t\tlet defValue;\n\n\t\t\tif (!(change.optional ?? false)) {\n\t\t\t\tif (change.type === EntitySchemaPropertyType.Boolean) {\n\t\t\t\t\tdefValue = change.defaultValue ?? false;\n\t\t\t\t} else if (\n\t\t\t\t\tchange.type === EntitySchemaPropertyType.Number ||\n\t\t\t\t\tchange.type === EntitySchemaPropertyType.Integer\n\t\t\t\t) {\n\t\t\t\t\tdefValue = change.defaultValue ?? 0;\n\t\t\t\t} else if (change.type === EntitySchemaPropertyType.String) {\n\t\t\t\t\tdefValue = change.defaultValue ?? \"\";\n\t\t\t\t} else if (change.type === EntitySchemaPropertyType.Array) {\n\t\t\t\t\tdefValue = change.defaultValue ?? [];\n\t\t\t\t} else if (change.type === EntitySchemaPropertyType.Object) {\n\t\t\t\t\tdefValue = change.defaultValue ?? {};\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (Is.notEmpty(defValue)) {\n\t\t\t\tObjectHelper.propertySet(newEntity, change.property as string, defValue);\n\t\t\t}\n\t\t}\n\n\t\tfor (const change of schemaDiff.modified) {\n\t\t\tconst currentValue = ObjectHelper.propertyGet(entity, change.from.property as string);\n\t\t\tlet newValue;\n\n\t\t\tif (change.to.type === EntitySchemaPropertyType.Boolean) {\n\t\t\t\tnewValue = Coerce.boolean(currentValue);\n\t\t\t} else if (\n\t\t\t\tchange.to.type === EntitySchemaPropertyType.Number ||\n\t\t\t\tchange.to.type === EntitySchemaPropertyType.Integer\n\t\t\t) {\n\t\t\t\tnewValue = Coerce.number(currentValue);\n\t\t\t} else if (change.to.type === EntitySchemaPropertyType.String) {\n\t\t\t\tnewValue = Coerce.string(currentValue);\n\t\t\t} else if (\n\t\t\t\tchange.to.type === EntitySchemaPropertyType.Array ||\n\t\t\t\tchange.to.type === EntitySchemaPropertyType.Object\n\t\t\t) {\n\t\t\t\tif (!Is.function(transformEntityProperty)) {\n\t\t\t\t\tthrow new GeneralError(MigrationHelper.CLASS_NAME, \"transformRequiredForProperty\", {\n\t\t\t\t\t\tfrom: change.from.property,\n\t\t\t\t\t\tto: change.to.property,\n\t\t\t\t\t\ttype: change.from.type\n\t\t\t\t\t});\n\t\t\t\t}\n\n\t\t\t\tnewValue = transformEntityProperty(change.from, change.to, currentValue);\n\t\t\t}\n\n\t\t\tif (newValue === undefined && !(change.to.optional ?? false)) {\n\t\t\t\tthrow new GeneralError(MigrationHelper.CLASS_NAME, \"coercionProducedUndefined\", {\n\t\t\t\t\tproperty: change.to.property,\n\t\t\t\t\ttype: change.to.type\n\t\t\t\t});\n\t\t\t}\n\n\t\t\tif (Is.notEmpty(newValue)) {\n\t\t\t\tObjectHelper.propertySet(newEntity, change.to.property as string, newValue);\n\t\t\t}\n\t\t}\n\n\t\t// Removed properties are simply dropped.\n\n\t\treturn newEntity;\n\t}\n\n\t/**\n\t * Starts the connector by calling bootstrap and start if they are defined.\n\t * @param connector The connector to start.\n\t * @param loggingComponentType The optional component type to use for logging the migration progress.\n\t * @internal\n\t */\n\tprivate static async startupConnector<T>(\n\t\tconnector: IEntityStorageConnector<T>,\n\t\tloggingComponentType?: string\n\t): Promise<void> {\n\t\tconst bootstrap = connector.bootstrap?.bind(connector);\n\t\tif (Is.function(bootstrap)) {\n\t\t\tawait bootstrap(loggingComponentType);\n\t\t}\n\t\tconst start = connector.start?.bind(connector);\n\t\tif (Is.function(start)) {\n\t\t\tawait start(loggingComponentType);\n\t\t}\n\t}\n}\n"]}
package/dist/es/index.js CHANGED
@@ -1,12 +1,25 @@
1
- // Copyright 2024 IOTA Stiftung.
1
+ // Copyright 2026 IOTA Stiftung.
2
2
  // SPDX-License-Identifier: Apache-2.0.
3
3
  export * from "./factories/entityStorageConnectorFactory.js";
4
+ export * from "./factories/schemaMigrationFactory.js";
5
+ export * from "./helpers/entityStorageHelper.js";
6
+ export * from "./helpers/migrationHelper.js";
7
+ export * from "./models/api/IEntityStorageCountRequest.js";
8
+ export * from "./models/api/IEntityStorageCountResponse.js";
9
+ export * from "./models/api/IEntityStorageEmptyRequest.js";
4
10
  export * from "./models/api/IEntityStorageGetRequest.js";
5
11
  export * from "./models/api/IEntityStorageGetResponse.js";
6
12
  export * from "./models/api/IEntityStorageListRequest.js";
7
13
  export * from "./models/api/IEntityStorageListResponse.js";
14
+ export * from "./models/api/IEntityStorageRemoveBatchRequest.js";
8
15
  export * from "./models/api/IEntityStorageRemoveRequest.js";
16
+ export * from "./models/api/IEntityStorageSetBatchRequest.js";
9
17
  export * from "./models/api/IEntityStorageSetRequest.js";
18
+ export * from "./models/entityPropertyTransformer.js";
10
19
  export * from "./models/IEntityStorageComponent.js";
11
20
  export * from "./models/IEntityStorageConnector.js";
21
+ export * from "./models/IEntityStorageMigrationConnector.js";
22
+ export * from "./models/IMigrationOptions.js";
23
+ export * from "./models/IResolvedMigrationStep.js";
24
+ export * from "./models/ISchemaMigration.js";
12
25
  //# sourceMappingURL=index.js.map
@@ -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,0CAA0C,CAAC;AACzD,cAAc,2CAA2C,CAAC;AAC1D,cAAc,2CAA2C,CAAC;AAC1D,cAAc,4CAA4C,CAAC;AAC3D,cAAc,6CAA6C,CAAC;AAC5D,cAAc,0CAA0C,CAAC;AACzD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./factories/entityStorageConnectorFactory.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/IEntityStorageRemoveRequest.js\";\nexport * from \"./models/api/IEntityStorageSetRequest.js\";\nexport * from \"./models/IEntityStorageComponent.js\";\nexport * from \"./models/IEntityStorageConnector.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,uCAAuC,CAAC;AACtD,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,uCAAuC,CAAC;AACtD,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,+BAA+B,CAAC;AAC9C,cAAc,oCAAoC,CAAC;AACnD,cAAc,8BAA8B,CAAC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nexport * from \"./factories/entityStorageConnectorFactory.js\";\nexport * from \"./factories/schemaMigrationFactory.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/entityPropertyTransformer.js\";\nexport * from \"./models/IEntityStorageComponent.js\";\nexport * from \"./models/IEntityStorageConnector.js\";\nexport * from \"./models/IEntityStorageMigrationConnector.js\";\nexport * from \"./models/IMigrationOptions.js\";\nexport * from \"./models/IResolvedMigrationStep.js\";\nexport * from \"./models/ISchemaMigration.js\";\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IEntityStorageComponent.js","sourceRoot":"","sources":["../../../src/models/IEntityStorageComponent.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IComponent } from \"@twin.org/core\";\nimport type { EntityCondition, SortDirection } from \"@twin.org/entity\";\n\n/**\n * Interface describing an entity storage component.\n */\nexport interface IEntityStorageComponent<T = unknown> extends IComponent {\n\t/**\n\t * Set an entity.\n\t * @param entity The entity to set.\n\t * @returns The id of the entity.\n\t */\n\tset(entity: T): Promise<void>;\n\n\t/**\n\t * Get an entity.\n\t * @param id The id of the entity to get, or the index value if secondaryIndex is set.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tget(id: string, secondaryIndex?: keyof T): Promise<T | undefined>;\n\n\t/**\n\t * Remove the entity.\n\t * @param id The id of the entity to remove.\n\t * @returns Nothing.\n\t */\n\tremove(id: string): Promise<void>;\n\n\t/**\n\t * Query all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param orderBy The order for the results.\n\t * @param orderByDirection The direction for the order, defaults to ascending.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tquery(\n\t\tconditions?: EntityCondition<T>,\n\t\torderBy?: keyof T,\n\t\torderByDirection?: SortDirection,\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The entities, which can be partial if a limited keys list was provided.\n\t\t */\n\t\tentities: Partial<T>[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more entities.\n\t\t */\n\t\tcursor?: string;\n\t}>;\n}\n"]}
1
+ {"version":3,"file":"IEntityStorageComponent.js","sourceRoot":"","sources":["../../../src/models/IEntityStorageComponent.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IComponent } from \"@twin.org/core\";\nimport type { EntityCondition, SortDirection } from \"@twin.org/entity\";\n\n/**\n * Interface describing an entity storage component.\n */\nexport interface IEntityStorageComponent<T = unknown> extends IComponent {\n\t/**\n\t * Set an entity.\n\t * @param entity The entity to set.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The id of the entity.\n\t */\n\tset(entity: T, conditions?: { property: keyof T; value: unknown }[]): Promise<void>;\n\n\t/**\n\t * Set multiple entities in a batch.\n\t * @param entities The entities to set.\n\t * @returns Nothing.\n\t */\n\tsetBatch(entities: T[]): Promise<void>;\n\n\t/**\n\t * Get an entity.\n\t * @param id The id of the entity to get, or the index value if secondaryIndex is set.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tget(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined>;\n\n\t/**\n\t * Remove the entity.\n\t * @param id The id of the entity to remove.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns Nothing.\n\t */\n\tremove(id: string, conditions?: { property: keyof T; value: unknown }[]): Promise<void>;\n\n\t/**\n\t * Remove multiple entities by id.\n\t * @param ids The ids of the entities to remove.\n\t * @returns Nothing.\n\t */\n\tremoveBatch(ids: string[]): Promise<void>;\n\n\t/**\n\t * Query all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param orderBy The order for the results.\n\t * @param orderByDirection The direction for the order, defaults to ascending.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tquery(\n\t\tconditions?: EntityCondition<T>,\n\t\torderBy?: keyof T,\n\t\torderByDirection?: SortDirection,\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The entities, which can be partial if a limited keys list was provided.\n\t\t */\n\t\tentities: Partial<T>[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more entities.\n\t\t */\n\t\tcursor?: string;\n\t}>;\n\n\t/**\n\t * Remove all entities from the storage.\n\t * @returns Nothing.\n\t */\n\tempty(): Promise<void>;\n\n\t/**\n\t * Count all the entities which match the conditions.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The total count of entities in the storage.\n\t */\n\tcount(conditions?: EntityCondition<T>): Promise<number>;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"IEntityStorageConnector.js","sourceRoot":"","sources":["../../../src/models/IEntityStorageConnector.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IComponent } from \"@twin.org/core\";\nimport type { EntityCondition, IEntitySchema, SortDirection } from \"@twin.org/entity\";\n\n/**\n * Interface describing an entity storage connector.\n */\nexport interface IEntityStorageConnector<T = unknown> extends IComponent {\n\t/**\n\t * Get the schema for the entities.\n\t * @returns The schema for the entities.\n\t */\n\tgetSchema(): IEntitySchema;\n\n\t/**\n\t * Set an entity.\n\t * @param entity The entity to set.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The id of the entity.\n\t */\n\tset(entity: T, conditions?: { property: keyof T; value: unknown }[]): Promise<void>;\n\n\t/**\n\t * Get an entity.\n\t * @param id The id of the entity to get, or the index value if secondaryIndex is set.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tget(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined>;\n\n\t/**\n\t * Remove the entity.\n\t * @param id The id of the entity to remove.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns Nothing.\n\t */\n\tremove(id: string, conditions?: { property: keyof T; value: unknown }[]): Promise<void>;\n\n\t/**\n\t * Query all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param sortProperties The optional sort order.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tquery(\n\t\tconditions?: EntityCondition<T>,\n\t\tsortProperties?: {\n\t\t\tproperty: keyof T;\n\t\t\tsortDirection: SortDirection;\n\t\t}[],\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The entities, which can be partial if a limited keys list was provided.\n\t\t */\n\t\tentities: Partial<T>[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more entities.\n\t\t */\n\t\tcursor?: string;\n\t}>;\n}\n"]}
1
+ {"version":3,"file":"IEntityStorageConnector.js","sourceRoot":"","sources":["../../../src/models/IEntityStorageConnector.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IComponent } from \"@twin.org/core\";\nimport type { EntityCondition, IEntitySchema, SortDirection } from \"@twin.org/entity\";\n\n/**\n * Interface describing an entity storage connector.\n */\nexport interface IEntityStorageConnector<T = unknown> extends IComponent {\n\t/**\n\t * Get the schema for the entities.\n\t * @returns The schema for the entities.\n\t */\n\tgetSchema(): IEntitySchema;\n\n\t/**\n\t * Set an entity.\n\t * @param entity The entity to set.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The id of the entity.\n\t */\n\tset(entity: T, conditions?: { property: keyof T; value: unknown }[]): Promise<void>;\n\n\t/**\n\t * Set multiple entities in a batch.\n\t * @param entities The entities to set.\n\t * @returns Nothing.\n\t */\n\tsetBatch(entities: T[]): Promise<void>;\n\n\t/**\n\t * Get an entity.\n\t * @param id The id of the entity to get, or the index value if secondaryIndex is set.\n\t * @param secondaryIndex Get the item using a secondary index.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The object if it can be found or undefined.\n\t */\n\tget(\n\t\tid: string,\n\t\tsecondaryIndex?: keyof T,\n\t\tconditions?: { property: keyof T; value: unknown }[]\n\t): Promise<T | undefined>;\n\n\t/**\n\t * Remove the entity.\n\t * @param id The id of the entity to remove.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns Nothing.\n\t */\n\tremove(id: string, conditions?: { property: keyof T; value: unknown }[]): Promise<void>;\n\n\t/**\n\t * Remove multiple entities by id.\n\t * @param ids The ids of the entities to remove.\n\t * @returns Nothing.\n\t */\n\tremoveBatch(ids: string[]): Promise<void>;\n\n\t/**\n\t * Query all the entities which match the conditions.\n\t * @param conditions The conditions to match for the entities.\n\t * @param sortProperties The optional sort order.\n\t * @param properties The optional properties to return, defaults to all.\n\t * @param cursor The cursor to request the next chunk of entities.\n\t * @param limit The suggested number of entities to return in each chunk, in some scenarios can return a different amount.\n\t * @returns All the entities for the storage matching the conditions,\n\t * and a cursor which can be used to request more entities.\n\t */\n\tquery(\n\t\tconditions?: EntityCondition<T>,\n\t\tsortProperties?: {\n\t\t\tproperty: keyof T;\n\t\t\tsortDirection: SortDirection;\n\t\t}[],\n\t\tproperties?: (keyof T)[],\n\t\tcursor?: string,\n\t\tlimit?: number\n\t): Promise<{\n\t\t/**\n\t\t * The entities, which can be partial if a limited keys list was provided.\n\t\t */\n\t\tentities: Partial<T>[];\n\t\t/**\n\t\t * An optional cursor, when defined can be used to call find to get more entities.\n\t\t */\n\t\tcursor?: string;\n\t}>;\n\n\t/**\n\t * Remove all entities from the storage.\n\t * @returns Nothing.\n\t */\n\tempty(): Promise<void>;\n\n\t/**\n\t * Count all the entities which match the conditions.\n\t * @param conditions The optional conditions to match for the entities.\n\t * @returns The total count of entities in the storage.\n\t */\n\tcount(conditions?: EntityCondition<T>): Promise<number>;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IEntityStorageMigrationConnector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IEntityStorageMigrationConnector.js","sourceRoot":"","sources":["../../../src/models/IEntityStorageMigrationConnector.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IContextIds } from \"@twin.org/context\";\nimport type { IEntityStorageConnector } from \"./IEntityStorageConnector.js\";\nimport type { IMigrationOptions } from \"./IMigrationOptions.js\";\n\n/**\n * Interface describing an entity storage migration connector.\n */\nexport interface IEntityStorageMigrationConnector<T = unknown> extends IEntityStorageConnector<T> {\n\t/**\n\t * Get a unique list of all the context ids from the storage.\n\t * @returns The list of unique context ids.\n\t */\n\tgetPartitionContextIds(): Promise<IContextIds[]>;\n\n\t/**\n\t * Create the target connector for performing the migration it will use a temporary storage location.\n\t * @param newEntitySchema The name of the new entity schema to create the connector for.\n\t * @returns Connector for performing the migration.\n\t */\n\tcreateTargetConnector<U>(newEntitySchema: string): Promise<IEntityStorageConnector<U>>;\n\n\t/**\n\t * Finalize the migration by tearing down the old connector and replacing it with the target connector.\n\t * @param targetConnector The target connector to finalize the migration with.\n\t * @param options The options to control how the migration is finalized.\n\t * @param loggingComponentType The optional component type to use for logging the migration progress.\n\t * @returns A promise that resolves when the migration is finalized and returns the final connector.\n\t */\n\tfinalizeMigration<U>(\n\t\ttargetConnector: IEntityStorageConnector<U>,\n\t\toptions?: IMigrationOptions,\n\t\tloggingComponentType?: string\n\t): Promise<IEntityStorageConnector<U>>;\n\n\t/**\n\t * Cleanup the migration if a migration fails or needs to be aborted.\n\t * @param targetConnector The target connector to cleanup the migration with.\n\t * @param options The options to control how the migration is cleaned up.\n\t * @param loggingComponentType The optional component type to use for logging the migration progress.\n\t * @returns A promise that resolves when the migration is cleaned up.\n\t */\n\tcleanupMigration<U>(\n\t\ttargetConnector: IEntityStorageConnector<U> | undefined,\n\t\toptions?: IMigrationOptions,\n\t\tloggingComponentType?: string\n\t): Promise<void>;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2026 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IMigrationOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IMigrationOptions.js","sourceRoot":"","sources":["../../../src/models/IMigrationOptions.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Options controlling how a schema migration is executed.\n */\nexport interface IMigrationOptions {\n\t/**\n\t * Number of entities to read and write per batch.\n\t * @default 100\n\t */\n\tbatchSize?: number;\n\n\t/**\n\t * Called for progress tracking.\n\t * @param progressItem The item progress being updated.\n\t * @param itemTotal The total number of rows to migrate.\n\t * @param itemIndex The number of rows migrated so far.\n\t */\n\tonProgress?: (\n\t\tprogressItem:\n\t\t\t| \"partitionStart\"\n\t\t\t| \"partitionProgress\"\n\t\t\t| \"partitionEnd\"\n\t\t\t| \"partitionItemsStart\"\n\t\t\t| \"partitionItemsProgress\"\n\t\t\t| \"partitionItemsEnd\",\n\t\titemTotal: number,\n\t\titemIndex: number\n\t) => Promise<void>;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=IResolvedMigrationStep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IResolvedMigrationStep.js","sourceRoot":"","sources":["../../../src/models/IResolvedMigrationStep.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { IEntitySchemaProperty } from \"@twin.org/entity\";\nimport type { EntityPropertyTransformer } from \"./entityPropertyTransformer.js\";\n\n/**\n * A fully-resolved single migration step used by MigrationHelper.\n * The SchemaVersionService builds these by looking up versioned schema classes\n * from EntitySchemaFactory (e.g. MyEntityV0, MyEntityV1) before invoking the helper,\n * keeping factory knowledge out of the helper itself.\n * @template T The entity type. Defaults to `unknown`. Use a concrete entity type\n * when the step's source and target schemas are known at the call site.\n */\nexport interface IResolvedMigrationStep<T = unknown, U = unknown> {\n\t/**\n\t * The property list of the entity at the start of this step (the \"old\" shape).\n\t * Sourced from the versioned schema class registered in EntitySchemaFactory,\n\t * e.g. EntitySchemaFactory.get(\"MyEntityV0\").properties.\n\t */\n\tfromProperties: IEntitySchemaProperty<T>[];\n\n\t/**\n\t * The property list of the entity at the end of this step (the \"new\" shape).\n\t * For the final step this is the live current schema's properties.\n\t */\n\ttoProperties: IEntitySchemaProperty<U>[];\n\n\t/**\n\t * Optional property renames for this step, forwarded to EntitySchemaDiffHelper.diff.\n\t */\n\trenames?: { from: string; to: string }[];\n\n\t/**\n\t * Optional transformation for properties, usually only called for object and array types.\n\t * @param schema1Property The property schema in the old schema.\n\t * @param schemaProperty2 The property schema in the new schema.\n\t * @param value The value of the property in the old schema.\n\t * @returns The transformed value to match the new schema.\n\t */\n\ttransformEntityProperty?: EntityPropertyTransformer<T, U>;\n}\n"]}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ISchemaMigration.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ISchemaMigration.js","sourceRoot":"","sources":["../../../src/models/ISchemaMigration.ts"],"names":[],"mappings":"","sourcesContent":["// Copyright 2026 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\nimport type { EntityPropertyTransformer } from \"./entityPropertyTransformer.js\";\n\n/**\n * Optional per-step override for a single version-to-version migration.\n * Only register an entry in SchemaMigrationFactory when a step requires property\n * renames or a custom object/array transform. For purely structural changes\n * (add/remove/type-change fields) no entry is needed — the runner diffs the two\n * versioned schema classes (e.g. MyEntityV0 vs MyEntityV1) from EntitySchemaFactory\n * automatically.\n *\n * Register under the key \"BaseSchemaName_fromVersion_toVersion\"\n * e.g. \"MyEntity_0_1\" for the step that migrates from version 0 to version 1.\n * The key itself encodes the version pair; no version field is needed on the object.\n */\nexport interface ISchemaMigration<T = unknown, U = unknown> {\n\t/**\n\t * Optional property renames to apply during this step.\n\t */\n\trenames?: { from: string; to: string }[];\n\n\t/**\n\t * Optional transformation for properties, usually only called for object and array types.\n\t * @param schema1Property The property schema in the old schema.\n\t * @param schemaProperty2 The property schema in the new schema.\n\t * @param value The value of the property in the old schema.\n\t * @returns The transformed value to match the new schema.\n\t */\n\ttransformEntityProperty?: EntityPropertyTransformer<T, U>;\n}\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IEntityStorageCountRequest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IEntityStorageCountRequest.js","sourceRoot":"","sources":["../../../../src/models/api/IEntityStorageCountRequest.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Count the entries in entity storage.\n */\nexport interface IEntityStorageCountRequest {\n\t/**\n\t * The query parameters.\n\t */\n\tquery?: {\n\t\t/**\n\t\t * The optional conditions to filter the count, JSON encoded EntityCondition.\n\t\t */\n\t\tconditions?: string;\n\t};\n}\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IEntityStorageCountResponse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IEntityStorageCountResponse.js","sourceRoot":"","sources":["../../../../src/models/api/IEntityStorageCountResponse.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * The response for counting entries in entity storage.\n */\nexport interface IEntityStorageCountResponse {\n\t/**\n\t * The body of the response.\n\t */\n\tbody: {\n\t\t/**\n\t\t * The total count of entities.\n\t\t */\n\t\tcount: number;\n\t};\n}\n"]}
@@ -0,0 +1,4 @@
1
+ // Copyright 2024 IOTA Stiftung.
2
+ // SPDX-License-Identifier: Apache-2.0.
3
+ export {};
4
+ //# sourceMappingURL=IEntityStorageEmptyRequest.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IEntityStorageEmptyRequest.js","sourceRoot":"","sources":["../../../../src/models/api/IEntityStorageEmptyRequest.ts"],"names":[],"mappings":"AAAA,gCAAgC;AAChC,uCAAuC","sourcesContent":["// Copyright 2024 IOTA Stiftung.\n// SPDX-License-Identifier: Apache-2.0.\n\n/**\n * Remove all entries from entity storage.\n */\n// eslint-disable-next-line @typescript-eslint/no-empty-interface\nexport interface IEntityStorageEmptyRequest {}\n"]}