@itwin/imodel-transformer 2.0.0-dev.5 → 2.0.0-dev.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (119) hide show
  1. package/README.md +13 -27
  2. package/lib/cjs/Algo.js.map +1 -1
  3. package/lib/cjs/BigMap.d.ts.map +1 -1
  4. package/lib/cjs/BigMap.js.map +1 -1
  5. package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
  6. package/lib/cjs/BranchProvenanceInitializer.js +34 -36
  7. package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
  8. package/lib/cjs/ChangesetScanner.d.ts +60 -0
  9. package/lib/cjs/ChangesetScanner.d.ts.map +1 -0
  10. package/lib/cjs/ChangesetScanner.js +101 -0
  11. package/lib/cjs/ChangesetScanner.js.map +1 -0
  12. package/lib/cjs/ECReferenceTypesCache.d.ts +0 -3
  13. package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
  14. package/lib/cjs/ECReferenceTypesCache.js +61 -103
  15. package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
  16. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts.map +1 -1
  17. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js.map +1 -1
  18. package/lib/cjs/ElementAspectCleanup.d.ts +16 -0
  19. package/lib/cjs/ElementAspectCleanup.d.ts.map +1 -0
  20. package/lib/cjs/ElementAspectCleanup.js +82 -0
  21. package/lib/cjs/ElementAspectCleanup.js.map +1 -0
  22. package/lib/cjs/ElementAspectExportCoordinator.d.ts +44 -0
  23. package/lib/cjs/ElementAspectExportCoordinator.d.ts.map +1 -0
  24. package/lib/cjs/ElementAspectExportCoordinator.js +148 -0
  25. package/lib/cjs/ElementAspectExportCoordinator.js.map +1 -0
  26. package/lib/cjs/ElementAspectExportProcessor.d.ts +43 -0
  27. package/lib/cjs/ElementAspectExportProcessor.d.ts.map +1 -0
  28. package/lib/cjs/ElementAspectExportProcessor.js +167 -0
  29. package/lib/cjs/ElementAspectExportProcessor.js.map +1 -0
  30. package/lib/cjs/ElementBulkDelete.d.ts +11 -0
  31. package/lib/cjs/ElementBulkDelete.d.ts.map +1 -0
  32. package/lib/cjs/ElementBulkDelete.js +55 -0
  33. package/lib/cjs/ElementBulkDelete.js.map +1 -0
  34. package/lib/cjs/EntityExistenceCache.d.ts +43 -0
  35. package/lib/cjs/EntityExistenceCache.d.ts.map +1 -0
  36. package/lib/cjs/EntityExistenceCache.js +91 -0
  37. package/lib/cjs/EntityExistenceCache.js.map +1 -0
  38. package/lib/cjs/EntityMap.d.ts.map +1 -1
  39. package/lib/cjs/EntityMap.js.map +1 -1
  40. package/lib/cjs/EntityUnifier.d.ts +8 -8
  41. package/lib/cjs/EntityUnifier.d.ts.map +1 -1
  42. package/lib/cjs/EntityUnifier.js +55 -32
  43. package/lib/cjs/EntityUnifier.js.map +1 -1
  44. package/lib/cjs/IModelCloneContext.d.ts +11 -5
  45. package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
  46. package/lib/cjs/IModelCloneContext.js +94 -58
  47. package/lib/cjs/IModelCloneContext.js.map +1 -1
  48. package/lib/cjs/IModelExporter.d.ts +109 -35
  49. package/lib/cjs/IModelExporter.d.ts.map +1 -1
  50. package/lib/cjs/IModelExporter.js +698 -224
  51. package/lib/cjs/IModelExporter.js.map +1 -1
  52. package/lib/cjs/IModelImporter.d.ts +73 -32
  53. package/lib/cjs/IModelImporter.d.ts.map +1 -1
  54. package/lib/cjs/IModelImporter.js +278 -144
  55. package/lib/cjs/IModelImporter.js.map +1 -1
  56. package/lib/cjs/IModelTransformContext.d.ts +54 -0
  57. package/lib/cjs/IModelTransformContext.d.ts.map +1 -0
  58. package/lib/cjs/IModelTransformContext.js +3 -0
  59. package/lib/cjs/IModelTransformContext.js.map +1 -0
  60. package/lib/cjs/IModelTransformer.d.ts +126 -206
  61. package/lib/cjs/IModelTransformer.d.ts.map +1 -1
  62. package/lib/cjs/IModelTransformer.js +652 -1198
  63. package/lib/cjs/IModelTransformer.js.map +1 -1
  64. package/lib/cjs/IModelTransformerError.d.ts +84 -0
  65. package/lib/cjs/IModelTransformerError.d.ts.map +1 -0
  66. package/lib/cjs/IModelTransformerError.js +93 -0
  67. package/lib/cjs/IModelTransformerError.js.map +1 -0
  68. package/lib/cjs/ProvenanceManager.d.ts +182 -0
  69. package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
  70. package/lib/cjs/ProvenanceManager.js +743 -0
  71. package/lib/cjs/ProvenanceManager.js.map +1 -0
  72. package/lib/cjs/SyncTypeResolver.d.ts +34 -0
  73. package/lib/cjs/SyncTypeResolver.d.ts.map +1 -0
  74. package/lib/cjs/SyncTypeResolver.js +98 -0
  75. package/lib/cjs/SyncTypeResolver.js.map +1 -0
  76. package/lib/cjs/TransformerLoggerCategory.d.ts +1 -1
  77. package/lib/cjs/TransformerLoggerCategory.js +1 -1
  78. package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
  79. package/lib/cjs/TransformerPackageMetadata.d.ts +8 -0
  80. package/lib/cjs/TransformerPackageMetadata.d.ts.map +1 -0
  81. package/lib/cjs/TransformerPackageMetadata.js +47 -0
  82. package/lib/cjs/TransformerPackageMetadata.js.map +1 -0
  83. package/lib/cjs/imodel-transformer.d.ts +19 -0
  84. package/lib/cjs/imodel-transformer.d.ts.map +1 -1
  85. package/lib/cjs/imodel-transformer.js +53 -34
  86. package/lib/cjs/imodel-transformer.js.map +1 -1
  87. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts +29 -0
  88. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts.map +1 -0
  89. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js +212 -0
  90. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js.map +1 -0
  91. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts +25 -0
  92. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts.map +1 -0
  93. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js +196 -0
  94. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js.map +1 -0
  95. package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts +59 -0
  96. package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts.map +1 -0
  97. package/lib/cjs/schema-processing/SchemaProcessingStrategy.js +28 -0
  98. package/lib/cjs/schema-processing/SchemaProcessingStrategy.js.map +1 -0
  99. package/lib/cjs/schema-processing.d.ts +7 -0
  100. package/lib/cjs/schema-processing.d.ts.map +1 -0
  101. package/lib/cjs/schema-processing.js +27 -0
  102. package/lib/cjs/schema-processing.js.map +1 -0
  103. package/package.json +65 -43
  104. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts +0 -16
  105. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +0 -1
  106. package/lib/cjs/DetachedExportElementAspectsStrategy.js +0 -106
  107. package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +0 -1
  108. package/lib/cjs/ElementCascadingDeleter.d.ts +0 -22
  109. package/lib/cjs/ElementCascadingDeleter.d.ts.map +0 -1
  110. package/lib/cjs/ElementCascadingDeleter.js +0 -57
  111. package/lib/cjs/ElementCascadingDeleter.js.map +0 -1
  112. package/lib/cjs/ExportElementAspectsStrategy.d.ts +0 -35
  113. package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +0 -1
  114. package/lib/cjs/ExportElementAspectsStrategy.js +0 -52
  115. package/lib/cjs/ExportElementAspectsStrategy.js.map +0 -1
  116. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts +0 -12
  117. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +0 -1
  118. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +0 -47
  119. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +0 -1
@@ -13,8 +13,11 @@ const core_bentley_1 = require("@itwin/core-bentley");
13
13
  const core_common_1 = require("@itwin/core-common");
14
14
  const TransformerLoggerCategory_1 = require("./TransformerLoggerCategory");
15
15
  const core_backend_1 = require("@itwin/core-backend");
16
- const assert = require("assert");
17
- const ElementCascadingDeleter_1 = require("./ElementCascadingDeleter");
16
+ const node_assert_1 = require("node:assert");
17
+ const ElementAspectCleanup_1 = require("./ElementAspectCleanup");
18
+ const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
19
+ const IModelTransformerError_1 = require("./IModelTransformerError");
20
+ const ElementBulkDelete_1 = require("./ElementBulkDelete");
18
21
  const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IModelImporter;
19
22
  /** Base class for importing data into an iModel.
20
23
  * @see [iModel Transformation and Data Exchange]($docs/learning/transformer/index.md)
@@ -25,6 +28,23 @@ const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IMo
25
28
  class IModelImporter {
26
29
  /** The read/write target iModel. */
27
30
  targetDb;
31
+ /** The [[EditTxn]] used for write operations on the target iModel.
32
+ * @beta
33
+ */
34
+ _editTxn;
35
+ /** The [[EditTxn]] used for write operations on the target iModel.
36
+ * @beta
37
+ */
38
+ get editTxn() {
39
+ return this._editTxn;
40
+ }
41
+ _elementAspectCleanup;
42
+ /** Deletes replaceable ElementAspects through this importer's customized deletion callback.
43
+ * @internal
44
+ */
45
+ get elementAspectCleanup() {
46
+ return this._elementAspectCleanup;
47
+ }
28
48
  /** resolved initialization options for the importer
29
49
  * @beta
30
50
  */
@@ -38,6 +58,7 @@ class IModelImporter {
38
58
  * To resolve code values to their intended values call [[IModelImporter.resolveDuplicateCodeValues]].
39
59
  */
40
60
  _duplicateCodeValueMap;
61
+ _entityExistenceCaches = new Set();
41
62
  /**
42
63
  * A set of elementIds that the transformer adds to while exporting elements to indicate that the element already exists in the target.
43
64
  * Defaults to an empty set.
@@ -51,9 +72,10 @@ class IModelImporter {
51
72
  * @note This set should stay small, as right after the transformer pushes to it, the importer will remove from the set.
52
73
  */
53
74
  _elementsToUpdateDuringPreserveIds = new Set([]);
54
- /** The set of elements that should not be updated by this IModelImporter.
55
- * Defaults to an empty set.
75
+ /** The set of target elements that this importer must not directly update or request for deletion.
76
+ * Defaults to an empty set and is populated by consumers or custom transformers.
56
77
  * @note Adding an element to this set is typically necessary when remapping a source element to one that already exists in the target and already has the desired properties.
78
+ * @note Deletion protection applies only when the exact element ID is passed to [[deleteElement]] or [[deleteElements]]. It does not protect the element from deletion through a parent, containing model, or other cascading dependency. Consumers that need to preserve such an element must also add every requested deletion root whose cascade would delete it.
57
79
  */
58
80
  doNotUpdateElementIds = new Set([]);
59
81
  /** This set is ONLY used for elements that are always present even in an "empty" iModel. We will use this set to filter out changes to root elements if [[IModelTransformOptions.skipPropagateChangesToRootElements]] is false. */
@@ -71,11 +93,12 @@ class IModelImporter {
71
93
  "geometryGuid", // cannot compare GeometricModel.GeometryGuid values across iModels
72
94
  ]);
73
95
  /** Construct a new IModelImporter
74
- * @param targetDb The target IModelDb
96
+ * @param editTxn The [[EditTxn]] to use for write operations on the target iModel. The target db is derived from `editTxn.iModel`.
75
97
  * @param options The options that specify how the import should be done.
76
98
  */
77
- constructor(targetDb, options) {
78
- this.targetDb = targetDb;
99
+ constructor(editTxn, options) {
100
+ this.targetDb = editTxn.iModel;
101
+ this._editTxn = editTxn;
79
102
  this.options = {
80
103
  autoExtendProjectExtents: options?.autoExtendProjectExtents ?? true,
81
104
  preserveElementIdsForFiltering: options?.preserveElementIdsForFiltering ?? false,
@@ -83,6 +106,19 @@ class IModelImporter {
83
106
  skipPropagateChangesToRootElements: options?.skipPropagateChangesToRootElements ?? true,
84
107
  };
85
108
  this._duplicateCodeValueMap = new Map();
109
+ this._elementAspectCleanup = new ElementAspectCleanup_1.ElementAspectCleanup(this.targetDb, this._editTxn, async (aspect) => this.onDeleteElementAspect(aspect));
110
+ }
111
+ /** Register a transformation-scoped existence cache for delete invalidation.
112
+ * @internal
113
+ */
114
+ registerEntityExistenceCache(cache) {
115
+ this._entityExistenceCaches.add(cache);
116
+ }
117
+ /** Stop invalidating a transformation-scoped existence cache.
118
+ * @internal
119
+ */
120
+ unregisterEntityExistenceCache(cache) {
121
+ this._entityExistenceCaches.delete(cache);
86
122
  }
87
123
  /**
88
124
  * Checks [[IModelImportOptions.skipPropagateChangesToRootElements]], [[IModelImporter.rootElementIds]], and [[IModelImporter.doNotUpdateElementIds]] and returns true for 'do not update', false for do 'update'.
@@ -105,9 +141,15 @@ class IModelImporter {
105
141
  this._elementsToUpdateDuringPreserveIds.add(elementId);
106
142
  }
107
143
  /** Import the specified ModelProps (either as an insert or an update) into the target iModel. */
108
- importModel(modelProps) {
144
+ async importModel(modelProps) {
109
145
  if (undefined === modelProps.id || !core_bentley_1.Id64.isValidId64(modelProps.id))
110
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "Model Id not provided, should be the same as the ModeledElementId");
146
+ core_bentley_1.ITwinError.throwError({
147
+ iTwinErrorId: {
148
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
149
+ key: IModelTransformerError_1.IModelTransformerError.InvalidModelId,
150
+ },
151
+ message: "Model Id not provided, should be the same as the ModeledElementId",
152
+ });
111
153
  if (this.doNotUpdateElement(modelProps.id)) {
112
154
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target model ${modelProps.id}`);
113
155
  return;
@@ -115,14 +157,14 @@ class IModelImporter {
115
157
  try {
116
158
  const model = this.targetDb.models.getModel(modelProps.id); // throws IModelError.NotFound if model does not exist
117
159
  if (hasEntityChanged(model, modelProps, this._modelPropertiesToIgnore)) {
118
- this.onUpdateModel(modelProps);
160
+ await this.onUpdateModel(modelProps);
119
161
  }
120
162
  }
121
163
  catch (error) {
122
164
  // catch NotFound error and insertModel
123
165
  if (error instanceof core_common_1.IModelError &&
124
166
  error.errorNumber === core_bentley_1.IModelStatus.NotFound) {
125
- this.onInsertModel(modelProps);
167
+ await this.onInsertModel(modelProps);
126
168
  return;
127
169
  }
128
170
  throw error;
@@ -131,29 +173,35 @@ class IModelImporter {
131
173
  /** Create a new Model from the specified ModelProps and insert it into the target iModel.
132
174
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertModel`.
133
175
  */
134
- onInsertModel(modelProps) {
176
+ async onInsertModel(modelProps) {
135
177
  try {
136
- const modelId = this.targetDb.models.insertModel(modelProps);
178
+ const modelId = this._editTxn.insertModel(modelProps);
137
179
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatModelForLogger(modelProps)}`);
138
- this.trackProgress();
180
+ await this.trackProgress();
139
181
  return modelId;
140
182
  }
141
183
  catch (error) {
142
184
  if (!this.targetDb.containsClass(modelProps.classFullName)) {
143
- // replace standard insert error with something more helpful
144
- const errorMessage = `Model class "${modelProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
145
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidName, errorMessage);
185
+ // Translate only confirmed missing-class failures; otherwise retain the backend error's identity.
186
+ core_bentley_1.ITwinError.throwError({
187
+ iTwinErrorId: {
188
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
189
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
190
+ },
191
+ message: `Model class "${modelProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
192
+ cause: error,
193
+ });
146
194
  }
147
- throw error; // throw original error
195
+ throw error;
148
196
  }
149
197
  }
150
198
  /** Update an existing Model in the target iModel from the specified ModelProps.
151
199
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateModel`.
152
200
  */
153
- onUpdateModel(modelProps) {
154
- this.targetDb.models.updateModel(modelProps);
201
+ async onUpdateModel(modelProps) {
202
+ this._editTxn.updateModel(modelProps);
155
203
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatModelForLogger(modelProps)}`);
156
- this.trackProgress();
204
+ await this.trackProgress();
157
205
  }
158
206
  /** Format a Model for the Logger. */
159
207
  formatModelForLogger(modelProps) {
@@ -164,18 +212,18 @@ class IModelImporter {
164
212
  * Tries to update an element with the specified element properties.
165
213
  * If a duplicate code error occurs, it assigns a new unique code value and retries the update
166
214
  */
167
- tryUpdateElement(elemProps) {
215
+ async tryUpdateElement(elemProps) {
168
216
  try {
169
- this.onUpdateElement(elemProps);
217
+ await this.onUpdateElement(elemProps);
170
218
  }
171
219
  catch (err) {
172
220
  if (err.errorNumber === core_bentley_1.IModelStatus.DuplicateCode) {
173
- assert(elemProps.code.value !== undefined, "NULL code values are always considered unique and cannot clash");
221
+ (0, node_assert_1.strict)(elemProps.code.value !== undefined, "NULL code values are always considered unique and cannot clash");
174
222
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
175
223
  this._duplicateCodeValueMap.set(elemProps.id, elemProps.code.value);
176
224
  // Using NULL code values as an alternative is not valid because definition elements cannot have NULL code values.
177
225
  elemProps.code.value = core_bentley_1.Guid.createValue();
178
- this.onUpdateElement(elemProps);
226
+ await this.onUpdateElement(elemProps);
179
227
  }
180
228
  else {
181
229
  throw err;
@@ -183,7 +231,7 @@ class IModelImporter {
183
231
  }
184
232
  }
185
233
  /** Import the specified ElementProps (either as an insert or an update) into the target iModel. */
186
- importElement(elementProps) {
234
+ async importElement(elementProps) {
187
235
  if (undefined !== elementProps.id &&
188
236
  this.doNotUpdateElement(elementProps.id)) {
189
237
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target element ${elementProps.id}`);
@@ -191,7 +239,13 @@ class IModelImporter {
191
239
  }
192
240
  if (this.options.preserveElementIdsForFiltering) {
193
241
  if (elementProps.id === undefined) {
194
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "elementProps.id must be defined during a preserveIds operation");
242
+ core_bentley_1.ITwinError.throwError({
243
+ iTwinErrorId: {
244
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
245
+ key: IModelTransformerError_1.IModelTransformerError.ElementIdRequired,
246
+ },
247
+ message: "elementProps.id must be defined during a preserveIds operation",
248
+ });
195
249
  }
196
250
  // Categories are the only element that onInserted will immediately insert a new element (their default subcategory)
197
251
  // since default subcategories always exist and always will be inserted after their categories, we treat them as an update
@@ -199,24 +253,24 @@ class IModelImporter {
199
253
  // Always present elements (0xe, 0x1, 0x10) also will be updated to prevent duplicate inserts.
200
254
  if ((isSubCategory(elementProps) && isDefaultSubCategory(elementProps)) ||
201
255
  this._rootElementIds.has(elementProps.id)) {
202
- this.onUpdateElement(elementProps);
256
+ await this.onUpdateElement(elementProps);
203
257
  }
204
258
  else {
205
259
  if (this._elementsToUpdateDuringPreserveIds.has(elementProps.id)) {
206
- this.tryUpdateElement(elementProps);
260
+ await this.tryUpdateElement(elementProps);
207
261
  this._elementsToUpdateDuringPreserveIds.delete(elementProps.id);
208
262
  }
209
263
  else {
210
- this.onInsertElement(elementProps);
264
+ await this.onInsertElement(elementProps);
211
265
  }
212
266
  }
213
267
  }
214
268
  else {
215
269
  if (undefined !== elementProps.id) {
216
- this.tryUpdateElement(elementProps);
270
+ await this.tryUpdateElement(elementProps);
217
271
  }
218
272
  else {
219
- elementProps.id = this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
273
+ elementProps.id = await this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
220
274
  }
221
275
  }
222
276
  return elementProps.id;
@@ -225,16 +279,15 @@ class IModelImporter {
225
279
  * @returns The Id of the newly inserted Element.
226
280
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertElement`.
227
281
  */
228
- onInsertElement(elementProps) {
229
- /* eslint-disable @typescript-eslint/no-deprecated */
282
+ async onInsertElement(elementProps) {
230
283
  try {
231
- const elementId = this.targetDb.elements.insertElement(elementProps, {
284
+ const elementId = this._editTxn.insertElement(elementProps, {
232
285
  forceUseId: this.options.preserveElementIdsForFiltering,
233
286
  });
234
287
  // set the id like [IModelDb.insertElement]($backend), does, the raw nativeDb method does not
235
288
  elementProps.id = elementId;
236
289
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatElementForLogger(elementProps)}`);
237
- this.trackProgress();
290
+ await this.trackProgress();
238
291
  if (this.options.simplifyElementGeometry) {
239
292
  this.targetDb.simplifyElementGeometry({
240
293
  id: elementId,
@@ -246,26 +299,35 @@ class IModelImporter {
246
299
  }
247
300
  catch (error) {
248
301
  if (!this.targetDb.containsClass(elementProps.classFullName)) {
249
- // replace standard insert error with something more helpful
250
- const errorMessage = `Element class "${elementProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
251
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidName, errorMessage);
302
+ core_bentley_1.ITwinError.throwError({
303
+ iTwinErrorId: {
304
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
305
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
306
+ },
307
+ message: `Element class "${elementProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
308
+ cause: error,
309
+ });
252
310
  }
253
- throw error; // throw original error
311
+ throw error;
254
312
  }
255
- /* eslint-enable @typescript-eslint/no-deprecated */
256
313
  }
257
314
  /** Update an existing Element in the target iModel from the specified ElementProps.
258
315
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateElement`.
259
316
  */
260
- onUpdateElement(elementProps) {
317
+ async onUpdateElement(elementProps) {
261
318
  if (!elementProps.id) {
262
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "ElementId not provided");
319
+ core_bentley_1.ITwinError.throwError({
320
+ iTwinErrorId: {
321
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
322
+ key: IModelTransformerError_1.IModelTransformerError.ElementIdRequired,
323
+ },
324
+ message: "ElementId not provided",
325
+ });
263
326
  }
264
- this.targetDb.elements.updateElement(elementProps);
327
+ this._editTxn.updateElement(elementProps);
265
328
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementForLogger(elementProps)}`);
266
- this.trackProgress();
329
+ await this.trackProgress();
267
330
  if (this.options.simplifyElementGeometry) {
268
- /* eslint-disable-next-line @typescript-eslint/no-deprecated */
269
331
  this.targetDb.simplifyElementGeometry({
270
332
  id: elementProps.id,
271
333
  convertBReps: true,
@@ -273,34 +335,70 @@ class IModelImporter {
273
335
  core_bentley_1.Logger.logInfo(loggerCategory, `Simplified element geometry for ${this.formatElementForLogger(elementProps)}`);
274
336
  }
275
337
  }
276
- /** Delete the specified Element (and all its children) from the target iModel.
277
- * Will delete special elements like definition elements and subjects.
278
- * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteElement`.
338
+ /** Deletes one target element tree through [[deleteElements]]. */
339
+ async deleteElement(elementId) {
340
+ await this.deleteElements(new Set([elementId]));
341
+ }
342
+ /** Adds roots required by code-scope dependencies, then deletes the target element trees in one native operation.
343
+ * @note An override must call `super.onDeleteElements` once to perform the deletion.
279
344
  */
280
- onDeleteElement(elementId) {
281
- (0, ElementCascadingDeleter_1.deleteElementTreeCascade)(this.targetDb, elementId);
282
- core_bentley_1.Logger.logInfo(loggerCategory, `Deleted element ${elementId} and its descendants`);
283
- this.trackProgress();
284
- }
285
- /** Delete the specified Element from the target iModel. */
286
- deleteElement(elementId) {
287
- if (this.doNotUpdateElement(elementId)) {
288
- core_bentley_1.Logger.logInfo(loggerCategory, `Do not delete target element ${elementId}`);
345
+ async onDeleteElements(elementIds) {
346
+ const deleteRoots = await (0, ElementBulkDelete_1.findBulkDeleteRoots)(this.targetDb, elementIds);
347
+ if (deleteRoots.size === 0)
289
348
  return;
349
+ const result = this._editTxn.deleteElements([...deleteRoots]);
350
+ if (result.status !== core_backend_1.BulkDeleteElementsStatus.Success) {
351
+ core_bentley_1.ITwinError.throwError({
352
+ iTwinErrorId: {
353
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
354
+ key: IModelTransformerError_1.IModelTransformerError.ElementBulkDeleteFailed,
355
+ },
356
+ message: `Bulk element deletion failed: status ${core_backend_1.BulkDeleteElementsStatus[result.status]}, ` +
357
+ `SQLite status ${result.sqlDeleteStatus}, failed element IDs: ${[
358
+ ...result.failedIds,
359
+ ].join(", ")}`,
360
+ status: result.status,
361
+ sqlDeleteStatus: result.sqlDeleteStatus,
362
+ failedIds: new Set(result.failedIds),
363
+ });
364
+ }
365
+ core_bentley_1.Logger.logInfo(loggerCategory, `Deleted ${elementIds.size} requested element trees using ${deleteRoots.size} native deletion roots`);
366
+ await this.trackProgress(elementIds.size);
367
+ }
368
+ /** Deletes target element trees in one native operation.
369
+ * Requested roots in [[doNotUpdateElementIds]] are skipped. This does not prevent an element in that set from being deleted as part of another root's cascade.
370
+ * @throws [[ElementBulkDeleteError]] if native deletion fails for any root. A partial failure leaves successful deletions pending in the caller-owned transaction. Abandon the transaction before retrying.
371
+ */
372
+ async deleteElements(elementIds) {
373
+ const idsToDelete = new Set();
374
+ for (const elementId of elementIds) {
375
+ if (!this.doNotUpdateElement(elementId)) {
376
+ idsToDelete.add(elementId);
377
+ continue;
378
+ }
379
+ core_bentley_1.Logger.logInfo(loggerCategory, `Do not delete target element ${elementId}`);
290
380
  }
291
- this.onDeleteElement(elementId);
381
+ if (idsToDelete.size === 0)
382
+ return;
383
+ await this.onDeleteElements(idsToDelete);
384
+ // Element deletion cascades can remove descendants and their submodels.
385
+ for (const cache of this._entityExistenceCaches)
386
+ cache.clearDb(this.targetDb);
292
387
  }
293
388
  /** Delete the specified Model from the target iModel.
294
389
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteModel`.
295
390
  */
296
- onDeleteModel(modelId) {
297
- this.targetDb.models.deleteModel(modelId);
391
+ async onDeleteModel(modelId) {
392
+ this._editTxn.deleteModel(modelId);
298
393
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted model ${modelId}`);
299
- this.trackProgress();
394
+ await this.trackProgress();
300
395
  }
301
396
  /** Delete the specified Model from the target iModel. */
302
- deleteModel(modelId) {
303
- this.onDeleteModel(modelId);
397
+ async deleteModel(modelId) {
398
+ await this.onDeleteModel(modelId);
399
+ const modelReference = core_backend_1.EntityReferences.fromEntityType(modelId, core_common_1.ConcreteEntityTypes.Model);
400
+ for (const cache of this._entityExistenceCaches)
401
+ cache.invalidate(this.targetDb, modelReference);
304
402
  }
305
403
  /** Format an Element for the Logger. */
306
404
  formatElementForLogger(elementProps) {
@@ -312,14 +410,14 @@ class IModelImporter {
312
410
  return `${elementProps.classFullName} ${namePiece}[${elementProps.id}]`;
313
411
  }
314
412
  /** Import an ElementUniqueAspect into the target iModel. */
315
- importElementUniqueAspect(aspectProps) {
413
+ async importElementUniqueAspect(aspectProps) {
316
414
  const aspects = this.targetDb.elements.getAspects(aspectProps.element.id, aspectProps.classFullName);
317
415
  if (aspects.length === 0) {
318
416
  return this.onInsertElementAspect(aspectProps);
319
417
  }
320
418
  else if (hasEntityChanged(aspects[0], aspectProps)) {
321
419
  aspectProps.id = aspects[0].id;
322
- this.onUpdateElementAspect(aspectProps);
420
+ await this.onUpdateElementAspect(aspectProps);
323
421
  }
324
422
  return aspects[0].id;
325
423
  }
@@ -329,7 +427,7 @@ class IModelImporter {
329
427
  * @note For insert vs. update reasons, it is important to process all ElementMultiAspects owned by an Element at once since we don't have aspect-specific provenance.
330
428
  * @returns the array of ids of the resulting ElementMultiAspects, in the same order of the aspectPropsArray parameter
331
429
  */
332
- importElementMultiAspects(aspectPropsArray,
430
+ async importElementMultiAspects(aspectPropsArray,
333
431
  /** caller must use this to enforce any aspects added by IModelTransformer are not considered for update */
334
432
  filterFunc = () => true) {
335
433
  const result = new Array(aspectPropsArray.length).fill(undefined);
@@ -337,91 +435,96 @@ class IModelImporter {
337
435
  return result;
338
436
  }
339
437
  const elementId = aspectPropsArray[0].element.id;
340
- // Determine the set of ElementMultiAspect classes to consider
341
- const aspectClassFullNames = new Set();
342
- aspectPropsArray.forEach((aspectsProps) => {
343
- aspectClassFullNames.add(aspectsProps.classFullName);
438
+ const proposedAspectsByClass = new Map();
439
+ aspectPropsArray.forEach((props, index) => {
440
+ const proposedAspects = proposedAspectsByClass.get(props.classFullName) ?? [];
441
+ proposedAspects.push({ props, index });
442
+ proposedAspectsByClass.set(props.classFullName, proposedAspects);
344
443
  });
345
444
  // Handle ElementMultiAspects in groups by class
346
- aspectClassFullNames.forEach((aspectClassFullName) => {
347
- const proposedAspects = aspectPropsArray
348
- .map((props, index) => ({ props, index }))
349
- .filter(({ props }) => aspectClassFullName === props.classFullName);
445
+ for (const [aspectClassFullName, proposedAspects,] of proposedAspectsByClass) {
350
446
  const currentAspects = this.targetDb.elements
351
447
  .getAspects(elementId, aspectClassFullName)
352
448
  .map((props, index) => ({ props, index }))
353
449
  .filter(({ props }) => filterFunc(props));
354
450
  if (proposedAspects.length >= currentAspects.length) {
355
- proposedAspects.forEach(({ props, index: resultIndex }, index) => {
451
+ for (let index = 0; index < proposedAspects.length; index++) {
452
+ const { props, index: resultIndex } = proposedAspects[index];
356
453
  let id;
357
454
  if (index < currentAspects.length) {
358
455
  id = currentAspects[index].props.id;
359
456
  props.id = id;
360
457
  if (hasEntityChanged(currentAspects[index].props, props)) {
361
- this.onUpdateElementAspect(props);
458
+ await this.onUpdateElementAspect(props);
362
459
  }
363
460
  id = props.id;
364
461
  }
365
462
  else {
366
- id = this.onInsertElementAspect(props);
463
+ id = await this.onInsertElementAspect(props);
367
464
  }
368
465
  result[resultIndex] = id;
369
- });
466
+ }
370
467
  }
371
468
  else {
372
- currentAspects.forEach(({ props, index: resultIndex }, index) => {
373
- let id;
469
+ for (let index = 0; index < currentAspects.length; index++) {
470
+ const { props } = currentAspects[index];
374
471
  if (index < proposedAspects.length) {
375
- id = props.id;
376
- proposedAspects[index].props.id = id;
377
- if (hasEntityChanged(props, proposedAspects[index].props)) {
378
- this.onUpdateElementAspect(proposedAspects[index].props);
472
+ const { props: proposedProps, index: resultIndex } = proposedAspects[index];
473
+ const id = props.id;
474
+ proposedProps.id = id;
475
+ if (hasEntityChanged(props, proposedProps)) {
476
+ await this.onUpdateElementAspect(proposedProps);
379
477
  }
380
478
  result[resultIndex] = id;
381
479
  }
382
480
  else {
383
- this.onDeleteElementAspect(props);
481
+ await this.onDeleteElementAspect(props);
384
482
  }
385
- });
483
+ }
386
484
  }
387
- });
388
- assert(result.every((r) => typeof r !== undefined));
485
+ }
486
+ (0, node_assert_1.strict)(result.every((r) => r !== undefined));
389
487
  return result;
390
488
  }
391
489
  /** Insert the ElementAspect into the target iModel.
392
490
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertElementAspect`.
393
491
  */
394
- onInsertElementAspect(aspectProps) {
492
+ async onInsertElementAspect(aspectProps) {
395
493
  try {
396
- const id = this.targetDb.elements.insertAspect(aspectProps);
494
+ const id = this._editTxn.insertAspect(aspectProps);
397
495
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatElementAspectForLogger(aspectProps)}`);
398
- this.trackProgress();
496
+ await this.trackProgress();
399
497
  return id;
400
498
  }
401
499
  catch (error) {
402
500
  if (!this.targetDb.containsClass(aspectProps.classFullName)) {
403
- // replace standard insert error with something more helpful
404
- const errorMessage = `ElementAspect class "${aspectProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
405
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidName, errorMessage);
501
+ core_bentley_1.ITwinError.throwError({
502
+ iTwinErrorId: {
503
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
504
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
505
+ },
506
+ message: `ElementAspect class "${aspectProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
507
+ cause: error,
508
+ });
406
509
  }
407
- throw error; // throw original error
510
+ throw error;
408
511
  }
409
512
  }
410
513
  /** Update the ElementAspect within the target iModel.
411
514
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateElementAspect`.
412
515
  */
413
- onUpdateElementAspect(aspectProps) {
414
- this.targetDb.elements.updateAspect(aspectProps);
516
+ async onUpdateElementAspect(aspectProps) {
517
+ this._editTxn.updateAspect(aspectProps);
415
518
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementAspectForLogger(aspectProps)}`);
416
- this.trackProgress();
519
+ await this.trackProgress();
417
520
  }
418
521
  /** Delete the specified ElementAspect from the target iModel.
419
522
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteElementAspect`.
420
523
  */
421
- onDeleteElementAspect(targetElementAspect) {
422
- this.targetDb.elements.deleteAspect(targetElementAspect.id);
524
+ async onDeleteElementAspect(targetElementAspect) {
525
+ this._editTxn.deleteAspect(targetElementAspect.id);
423
526
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted ${this.formatElementAspectForLogger(targetElementAspect)}`);
424
- this.trackProgress();
527
+ await this.trackProgress();
425
528
  }
426
529
  /** Format an ElementAspect for the Logger. */
427
530
  formatElementAspectForLogger(elementAspectProps) {
@@ -430,7 +533,7 @@ class IModelImporter {
430
533
  /** Import the specified RelationshipProps (either as an insert or an update) into the target iModel.
431
534
  * @returns The instance Id of the inserted or updated Relationship.
432
535
  */
433
- importRelationship(relationshipProps) {
536
+ async importRelationship(relationshipProps) {
434
537
  if (undefined === relationshipProps.sourceId ||
435
538
  !core_bentley_1.Id64.isValidId64(relationshipProps.sourceId)) {
436
539
  core_bentley_1.Logger.logInfo(loggerCategory, `Ignoring ${relationshipProps.classFullName} instance because of invalid RelationshipProps.sourceId`);
@@ -451,7 +554,7 @@ class IModelImporter {
451
554
  // if relationship found, update it
452
555
  relationshipProps.id = relationship.id;
453
556
  if (hasEntityChanged(relationship, relationshipProps)) {
454
- this.onUpdateRelationship(relationshipProps);
557
+ await this.onUpdateRelationship(relationshipProps);
455
558
  }
456
559
  return relationshipProps.id;
457
560
  }
@@ -463,62 +566,75 @@ class IModelImporter {
463
566
  * @returns The instance Id of the newly inserted relationship.
464
567
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertRelationship`.
465
568
  */
466
- onInsertRelationship(relationshipProps) {
569
+ async onInsertRelationship(relationshipProps) {
467
570
  try {
468
- const targetRelInstanceId = this.targetDb.relationships.insertInstance(relationshipProps);
571
+ const targetRelInstanceId = this._editTxn.insertRelationship(relationshipProps);
469
572
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatRelationshipForLogger(relationshipProps)}`);
470
- this.trackProgress();
573
+ await this.trackProgress();
471
574
  return targetRelInstanceId;
472
575
  }
473
576
  catch (error) {
474
577
  if (!this.targetDb.containsClass(relationshipProps.classFullName)) {
475
- // replace standard insert error with something more helpful
476
- const errorMessage = `Relationship class "${relationshipProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
477
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidName, errorMessage);
578
+ core_bentley_1.ITwinError.throwError({
579
+ iTwinErrorId: {
580
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
581
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
582
+ },
583
+ message: `Relationship class "${relationshipProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
584
+ cause: error,
585
+ });
478
586
  }
479
- throw error; // throw original error
587
+ throw error;
480
588
  }
481
589
  }
482
590
  /** Update an existing Relationship in the target iModel from the specified RelationshipProps.
483
591
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateRelationship`.
484
592
  */
485
- onUpdateRelationship(relationshipProps) {
593
+ async onUpdateRelationship(relationshipProps) {
486
594
  if (!relationshipProps.id) {
487
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "Relationship instance Id not provided");
595
+ core_bentley_1.ITwinError.throwError({
596
+ iTwinErrorId: {
597
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
598
+ key: IModelTransformerError_1.IModelTransformerError.RelationshipIdRequired,
599
+ },
600
+ message: "Relationship instance Id not provided",
601
+ });
488
602
  }
489
- this.targetDb.relationships.updateInstance(relationshipProps);
603
+ this._editTxn.updateRelationship(relationshipProps);
490
604
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatRelationshipForLogger(relationshipProps)}`);
491
- this.trackProgress();
605
+ await this.trackProgress();
492
606
  }
493
607
  /** Delete the specified Relationship from the target iModel. */
494
- onDeleteRelationship(relationshipProps) {
608
+ async onDeleteRelationship(relationshipProps) {
495
609
  // Only passing in what deleteInstance actually uses, full relationshipProps is not necessary.
496
- this.targetDb.relationships.deleteInstance({
610
+ this._editTxn.deleteRelationship({
497
611
  id: relationshipProps.id,
498
612
  classFullName: relationshipProps.classFullName,
499
613
  });
500
614
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted relationship ${relationshipProps.classFullName} id=${relationshipProps.id}`);
501
- this.trackProgress();
615
+ await this.trackProgress();
502
616
  }
503
617
  /** Delete the specified Relationship from the target iModel. */
504
- deleteRelationship(relationshipProps) {
505
- this.onDeleteRelationship(relationshipProps);
618
+ async deleteRelationship(relationshipProps) {
619
+ await this.onDeleteRelationship(relationshipProps);
506
620
  }
507
621
  /** Format a Relationship for the Logger. */
508
622
  formatRelationshipForLogger(relProps) {
509
623
  return `${relProps.classFullName} sourceId=[${relProps.sourceId}] targetId=[${relProps.targetId}]`;
510
624
  }
511
625
  /** Tracks incremental progress */
512
- trackProgress() {
513
- this._progressCounter++;
514
- if (0 === this._progressCounter % this.progressInterval) {
515
- this.onProgress();
516
- }
626
+ async trackProgress(increment = 1) {
627
+ const previousProgressCounter = this._progressCounter;
628
+ this._progressCounter += increment;
629
+ const progressCallbacks = Math.floor(this._progressCounter / this.progressInterval) -
630
+ Math.floor(previousProgressCounter / this.progressInterval);
631
+ for (let i = 0; i < progressCallbacks; ++i)
632
+ await this.onProgress();
517
633
  }
518
634
  /** This method is called when IModelImporter has made incremental progress based on the [[progressInterval]] setting.
519
635
  * @note A subclass may override this method to report custom progress but should call `super.onProgress`.
520
636
  */
521
- onProgress() { }
637
+ async onProgress() { }
522
638
  /** Optionally compute the projectExtents for the target iModel depending on the options for this IModelImporter.
523
639
  * @note This method is automatically called from [IModelTransformer.process]($transformer).
524
640
  * @see [IModelDb.computeProjectExtents]($backend), [[autoExtendProjectExtents]]
@@ -535,13 +651,15 @@ class IModelImporter {
535
651
  const excludeOutliers = typeof this.options.autoExtendProjectExtents === "object"
536
652
  ? this.options.autoExtendProjectExtents.excludeOutliers
537
653
  : false;
538
- // If excludeOutliers is true, use the extents without outliers. If extentsWithOutliers is undefined, fall back to extents.
539
- const newProjectExtents = excludeOutliers
540
- ? computedProjectExtents.extents
541
- : (computedProjectExtents.extentsWithOutliers ??
542
- computedProjectExtents.extents);
654
+ // If excludeOutliers is true, use the extents without outliers.
655
+ // Otherwise prefer extentsWithOutliers, but fall back to extents if it's undefined or a null range.
656
+ const extentsWithOutliers = computedProjectExtents.extentsWithOutliers;
657
+ const useExtentsWithOutliers = !excludeOutliers && extentsWithOutliers && !extentsWithOutliers.isNull;
658
+ const newProjectExtents = useExtentsWithOutliers
659
+ ? extentsWithOutliers
660
+ : computedProjectExtents.extents;
543
661
  if (!newProjectExtents.isAlmostEqual(this.targetDb.projectExtents)) {
544
- this.targetDb.updateProjectExtents(newProjectExtents);
662
+ this._editTxn.updateProjectExtents(newProjectExtents);
545
663
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated projectExtents=${JSON.stringify(this.targetDb.projectExtents)}`);
546
664
  }
547
665
  if (!excludeOutliers &&
@@ -566,7 +684,6 @@ class IModelImporter {
566
684
  */
567
685
  optimizeGeometry(options) {
568
686
  if (options.inlineUniqueGeometryParts) {
569
- /* eslint-disable-next-line @typescript-eslint/no-deprecated */
570
687
  const result = this.targetDb.inlineGeometryParts();
571
688
  core_bentley_1.Logger.logInfo(loggerCategory, `Inlined ${result.numRefsInlined} references to ${result.numCandidateParts} geometry parts and deleted ${result.numPartsDeleted} parts.`);
572
689
  }
@@ -575,7 +692,7 @@ class IModelImporter {
575
692
  for (const [elementId, codeValue] of this._duplicateCodeValueMap) {
576
693
  const element = this.targetDb.elements.getElement(elementId);
577
694
  element.code.value = codeValue;
578
- element.update();
695
+ this._editTxn.updateElement(element.toJSON());
579
696
  }
580
697
  this._duplicateCodeValueMap.clear();
581
698
  }
@@ -583,7 +700,8 @@ class IModelImporter {
583
700
  * Needs to be called to perform necessary cleanup operations.
584
701
  * By not calling `finalize` there is a risk that data imported into targetDb will not be as expected.
585
702
  *
586
- * @note No need to call this If using [[IModelTransformer]] as it automatically invokes this method.
703
+ * @note [[IModelTransformer.process]] invokes this method automatically. Call it explicitly after
704
+ * composing the transformer's lower-level subset processing methods.
587
705
  */
588
706
  finalize() {
589
707
  this.resolveDuplicateCodeValues();
@@ -598,22 +716,26 @@ exports.IModelImporter = IModelImporter;
598
716
  */
599
717
  function hasEntityChanged(entity, entityProps, namesToIgnore) {
600
718
  let changed = false;
601
- entity.forEachProperty((propertyName, propertyMeta) => {
719
+ const entityClass = entity.iModel.schemaContext.getSchemaItemSync(entity.schemaItemKey);
720
+ (0, node_assert_1.strict)(ecschema_metadata_1.EntityClass.isEntityClass(entityClass) ||
721
+ ecschema_metadata_1.RelationshipClass.isRelationshipClass(entityClass), `Cannot get metadata for ${entity.classFullName}.`);
722
+ for (const property of entityClass.getPropertiesSync()) {
723
+ const propertyName = property.name;
602
724
  if (!changed) {
603
725
  if (namesToIgnore && namesToIgnore.has(propertyName)) {
604
726
  // skip
605
727
  }
606
- else if (core_common_1.PrimitiveTypeCode.Binary === propertyMeta.primitiveType) {
607
- changed = hasBinaryValueChanged(entity.asAny[propertyName], entityProps[propertyName]);
728
+ else if (ecschema_metadata_1.PropertyType.Binary === property.propertyType) {
729
+ changed = hasBinaryValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
608
730
  }
609
- else if (propertyMeta.isNavigation) {
610
- changed = hasNavigationValueChanged(entity.asAny[propertyName], entityProps[propertyName]);
731
+ else if (property.isNavigation()) {
732
+ changed = hasNavigationValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
611
733
  }
612
734
  else {
613
- changed = hasValueChanged(entity.asAny[propertyName], entityProps[propertyName]);
735
+ changed = hasValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
614
736
  }
615
737
  }
616
- });
738
+ }
617
739
  return changed;
618
740
  }
619
741
  /** Returns true if the specified binary values are different. */
@@ -643,9 +765,21 @@ function isDefaultSubCategory(props) {
643
765
  if (props.id === undefined)
644
766
  return false;
645
767
  if (!core_bentley_1.Id64.isId64(props.id))
646
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "subcategory had invalid id");
768
+ core_bentley_1.ITwinError.throwError({
769
+ iTwinErrorId: {
770
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
771
+ key: IModelTransformerError_1.IModelTransformerError.InvalidSubCategory,
772
+ },
773
+ message: "subcategory had invalid id",
774
+ });
647
775
  if (props.parent?.id === undefined)
648
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, `subcategory with id ${props.id} had no parent`);
776
+ core_bentley_1.ITwinError.throwError({
777
+ iTwinErrorId: {
778
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
779
+ key: IModelTransformerError_1.IModelTransformerError.InvalidSubCategory,
780
+ },
781
+ message: `subcategory with id ${props.id} had no parent`,
782
+ });
649
783
  return props.id === core_backend_1.IModelDb.getDefaultSubCategoryId(props.parent.id);
650
784
  }
651
785
  //# sourceMappingURL=IModelImporter.js.map