@itwin/imodel-transformer 2.0.0-dev.6 → 2.0.0-dev.60

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 (123) 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 -34
  7. package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
  8. package/lib/cjs/ChangedElementForest.d.ts +14 -0
  9. package/lib/cjs/ChangedElementForest.d.ts.map +1 -0
  10. package/lib/cjs/ChangedElementForest.js +86 -0
  11. package/lib/cjs/ChangedElementForest.js.map +1 -0
  12. package/lib/cjs/ChangesetScanner.d.ts +61 -0
  13. package/lib/cjs/ChangesetScanner.d.ts.map +1 -0
  14. package/lib/cjs/ChangesetScanner.js +104 -0
  15. package/lib/cjs/ChangesetScanner.js.map +1 -0
  16. package/lib/cjs/ECReferenceTypesCache.d.ts +0 -3
  17. package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
  18. package/lib/cjs/ECReferenceTypesCache.js +61 -104
  19. package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
  20. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts.map +1 -1
  21. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js.map +1 -1
  22. package/lib/cjs/ElementAspectCleanup.d.ts +16 -0
  23. package/lib/cjs/ElementAspectCleanup.d.ts.map +1 -0
  24. package/lib/cjs/ElementAspectCleanup.js +87 -0
  25. package/lib/cjs/ElementAspectCleanup.js.map +1 -0
  26. package/lib/cjs/ElementAspectExportCoordinator.d.ts +44 -0
  27. package/lib/cjs/ElementAspectExportCoordinator.d.ts.map +1 -0
  28. package/lib/cjs/ElementAspectExportCoordinator.js +148 -0
  29. package/lib/cjs/ElementAspectExportCoordinator.js.map +1 -0
  30. package/lib/cjs/ElementAspectExportProcessor.d.ts +44 -0
  31. package/lib/cjs/ElementAspectExportProcessor.d.ts.map +1 -0
  32. package/lib/cjs/ElementAspectExportProcessor.js +152 -0
  33. package/lib/cjs/ElementAspectExportProcessor.js.map +1 -0
  34. package/lib/cjs/ElementBulkDelete.d.ts +11 -0
  35. package/lib/cjs/ElementBulkDelete.d.ts.map +1 -0
  36. package/lib/cjs/ElementBulkDelete.js +55 -0
  37. package/lib/cjs/ElementBulkDelete.js.map +1 -0
  38. package/lib/cjs/EntityExistenceCache.d.ts +43 -0
  39. package/lib/cjs/EntityExistenceCache.d.ts.map +1 -0
  40. package/lib/cjs/EntityExistenceCache.js +91 -0
  41. package/lib/cjs/EntityExistenceCache.js.map +1 -0
  42. package/lib/cjs/EntityMap.d.ts.map +1 -1
  43. package/lib/cjs/EntityMap.js.map +1 -1
  44. package/lib/cjs/EntityUnifier.d.ts +8 -8
  45. package/lib/cjs/EntityUnifier.d.ts.map +1 -1
  46. package/lib/cjs/EntityUnifier.js +56 -32
  47. package/lib/cjs/EntityUnifier.js.map +1 -1
  48. package/lib/cjs/IModelCloneContext.d.ts +9 -3
  49. package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
  50. package/lib/cjs/IModelCloneContext.js +66 -31
  51. package/lib/cjs/IModelCloneContext.js.map +1 -1
  52. package/lib/cjs/IModelExporter.d.ts +143 -30
  53. package/lib/cjs/IModelExporter.d.ts.map +1 -1
  54. package/lib/cjs/IModelExporter.js +893 -178
  55. package/lib/cjs/IModelExporter.js.map +1 -1
  56. package/lib/cjs/IModelImporter.d.ts +71 -30
  57. package/lib/cjs/IModelImporter.d.ts.map +1 -1
  58. package/lib/cjs/IModelImporter.js +269 -132
  59. package/lib/cjs/IModelImporter.js.map +1 -1
  60. package/lib/cjs/IModelTransformContext.d.ts +54 -0
  61. package/lib/cjs/IModelTransformContext.d.ts.map +1 -0
  62. package/lib/cjs/IModelTransformContext.js +3 -0
  63. package/lib/cjs/IModelTransformContext.js.map +1 -0
  64. package/lib/cjs/IModelTransformer.d.ts +130 -196
  65. package/lib/cjs/IModelTransformer.d.ts.map +1 -1
  66. package/lib/cjs/IModelTransformer.js +697 -1155
  67. package/lib/cjs/IModelTransformer.js.map +1 -1
  68. package/lib/cjs/IModelTransformerError.d.ts +84 -0
  69. package/lib/cjs/IModelTransformerError.d.ts.map +1 -0
  70. package/lib/cjs/IModelTransformerError.js +93 -0
  71. package/lib/cjs/IModelTransformerError.js.map +1 -0
  72. package/lib/cjs/ProvenanceManager.d.ts +177 -0
  73. package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
  74. package/lib/cjs/ProvenanceManager.js +719 -0
  75. package/lib/cjs/ProvenanceManager.js.map +1 -0
  76. package/lib/cjs/SyncTypeResolver.d.ts +34 -0
  77. package/lib/cjs/SyncTypeResolver.d.ts.map +1 -0
  78. package/lib/cjs/SyncTypeResolver.js +98 -0
  79. package/lib/cjs/SyncTypeResolver.js.map +1 -0
  80. package/lib/cjs/TransformerLoggerCategory.d.ts +1 -1
  81. package/lib/cjs/TransformerLoggerCategory.js +1 -1
  82. package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
  83. package/lib/cjs/TransformerPackageMetadata.d.ts +8 -0
  84. package/lib/cjs/TransformerPackageMetadata.d.ts.map +1 -0
  85. package/lib/cjs/TransformerPackageMetadata.js +47 -0
  86. package/lib/cjs/TransformerPackageMetadata.js.map +1 -0
  87. package/lib/cjs/imodel-transformer.d.ts +27 -0
  88. package/lib/cjs/imodel-transformer.d.ts.map +1 -1
  89. package/lib/cjs/imodel-transformer.js +61 -34
  90. package/lib/cjs/imodel-transformer.js.map +1 -1
  91. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts +29 -0
  92. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts.map +1 -0
  93. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js +212 -0
  94. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js.map +1 -0
  95. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts +25 -0
  96. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts.map +1 -0
  97. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js +196 -0
  98. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js.map +1 -0
  99. package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts +59 -0
  100. package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts.map +1 -0
  101. package/lib/cjs/schema-processing/SchemaProcessingStrategy.js +28 -0
  102. package/lib/cjs/schema-processing/SchemaProcessingStrategy.js.map +1 -0
  103. package/lib/cjs/schema-processing.d.ts +7 -0
  104. package/lib/cjs/schema-processing.d.ts.map +1 -0
  105. package/lib/cjs/schema-processing.js +27 -0
  106. package/lib/cjs/schema-processing.js.map +1 -0
  107. package/package.json +65 -43
  108. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts +0 -16
  109. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +0 -1
  110. package/lib/cjs/DetachedExportElementAspectsStrategy.js +0 -106
  111. package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +0 -1
  112. package/lib/cjs/ElementCascadingDeleter.d.ts +0 -22
  113. package/lib/cjs/ElementCascadingDeleter.d.ts.map +0 -1
  114. package/lib/cjs/ElementCascadingDeleter.js +0 -57
  115. package/lib/cjs/ElementCascadingDeleter.js.map +0 -1
  116. package/lib/cjs/ExportElementAspectsStrategy.d.ts +0 -35
  117. package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +0 -1
  118. package/lib/cjs/ExportElementAspectsStrategy.js +0 -52
  119. package/lib/cjs/ExportElementAspectsStrategy.js.map +0 -1
  120. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts +0 -12
  121. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +0 -1
  122. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +0 -47
  123. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +0 -1
@@ -13,9 +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
18
  const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
19
+ const IModelTransformerError_1 = require("./IModelTransformerError");
20
+ const ElementBulkDelete_1 = require("./ElementBulkDelete");
19
21
  const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IModelImporter;
20
22
  /** Base class for importing data into an iModel.
21
23
  * @see [iModel Transformation and Data Exchange]($docs/learning/transformer/index.md)
@@ -26,6 +28,23 @@ const loggerCategory = TransformerLoggerCategory_1.TransformerLoggerCategory.IMo
26
28
  class IModelImporter {
27
29
  /** The read/write target iModel. */
28
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
+ }
29
48
  /** resolved initialization options for the importer
30
49
  * @beta
31
50
  */
@@ -39,6 +58,7 @@ class IModelImporter {
39
58
  * To resolve code values to their intended values call [[IModelImporter.resolveDuplicateCodeValues]].
40
59
  */
41
60
  _duplicateCodeValueMap;
61
+ _entityExistenceCaches = new Set();
42
62
  /**
43
63
  * A set of elementIds that the transformer adds to while exporting elements to indicate that the element already exists in the target.
44
64
  * Defaults to an empty set.
@@ -52,9 +72,10 @@ class IModelImporter {
52
72
  * @note This set should stay small, as right after the transformer pushes to it, the importer will remove from the set.
53
73
  */
54
74
  _elementsToUpdateDuringPreserveIds = new Set([]);
55
- /** The set of elements that should not be updated by this IModelImporter.
56
- * 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.
57
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.
58
79
  */
59
80
  doNotUpdateElementIds = new Set([]);
60
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. */
@@ -72,11 +93,12 @@ class IModelImporter {
72
93
  "geometryGuid", // cannot compare GeometricModel.GeometryGuid values across iModels
73
94
  ]);
74
95
  /** Construct a new IModelImporter
75
- * @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`.
76
97
  * @param options The options that specify how the import should be done.
77
98
  */
78
- constructor(targetDb, options) {
79
- this.targetDb = targetDb;
99
+ constructor(editTxn, options) {
100
+ this.targetDb = editTxn.iModel;
101
+ this._editTxn = editTxn;
80
102
  this.options = {
81
103
  autoExtendProjectExtents: options?.autoExtendProjectExtents ?? true,
82
104
  preserveElementIdsForFiltering: options?.preserveElementIdsForFiltering ?? false,
@@ -84,6 +106,19 @@ class IModelImporter {
84
106
  skipPropagateChangesToRootElements: options?.skipPropagateChangesToRootElements ?? true,
85
107
  };
86
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);
87
122
  }
88
123
  /**
89
124
  * Checks [[IModelImportOptions.skipPropagateChangesToRootElements]], [[IModelImporter.rootElementIds]], and [[IModelImporter.doNotUpdateElementIds]] and returns true for 'do not update', false for do 'update'.
@@ -106,9 +141,15 @@ class IModelImporter {
106
141
  this._elementsToUpdateDuringPreserveIds.add(elementId);
107
142
  }
108
143
  /** Import the specified ModelProps (either as an insert or an update) into the target iModel. */
109
- importModel(modelProps) {
144
+ async importModel(modelProps) {
110
145
  if (undefined === modelProps.id || !core_bentley_1.Id64.isValidId64(modelProps.id))
111
- 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
+ });
112
153
  if (this.doNotUpdateElement(modelProps.id)) {
113
154
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target model ${modelProps.id}`);
114
155
  return;
@@ -116,14 +157,14 @@ class IModelImporter {
116
157
  try {
117
158
  const model = this.targetDb.models.getModel(modelProps.id); // throws IModelError.NotFound if model does not exist
118
159
  if (hasEntityChanged(model, modelProps, this._modelPropertiesToIgnore)) {
119
- this.onUpdateModel(modelProps);
160
+ await this.onUpdateModel(modelProps);
120
161
  }
121
162
  }
122
163
  catch (error) {
123
164
  // catch NotFound error and insertModel
124
165
  if (error instanceof core_common_1.IModelError &&
125
166
  error.errorNumber === core_bentley_1.IModelStatus.NotFound) {
126
- this.onInsertModel(modelProps);
167
+ await this.onInsertModel(modelProps);
127
168
  return;
128
169
  }
129
170
  throw error;
@@ -132,29 +173,35 @@ class IModelImporter {
132
173
  /** Create a new Model from the specified ModelProps and insert it into the target iModel.
133
174
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertModel`.
134
175
  */
135
- onInsertModel(modelProps) {
176
+ async onInsertModel(modelProps) {
136
177
  try {
137
- const modelId = this.targetDb.models.insertModel(modelProps);
178
+ const modelId = this._editTxn.insertModel(modelProps);
138
179
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatModelForLogger(modelProps)}`);
139
- this.trackProgress();
180
+ await this.trackProgress();
140
181
  return modelId;
141
182
  }
142
183
  catch (error) {
143
184
  if (!this.targetDb.containsClass(modelProps.classFullName)) {
144
- // replace standard insert error with something more helpful
145
- const errorMessage = `Model class "${modelProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
146
- 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
+ });
147
194
  }
148
- throw error; // throw original error
195
+ throw error;
149
196
  }
150
197
  }
151
198
  /** Update an existing Model in the target iModel from the specified ModelProps.
152
199
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateModel`.
153
200
  */
154
- onUpdateModel(modelProps) {
155
- this.targetDb.models.updateModel(modelProps);
201
+ async onUpdateModel(modelProps) {
202
+ this._editTxn.updateModel(modelProps);
156
203
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatModelForLogger(modelProps)}`);
157
- this.trackProgress();
204
+ await this.trackProgress();
158
205
  }
159
206
  /** Format a Model for the Logger. */
160
207
  formatModelForLogger(modelProps) {
@@ -165,18 +212,18 @@ class IModelImporter {
165
212
  * Tries to update an element with the specified element properties.
166
213
  * If a duplicate code error occurs, it assigns a new unique code value and retries the update
167
214
  */
168
- tryUpdateElement(elemProps) {
215
+ async tryUpdateElement(elemProps) {
169
216
  try {
170
- this.onUpdateElement(elemProps);
217
+ await this.onUpdateElement(elemProps);
171
218
  }
172
219
  catch (err) {
173
220
  if (err.errorNumber === core_bentley_1.IModelStatus.DuplicateCode) {
174
- 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");
175
222
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
176
223
  this._duplicateCodeValueMap.set(elemProps.id, elemProps.code.value);
177
224
  // Using NULL code values as an alternative is not valid because definition elements cannot have NULL code values.
178
225
  elemProps.code.value = core_bentley_1.Guid.createValue();
179
- this.onUpdateElement(elemProps);
226
+ await this.onUpdateElement(elemProps);
180
227
  }
181
228
  else {
182
229
  throw err;
@@ -184,7 +231,7 @@ class IModelImporter {
184
231
  }
185
232
  }
186
233
  /** Import the specified ElementProps (either as an insert or an update) into the target iModel. */
187
- importElement(elementProps) {
234
+ async importElement(elementProps) {
188
235
  if (undefined !== elementProps.id &&
189
236
  this.doNotUpdateElement(elementProps.id)) {
190
237
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target element ${elementProps.id}`);
@@ -192,7 +239,13 @@ class IModelImporter {
192
239
  }
193
240
  if (this.options.preserveElementIdsForFiltering) {
194
241
  if (elementProps.id === undefined) {
195
- 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
+ });
196
249
  }
197
250
  // Categories are the only element that onInserted will immediately insert a new element (their default subcategory)
198
251
  // since default subcategories always exist and always will be inserted after their categories, we treat them as an update
@@ -200,24 +253,24 @@ class IModelImporter {
200
253
  // Always present elements (0xe, 0x1, 0x10) also will be updated to prevent duplicate inserts.
201
254
  if ((isSubCategory(elementProps) && isDefaultSubCategory(elementProps)) ||
202
255
  this._rootElementIds.has(elementProps.id)) {
203
- this.onUpdateElement(elementProps);
256
+ await this.onUpdateElement(elementProps);
204
257
  }
205
258
  else {
206
259
  if (this._elementsToUpdateDuringPreserveIds.has(elementProps.id)) {
207
- this.tryUpdateElement(elementProps);
260
+ await this.tryUpdateElement(elementProps);
208
261
  this._elementsToUpdateDuringPreserveIds.delete(elementProps.id);
209
262
  }
210
263
  else {
211
- this.onInsertElement(elementProps);
264
+ await this.onInsertElement(elementProps);
212
265
  }
213
266
  }
214
267
  }
215
268
  else {
216
269
  if (undefined !== elementProps.id) {
217
- this.tryUpdateElement(elementProps);
270
+ await this.tryUpdateElement(elementProps);
218
271
  }
219
272
  else {
220
- elementProps.id = this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
273
+ elementProps.id = await this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
221
274
  }
222
275
  }
223
276
  return elementProps.id;
@@ -226,15 +279,15 @@ class IModelImporter {
226
279
  * @returns The Id of the newly inserted Element.
227
280
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertElement`.
228
281
  */
229
- onInsertElement(elementProps) {
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,23 +299,34 @@ 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
313
  }
256
314
  /** Update an existing Element in the target iModel from the specified ElementProps.
257
315
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateElement`.
258
316
  */
259
- onUpdateElement(elementProps) {
317
+ async onUpdateElement(elementProps) {
260
318
  if (!elementProps.id) {
261
- 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
+ });
262
326
  }
263
- this.targetDb.elements.updateElement(elementProps);
327
+ this._editTxn.updateElement(elementProps);
264
328
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementForLogger(elementProps)}`);
265
- this.trackProgress();
329
+ await this.trackProgress();
266
330
  if (this.options.simplifyElementGeometry) {
267
331
  this.targetDb.simplifyElementGeometry({
268
332
  id: elementProps.id,
@@ -271,34 +335,70 @@ class IModelImporter {
271
335
  core_bentley_1.Logger.logInfo(loggerCategory, `Simplified element geometry for ${this.formatElementForLogger(elementProps)}`);
272
336
  }
273
337
  }
274
- /** Delete the specified Element (and all its children) from the target iModel.
275
- * Will delete special elements like definition elements and subjects.
276
- * @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.
277
344
  */
278
- onDeleteElement(elementId) {
279
- (0, ElementCascadingDeleter_1.deleteElementTreeCascade)(this.targetDb, elementId);
280
- core_bentley_1.Logger.logInfo(loggerCategory, `Deleted element ${elementId} and its descendants`);
281
- this.trackProgress();
282
- }
283
- /** Delete the specified Element from the target iModel. */
284
- deleteElement(elementId) {
285
- if (this.doNotUpdateElement(elementId)) {
286
- 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)
287
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}`);
288
380
  }
289
- 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);
290
387
  }
291
388
  /** Delete the specified Model from the target iModel.
292
389
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteModel`.
293
390
  */
294
391
  async onDeleteModel(modelId) {
295
- this.targetDb.models.deleteModel(modelId);
392
+ this._editTxn.deleteModel(modelId);
296
393
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted model ${modelId}`);
297
- this.trackProgress();
394
+ await this.trackProgress();
298
395
  }
299
396
  /** Delete the specified Model from the target iModel. */
300
397
  async deleteModel(modelId) {
301
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);
302
402
  }
303
403
  /** Format an Element for the Logger. */
304
404
  formatElementForLogger(elementProps) {
@@ -310,14 +410,14 @@ class IModelImporter {
310
410
  return `${elementProps.classFullName} ${namePiece}[${elementProps.id}]`;
311
411
  }
312
412
  /** Import an ElementUniqueAspect into the target iModel. */
313
- importElementUniqueAspect(aspectProps) {
413
+ async importElementUniqueAspect(aspectProps) {
314
414
  const aspects = this.targetDb.elements.getAspects(aspectProps.element.id, aspectProps.classFullName);
315
415
  if (aspects.length === 0) {
316
416
  return this.onInsertElementAspect(aspectProps);
317
417
  }
318
418
  else if (hasEntityChanged(aspects[0], aspectProps)) {
319
419
  aspectProps.id = aspects[0].id;
320
- this.onUpdateElementAspect(aspectProps);
420
+ await this.onUpdateElementAspect(aspectProps);
321
421
  }
322
422
  return aspects[0].id;
323
423
  }
@@ -327,7 +427,7 @@ class IModelImporter {
327
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.
328
428
  * @returns the array of ids of the resulting ElementMultiAspects, in the same order of the aspectPropsArray parameter
329
429
  */
330
- importElementMultiAspects(aspectPropsArray,
430
+ async importElementMultiAspects(aspectPropsArray,
331
431
  /** caller must use this to enforce any aspects added by IModelTransformer are not considered for update */
332
432
  filterFunc = () => true) {
333
433
  const result = new Array(aspectPropsArray.length).fill(undefined);
@@ -335,91 +435,96 @@ class IModelImporter {
335
435
  return result;
336
436
  }
337
437
  const elementId = aspectPropsArray[0].element.id;
338
- // Determine the set of ElementMultiAspect classes to consider
339
- const aspectClassFullNames = new Set();
340
- aspectPropsArray.forEach((aspectsProps) => {
341
- 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);
342
443
  });
343
444
  // Handle ElementMultiAspects in groups by class
344
- aspectClassFullNames.forEach((aspectClassFullName) => {
345
- const proposedAspects = aspectPropsArray
346
- .map((props, index) => ({ props, index }))
347
- .filter(({ props }) => aspectClassFullName === props.classFullName);
445
+ for (const [aspectClassFullName, proposedAspects,] of proposedAspectsByClass) {
348
446
  const currentAspects = this.targetDb.elements
349
447
  .getAspects(elementId, aspectClassFullName)
350
448
  .map((props, index) => ({ props, index }))
351
449
  .filter(({ props }) => filterFunc(props));
352
450
  if (proposedAspects.length >= currentAspects.length) {
353
- proposedAspects.forEach(({ props, index: resultIndex }, index) => {
451
+ for (let index = 0; index < proposedAspects.length; index++) {
452
+ const { props, index: resultIndex } = proposedAspects[index];
354
453
  let id;
355
454
  if (index < currentAspects.length) {
356
455
  id = currentAspects[index].props.id;
357
456
  props.id = id;
358
457
  if (hasEntityChanged(currentAspects[index].props, props)) {
359
- this.onUpdateElementAspect(props);
458
+ await this.onUpdateElementAspect(props);
360
459
  }
361
460
  id = props.id;
362
461
  }
363
462
  else {
364
- id = this.onInsertElementAspect(props);
463
+ id = await this.onInsertElementAspect(props);
365
464
  }
366
465
  result[resultIndex] = id;
367
- });
466
+ }
368
467
  }
369
468
  else {
370
- currentAspects.forEach(({ props, index: resultIndex }, index) => {
371
- let id;
469
+ for (let index = 0; index < currentAspects.length; index++) {
470
+ const { props } = currentAspects[index];
372
471
  if (index < proposedAspects.length) {
373
- id = props.id;
374
- proposedAspects[index].props.id = id;
375
- if (hasEntityChanged(props, proposedAspects[index].props)) {
376
- 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);
377
477
  }
378
478
  result[resultIndex] = id;
379
479
  }
380
480
  else {
381
- this.onDeleteElementAspect(props);
481
+ await this.onDeleteElementAspect(props);
382
482
  }
383
- });
483
+ }
384
484
  }
385
- });
386
- assert(result.every((r) => typeof r !== undefined));
485
+ }
486
+ (0, node_assert_1.strict)(result.every((r) => r !== undefined));
387
487
  return result;
388
488
  }
389
489
  /** Insert the ElementAspect into the target iModel.
390
490
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertElementAspect`.
391
491
  */
392
- onInsertElementAspect(aspectProps) {
492
+ async onInsertElementAspect(aspectProps) {
393
493
  try {
394
- const id = this.targetDb.elements.insertAspect(aspectProps);
494
+ const id = this._editTxn.insertAspect(aspectProps);
395
495
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatElementAspectForLogger(aspectProps)}`);
396
- this.trackProgress();
496
+ await this.trackProgress();
397
497
  return id;
398
498
  }
399
499
  catch (error) {
400
500
  if (!this.targetDb.containsClass(aspectProps.classFullName)) {
401
- // replace standard insert error with something more helpful
402
- const errorMessage = `ElementAspect class "${aspectProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
403
- 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
+ });
404
509
  }
405
- throw error; // throw original error
510
+ throw error;
406
511
  }
407
512
  }
408
513
  /** Update the ElementAspect within the target iModel.
409
514
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateElementAspect`.
410
515
  */
411
- onUpdateElementAspect(aspectProps) {
412
- this.targetDb.elements.updateAspect(aspectProps);
516
+ async onUpdateElementAspect(aspectProps) {
517
+ this._editTxn.updateAspect(aspectProps);
413
518
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementAspectForLogger(aspectProps)}`);
414
- this.trackProgress();
519
+ await this.trackProgress();
415
520
  }
416
521
  /** Delete the specified ElementAspect from the target iModel.
417
522
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteElementAspect`.
418
523
  */
419
- onDeleteElementAspect(targetElementAspect) {
420
- this.targetDb.elements.deleteAspect(targetElementAspect.id);
524
+ async onDeleteElementAspect(targetElementAspect) {
525
+ this._editTxn.deleteAspect(targetElementAspect.id);
421
526
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted ${this.formatElementAspectForLogger(targetElementAspect)}`);
422
- this.trackProgress();
527
+ await this.trackProgress();
423
528
  }
424
529
  /** Format an ElementAspect for the Logger. */
425
530
  formatElementAspectForLogger(elementAspectProps) {
@@ -428,7 +533,7 @@ class IModelImporter {
428
533
  /** Import the specified RelationshipProps (either as an insert or an update) into the target iModel.
429
534
  * @returns The instance Id of the inserted or updated Relationship.
430
535
  */
431
- importRelationship(relationshipProps) {
536
+ async importRelationship(relationshipProps) {
432
537
  if (undefined === relationshipProps.sourceId ||
433
538
  !core_bentley_1.Id64.isValidId64(relationshipProps.sourceId)) {
434
539
  core_bentley_1.Logger.logInfo(loggerCategory, `Ignoring ${relationshipProps.classFullName} instance because of invalid RelationshipProps.sourceId`);
@@ -449,7 +554,7 @@ class IModelImporter {
449
554
  // if relationship found, update it
450
555
  relationshipProps.id = relationship.id;
451
556
  if (hasEntityChanged(relationship, relationshipProps)) {
452
- this.onUpdateRelationship(relationshipProps);
557
+ await this.onUpdateRelationship(relationshipProps);
453
558
  }
454
559
  return relationshipProps.id;
455
560
  }
@@ -461,62 +566,75 @@ class IModelImporter {
461
566
  * @returns The instance Id of the newly inserted relationship.
462
567
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertRelationship`.
463
568
  */
464
- onInsertRelationship(relationshipProps) {
569
+ async onInsertRelationship(relationshipProps) {
465
570
  try {
466
- const targetRelInstanceId = this.targetDb.relationships.insertInstance(relationshipProps);
571
+ const targetRelInstanceId = this._editTxn.insertRelationship(relationshipProps);
467
572
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatRelationshipForLogger(relationshipProps)}`);
468
- this.trackProgress();
573
+ await this.trackProgress();
469
574
  return targetRelInstanceId;
470
575
  }
471
576
  catch (error) {
472
577
  if (!this.targetDb.containsClass(relationshipProps.classFullName)) {
473
- // replace standard insert error with something more helpful
474
- const errorMessage = `Relationship class "${relationshipProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`;
475
- 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
+ });
476
586
  }
477
- throw error; // throw original error
587
+ throw error;
478
588
  }
479
589
  }
480
590
  /** Update an existing Relationship in the target iModel from the specified RelationshipProps.
481
591
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateRelationship`.
482
592
  */
483
- onUpdateRelationship(relationshipProps) {
593
+ async onUpdateRelationship(relationshipProps) {
484
594
  if (!relationshipProps.id) {
485
- 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
+ });
486
602
  }
487
- this.targetDb.relationships.updateInstance(relationshipProps);
603
+ this._editTxn.updateRelationship(relationshipProps);
488
604
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatRelationshipForLogger(relationshipProps)}`);
489
- this.trackProgress();
605
+ await this.trackProgress();
490
606
  }
491
607
  /** Delete the specified Relationship from the target iModel. */
492
- onDeleteRelationship(relationshipProps) {
608
+ async onDeleteRelationship(relationshipProps) {
493
609
  // Only passing in what deleteInstance actually uses, full relationshipProps is not necessary.
494
- this.targetDb.relationships.deleteInstance({
610
+ this._editTxn.deleteRelationship({
495
611
  id: relationshipProps.id,
496
612
  classFullName: relationshipProps.classFullName,
497
613
  });
498
614
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted relationship ${relationshipProps.classFullName} id=${relationshipProps.id}`);
499
- this.trackProgress();
615
+ await this.trackProgress();
500
616
  }
501
617
  /** Delete the specified Relationship from the target iModel. */
502
- deleteRelationship(relationshipProps) {
503
- this.onDeleteRelationship(relationshipProps);
618
+ async deleteRelationship(relationshipProps) {
619
+ await this.onDeleteRelationship(relationshipProps);
504
620
  }
505
621
  /** Format a Relationship for the Logger. */
506
622
  formatRelationshipForLogger(relProps) {
507
623
  return `${relProps.classFullName} sourceId=[${relProps.sourceId}] targetId=[${relProps.targetId}]`;
508
624
  }
509
625
  /** Tracks incremental progress */
510
- trackProgress() {
511
- this._progressCounter++;
512
- if (0 === this._progressCounter % this.progressInterval) {
513
- this.onProgress();
514
- }
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();
515
633
  }
516
634
  /** This method is called when IModelImporter has made incremental progress based on the [[progressInterval]] setting.
517
635
  * @note A subclass may override this method to report custom progress but should call `super.onProgress`.
518
636
  */
519
- onProgress() { }
637
+ async onProgress() { }
520
638
  /** Optionally compute the projectExtents for the target iModel depending on the options for this IModelImporter.
521
639
  * @note This method is automatically called from [IModelTransformer.process]($transformer).
522
640
  * @see [IModelDb.computeProjectExtents]($backend), [[autoExtendProjectExtents]]
@@ -533,13 +651,15 @@ class IModelImporter {
533
651
  const excludeOutliers = typeof this.options.autoExtendProjectExtents === "object"
534
652
  ? this.options.autoExtendProjectExtents.excludeOutliers
535
653
  : false;
536
- // If excludeOutliers is true, use the extents without outliers. If extentsWithOutliers is undefined, fall back to extents.
537
- const newProjectExtents = excludeOutliers
538
- ? computedProjectExtents.extents
539
- : (computedProjectExtents.extentsWithOutliers ??
540
- 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;
541
661
  if (!newProjectExtents.isAlmostEqual(this.targetDb.projectExtents)) {
542
- this.targetDb.updateProjectExtents(newProjectExtents);
662
+ this._editTxn.updateProjectExtents(newProjectExtents);
543
663
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated projectExtents=${JSON.stringify(this.targetDb.projectExtents)}`);
544
664
  }
545
665
  if (!excludeOutliers &&
@@ -572,7 +692,7 @@ class IModelImporter {
572
692
  for (const [elementId, codeValue] of this._duplicateCodeValueMap) {
573
693
  const element = this.targetDb.elements.getElement(elementId);
574
694
  element.code.value = codeValue;
575
- element.update();
695
+ this._editTxn.updateElement(element.toJSON());
576
696
  }
577
697
  this._duplicateCodeValueMap.clear();
578
698
  }
@@ -580,7 +700,8 @@ class IModelImporter {
580
700
  * Needs to be called to perform necessary cleanup operations.
581
701
  * By not calling `finalize` there is a risk that data imported into targetDb will not be as expected.
582
702
  *
583
- * @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.
584
705
  */
585
706
  finalize() {
586
707
  this.resolveDuplicateCodeValues();
@@ -595,7 +716,11 @@ exports.IModelImporter = IModelImporter;
595
716
  */
596
717
  function hasEntityChanged(entity, entityProps, namesToIgnore) {
597
718
  let changed = false;
598
- entity.forEach((propertyName, property) => {
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;
599
724
  if (!changed) {
600
725
  if (namesToIgnore && namesToIgnore.has(propertyName)) {
601
726
  // skip
@@ -610,7 +735,7 @@ function hasEntityChanged(entity, entityProps, namesToIgnore) {
610
735
  changed = hasValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
611
736
  }
612
737
  }
613
- });
738
+ }
614
739
  return changed;
615
740
  }
616
741
  /** Returns true if the specified binary values are different. */
@@ -640,9 +765,21 @@ function isDefaultSubCategory(props) {
640
765
  if (props.id === undefined)
641
766
  return false;
642
767
  if (!core_bentley_1.Id64.isId64(props.id))
643
- 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
+ });
644
775
  if (props.parent?.id === undefined)
645
- 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
+ });
646
783
  return props.id === core_backend_1.IModelDb.getDefaultSubCategoryId(props.parent.id);
647
784
  }
648
785
  //# sourceMappingURL=IModelImporter.js.map