@itwin/imodel-transformer 2.0.0-dev.4 → 2.0.0-dev.41

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 (88) hide show
  1. package/README.md +12 -26
  2. package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
  3. package/lib/cjs/BranchProvenanceInitializer.js +35 -30
  4. package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
  5. package/lib/cjs/ChangesetScanner.d.ts +60 -0
  6. package/lib/cjs/ChangesetScanner.d.ts.map +1 -0
  7. package/lib/cjs/ChangesetScanner.js +101 -0
  8. package/lib/cjs/ChangesetScanner.js.map +1 -0
  9. package/lib/cjs/ECReferenceTypesCache.d.ts +0 -3
  10. package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
  11. package/lib/cjs/ECReferenceTypesCache.js +61 -103
  12. package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
  13. package/lib/cjs/ElementAspectCleanup.d.ts +16 -0
  14. package/lib/cjs/ElementAspectCleanup.d.ts.map +1 -0
  15. package/lib/cjs/ElementAspectCleanup.js +83 -0
  16. package/lib/cjs/ElementAspectCleanup.js.map +1 -0
  17. package/lib/cjs/ElementAspectExportCoordinator.d.ts +44 -0
  18. package/lib/cjs/ElementAspectExportCoordinator.d.ts.map +1 -0
  19. package/lib/cjs/ElementAspectExportCoordinator.js +148 -0
  20. package/lib/cjs/ElementAspectExportCoordinator.js.map +1 -0
  21. package/lib/cjs/ElementAspectExportProcessor.d.ts +55 -0
  22. package/lib/cjs/ElementAspectExportProcessor.d.ts.map +1 -0
  23. package/lib/cjs/ElementAspectExportProcessor.js +186 -0
  24. package/lib/cjs/ElementAspectExportProcessor.js.map +1 -0
  25. package/lib/cjs/ElementCascadingDeleter.d.ts +3 -3
  26. package/lib/cjs/ElementCascadingDeleter.d.ts.map +1 -1
  27. package/lib/cjs/ElementCascadingDeleter.js +12 -12
  28. package/lib/cjs/ElementCascadingDeleter.js.map +1 -1
  29. package/lib/cjs/EntityUnifier.d.ts +4 -8
  30. package/lib/cjs/EntityUnifier.d.ts.map +1 -1
  31. package/lib/cjs/EntityUnifier.js +6 -24
  32. package/lib/cjs/EntityUnifier.js.map +1 -1
  33. package/lib/cjs/IModelCloneContext.d.ts +6 -5
  34. package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
  35. package/lib/cjs/IModelCloneContext.js +74 -57
  36. package/lib/cjs/IModelCloneContext.js.map +1 -1
  37. package/lib/cjs/IModelExporter.d.ts +80 -32
  38. package/lib/cjs/IModelExporter.d.ts.map +1 -1
  39. package/lib/cjs/IModelExporter.js +563 -188
  40. package/lib/cjs/IModelExporter.js.map +1 -1
  41. package/lib/cjs/IModelImporter.d.ts +38 -24
  42. package/lib/cjs/IModelImporter.d.ts.map +1 -1
  43. package/lib/cjs/IModelImporter.js +210 -129
  44. package/lib/cjs/IModelImporter.js.map +1 -1
  45. package/lib/cjs/IModelTransformContext.d.ts +54 -0
  46. package/lib/cjs/IModelTransformContext.d.ts.map +1 -0
  47. package/lib/cjs/IModelTransformContext.js +3 -0
  48. package/lib/cjs/IModelTransformContext.js.map +1 -0
  49. package/lib/cjs/IModelTransformer.d.ts +101 -189
  50. package/lib/cjs/IModelTransformer.d.ts.map +1 -1
  51. package/lib/cjs/IModelTransformer.js +558 -1104
  52. package/lib/cjs/IModelTransformer.js.map +1 -1
  53. package/lib/cjs/IModelTransformerError.d.ts +76 -0
  54. package/lib/cjs/IModelTransformerError.d.ts.map +1 -0
  55. package/lib/cjs/IModelTransformerError.js +85 -0
  56. package/lib/cjs/IModelTransformerError.js.map +1 -0
  57. package/lib/cjs/ProvenanceManager.d.ts +170 -0
  58. package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
  59. package/lib/cjs/ProvenanceManager.js +735 -0
  60. package/lib/cjs/ProvenanceManager.js.map +1 -0
  61. package/lib/cjs/SyncTypeResolver.d.ts +34 -0
  62. package/lib/cjs/SyncTypeResolver.d.ts.map +1 -0
  63. package/lib/cjs/SyncTypeResolver.js +98 -0
  64. package/lib/cjs/SyncTypeResolver.js.map +1 -0
  65. package/lib/cjs/TransformerLoggerCategory.d.ts +1 -1
  66. package/lib/cjs/TransformerLoggerCategory.js +1 -1
  67. package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
  68. package/lib/cjs/TransformerPackageMetadata.d.ts +8 -0
  69. package/lib/cjs/TransformerPackageMetadata.d.ts.map +1 -0
  70. package/lib/cjs/TransformerPackageMetadata.js +14 -0
  71. package/lib/cjs/TransformerPackageMetadata.js.map +1 -0
  72. package/lib/cjs/imodel-transformer.d.ts +18 -0
  73. package/lib/cjs/imodel-transformer.d.ts.map +1 -1
  74. package/lib/cjs/imodel-transformer.js +29 -33
  75. package/lib/cjs/imodel-transformer.js.map +1 -1
  76. package/package.json +27 -39
  77. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts +0 -16
  78. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +0 -1
  79. package/lib/cjs/DetachedExportElementAspectsStrategy.js +0 -106
  80. package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +0 -1
  81. package/lib/cjs/ExportElementAspectsStrategy.d.ts +0 -35
  82. package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +0 -1
  83. package/lib/cjs/ExportElementAspectsStrategy.js +0 -52
  84. package/lib/cjs/ExportElementAspectsStrategy.js.map +0 -1
  85. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts +0 -12
  86. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +0 -1
  87. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +0 -47
  88. 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");
16
+ const node_assert_1 = require("node:assert");
17
17
  const ElementCascadingDeleter_1 = require("./ElementCascadingDeleter");
18
+ const ElementAspectCleanup_1 = require("./ElementAspectCleanup");
19
+ const ecschema_metadata_1 = require("@itwin/ecschema-metadata");
20
+ const IModelTransformerError_1 = require("./IModelTransformerError");
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
  */
@@ -71,11 +91,12 @@ class IModelImporter {
71
91
  "geometryGuid", // cannot compare GeometricModel.GeometryGuid values across iModels
72
92
  ]);
73
93
  /** Construct a new IModelImporter
74
- * @param targetDb The target IModelDb
94
+ * @param editTxn The [[EditTxn]] to use for write operations on the target iModel. The target db is derived from `editTxn.iModel`.
75
95
  * @param options The options that specify how the import should be done.
76
96
  */
77
- constructor(targetDb, options) {
78
- this.targetDb = targetDb;
97
+ constructor(editTxn, options) {
98
+ this.targetDb = editTxn.iModel;
99
+ this._editTxn = editTxn;
79
100
  this.options = {
80
101
  autoExtendProjectExtents: options?.autoExtendProjectExtents ?? true,
81
102
  preserveElementIdsForFiltering: options?.preserveElementIdsForFiltering ?? false,
@@ -83,6 +104,7 @@ class IModelImporter {
83
104
  skipPropagateChangesToRootElements: options?.skipPropagateChangesToRootElements ?? true,
84
105
  };
85
106
  this._duplicateCodeValueMap = new Map();
107
+ this._elementAspectCleanup = new ElementAspectCleanup_1.ElementAspectCleanup(this.targetDb, this._editTxn, async (aspect) => this.onDeleteElementAspect(aspect));
86
108
  }
87
109
  /**
88
110
  * Checks [[IModelImportOptions.skipPropagateChangesToRootElements]], [[IModelImporter.rootElementIds]], and [[IModelImporter.doNotUpdateElementIds]] and returns true for 'do not update', false for do 'update'.
@@ -105,9 +127,15 @@ class IModelImporter {
105
127
  this._elementsToUpdateDuringPreserveIds.add(elementId);
106
128
  }
107
129
  /** Import the specified ModelProps (either as an insert or an update) into the target iModel. */
108
- importModel(modelProps) {
130
+ async importModel(modelProps) {
109
131
  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");
132
+ core_bentley_1.ITwinError.throwError({
133
+ iTwinErrorId: {
134
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
135
+ key: IModelTransformerError_1.IModelTransformerError.InvalidModelId,
136
+ },
137
+ message: "Model Id not provided, should be the same as the ModeledElementId",
138
+ });
111
139
  if (this.doNotUpdateElement(modelProps.id)) {
112
140
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target model ${modelProps.id}`);
113
141
  return;
@@ -115,14 +143,14 @@ class IModelImporter {
115
143
  try {
116
144
  const model = this.targetDb.models.getModel(modelProps.id); // throws IModelError.NotFound if model does not exist
117
145
  if (hasEntityChanged(model, modelProps, this._modelPropertiesToIgnore)) {
118
- this.onUpdateModel(modelProps);
146
+ await this.onUpdateModel(modelProps);
119
147
  }
120
148
  }
121
149
  catch (error) {
122
150
  // catch NotFound error and insertModel
123
151
  if (error instanceof core_common_1.IModelError &&
124
152
  error.errorNumber === core_bentley_1.IModelStatus.NotFound) {
125
- this.onInsertModel(modelProps);
153
+ await this.onInsertModel(modelProps);
126
154
  return;
127
155
  }
128
156
  throw error;
@@ -131,29 +159,35 @@ class IModelImporter {
131
159
  /** Create a new Model from the specified ModelProps and insert it into the target iModel.
132
160
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertModel`.
133
161
  */
134
- onInsertModel(modelProps) {
162
+ async onInsertModel(modelProps) {
135
163
  try {
136
- const modelId = this.targetDb.models.insertModel(modelProps);
164
+ const modelId = this._editTxn.insertModel(modelProps);
137
165
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatModelForLogger(modelProps)}`);
138
- this.trackProgress();
166
+ await this.trackProgress();
139
167
  return modelId;
140
168
  }
141
169
  catch (error) {
142
170
  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);
171
+ // Translate only confirmed missing-class failures; otherwise retain the backend error's identity.
172
+ core_bentley_1.ITwinError.throwError({
173
+ iTwinErrorId: {
174
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
175
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
176
+ },
177
+ message: `Model class "${modelProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
178
+ cause: error,
179
+ });
146
180
  }
147
- throw error; // throw original error
181
+ throw error;
148
182
  }
149
183
  }
150
184
  /** Update an existing Model in the target iModel from the specified ModelProps.
151
185
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateModel`.
152
186
  */
153
- onUpdateModel(modelProps) {
154
- this.targetDb.models.updateModel(modelProps);
187
+ async onUpdateModel(modelProps) {
188
+ this._editTxn.updateModel(modelProps);
155
189
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatModelForLogger(modelProps)}`);
156
- this.trackProgress();
190
+ await this.trackProgress();
157
191
  }
158
192
  /** Format a Model for the Logger. */
159
193
  formatModelForLogger(modelProps) {
@@ -164,18 +198,18 @@ class IModelImporter {
164
198
  * Tries to update an element with the specified element properties.
165
199
  * If a duplicate code error occurs, it assigns a new unique code value and retries the update
166
200
  */
167
- tryUpdateElement(elemProps) {
201
+ async tryUpdateElement(elemProps) {
168
202
  try {
169
- this.onUpdateElement(elemProps);
203
+ await this.onUpdateElement(elemProps);
170
204
  }
171
205
  catch (err) {
172
206
  if (err.errorNumber === core_bentley_1.IModelStatus.DuplicateCode) {
173
- assert(elemProps.code.value !== undefined, "NULL code values are always considered unique and cannot clash");
207
+ (0, node_assert_1.strict)(elemProps.code.value !== undefined, "NULL code values are always considered unique and cannot clash");
174
208
  // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
175
209
  this._duplicateCodeValueMap.set(elemProps.id, elemProps.code.value);
176
210
  // Using NULL code values as an alternative is not valid because definition elements cannot have NULL code values.
177
211
  elemProps.code.value = core_bentley_1.Guid.createValue();
178
- this.onUpdateElement(elemProps);
212
+ await this.onUpdateElement(elemProps);
179
213
  }
180
214
  else {
181
215
  throw err;
@@ -183,7 +217,7 @@ class IModelImporter {
183
217
  }
184
218
  }
185
219
  /** Import the specified ElementProps (either as an insert or an update) into the target iModel. */
186
- importElement(elementProps) {
220
+ async importElement(elementProps) {
187
221
  if (undefined !== elementProps.id &&
188
222
  this.doNotUpdateElement(elementProps.id)) {
189
223
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not update target element ${elementProps.id}`);
@@ -191,7 +225,13 @@ class IModelImporter {
191
225
  }
192
226
  if (this.options.preserveElementIdsForFiltering) {
193
227
  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");
228
+ core_bentley_1.ITwinError.throwError({
229
+ iTwinErrorId: {
230
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
231
+ key: IModelTransformerError_1.IModelTransformerError.ElementIdRequired,
232
+ },
233
+ message: "elementProps.id must be defined during a preserveIds operation",
234
+ });
195
235
  }
196
236
  // Categories are the only element that onInserted will immediately insert a new element (their default subcategory)
197
237
  // since default subcategories always exist and always will be inserted after their categories, we treat them as an update
@@ -199,24 +239,24 @@ class IModelImporter {
199
239
  // Always present elements (0xe, 0x1, 0x10) also will be updated to prevent duplicate inserts.
200
240
  if ((isSubCategory(elementProps) && isDefaultSubCategory(elementProps)) ||
201
241
  this._rootElementIds.has(elementProps.id)) {
202
- this.onUpdateElement(elementProps);
242
+ await this.onUpdateElement(elementProps);
203
243
  }
204
244
  else {
205
245
  if (this._elementsToUpdateDuringPreserveIds.has(elementProps.id)) {
206
- this.tryUpdateElement(elementProps);
246
+ await this.tryUpdateElement(elementProps);
207
247
  this._elementsToUpdateDuringPreserveIds.delete(elementProps.id);
208
248
  }
209
249
  else {
210
- this.onInsertElement(elementProps);
250
+ await this.onInsertElement(elementProps);
211
251
  }
212
252
  }
213
253
  }
214
254
  else {
215
255
  if (undefined !== elementProps.id) {
216
- this.tryUpdateElement(elementProps);
256
+ await this.tryUpdateElement(elementProps);
217
257
  }
218
258
  else {
219
- elementProps.id = this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
259
+ elementProps.id = await this.onInsertElement(elementProps); // targetElementProps.id assigned by insertElement
220
260
  }
221
261
  }
222
262
  return elementProps.id;
@@ -225,16 +265,15 @@ class IModelImporter {
225
265
  * @returns The Id of the newly inserted Element.
226
266
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertElement`.
227
267
  */
228
- onInsertElement(elementProps) {
229
- /* eslint-disable @typescript-eslint/no-deprecated */
268
+ async onInsertElement(elementProps) {
230
269
  try {
231
- const elementId = this.targetDb.elements.insertElement(elementProps, {
270
+ const elementId = this._editTxn.insertElement(elementProps, {
232
271
  forceUseId: this.options.preserveElementIdsForFiltering,
233
272
  });
234
273
  // set the id like [IModelDb.insertElement]($backend), does, the raw nativeDb method does not
235
274
  elementProps.id = elementId;
236
275
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatElementForLogger(elementProps)}`);
237
- this.trackProgress();
276
+ await this.trackProgress();
238
277
  if (this.options.simplifyElementGeometry) {
239
278
  this.targetDb.simplifyElementGeometry({
240
279
  id: elementId,
@@ -246,26 +285,35 @@ class IModelImporter {
246
285
  }
247
286
  catch (error) {
248
287
  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);
288
+ core_bentley_1.ITwinError.throwError({
289
+ iTwinErrorId: {
290
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
291
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
292
+ },
293
+ message: `Element class "${elementProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
294
+ cause: error,
295
+ });
252
296
  }
253
- throw error; // throw original error
297
+ throw error;
254
298
  }
255
- /* eslint-enable @typescript-eslint/no-deprecated */
256
299
  }
257
300
  /** Update an existing Element in the target iModel from the specified ElementProps.
258
301
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateElement`.
259
302
  */
260
- onUpdateElement(elementProps) {
303
+ async onUpdateElement(elementProps) {
261
304
  if (!elementProps.id) {
262
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "ElementId not provided");
305
+ core_bentley_1.ITwinError.throwError({
306
+ iTwinErrorId: {
307
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
308
+ key: IModelTransformerError_1.IModelTransformerError.ElementIdRequired,
309
+ },
310
+ message: "ElementId not provided",
311
+ });
263
312
  }
264
- this.targetDb.elements.updateElement(elementProps);
313
+ this._editTxn.updateElement(elementProps);
265
314
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementForLogger(elementProps)}`);
266
- this.trackProgress();
315
+ await this.trackProgress();
267
316
  if (this.options.simplifyElementGeometry) {
268
- /* eslint-disable-next-line @typescript-eslint/no-deprecated */
269
317
  this.targetDb.simplifyElementGeometry({
270
318
  id: elementProps.id,
271
319
  convertBReps: true,
@@ -277,30 +325,30 @@ class IModelImporter {
277
325
  * Will delete special elements like definition elements and subjects.
278
326
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteElement`.
279
327
  */
280
- onDeleteElement(elementId) {
281
- (0, ElementCascadingDeleter_1.deleteElementTreeCascade)(this.targetDb, elementId);
328
+ async onDeleteElement(elementId) {
329
+ (0, ElementCascadingDeleter_1.deleteElementTreeCascade)(this._editTxn, elementId);
282
330
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted element ${elementId} and its descendants`);
283
- this.trackProgress();
331
+ await this.trackProgress();
284
332
  }
285
333
  /** Delete the specified Element from the target iModel. */
286
- deleteElement(elementId) {
334
+ async deleteElement(elementId) {
287
335
  if (this.doNotUpdateElement(elementId)) {
288
336
  core_bentley_1.Logger.logInfo(loggerCategory, `Do not delete target element ${elementId}`);
289
337
  return;
290
338
  }
291
- this.onDeleteElement(elementId);
339
+ await this.onDeleteElement(elementId);
292
340
  }
293
341
  /** Delete the specified Model from the target iModel.
294
342
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteModel`.
295
343
  */
296
- onDeleteModel(modelId) {
297
- this.targetDb.models.deleteModel(modelId);
344
+ async onDeleteModel(modelId) {
345
+ this._editTxn.deleteModel(modelId);
298
346
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted model ${modelId}`);
299
- this.trackProgress();
347
+ await this.trackProgress();
300
348
  }
301
349
  /** Delete the specified Model from the target iModel. */
302
- deleteModel(modelId) {
303
- this.onDeleteModel(modelId);
350
+ async deleteModel(modelId) {
351
+ await this.onDeleteModel(modelId);
304
352
  }
305
353
  /** Format an Element for the Logger. */
306
354
  formatElementForLogger(elementProps) {
@@ -312,14 +360,14 @@ class IModelImporter {
312
360
  return `${elementProps.classFullName} ${namePiece}[${elementProps.id}]`;
313
361
  }
314
362
  /** Import an ElementUniqueAspect into the target iModel. */
315
- importElementUniqueAspect(aspectProps) {
363
+ async importElementUniqueAspect(aspectProps) {
316
364
  const aspects = this.targetDb.elements.getAspects(aspectProps.element.id, aspectProps.classFullName);
317
365
  if (aspects.length === 0) {
318
366
  return this.onInsertElementAspect(aspectProps);
319
367
  }
320
368
  else if (hasEntityChanged(aspects[0], aspectProps)) {
321
369
  aspectProps.id = aspects[0].id;
322
- this.onUpdateElementAspect(aspectProps);
370
+ await this.onUpdateElementAspect(aspectProps);
323
371
  }
324
372
  return aspects[0].id;
325
373
  }
@@ -329,7 +377,7 @@ class IModelImporter {
329
377
  * @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
378
  * @returns the array of ids of the resulting ElementMultiAspects, in the same order of the aspectPropsArray parameter
331
379
  */
332
- importElementMultiAspects(aspectPropsArray,
380
+ async importElementMultiAspects(aspectPropsArray,
333
381
  /** caller must use this to enforce any aspects added by IModelTransformer are not considered for update */
334
382
  filterFunc = () => true) {
335
383
  const result = new Array(aspectPropsArray.length).fill(undefined);
@@ -337,91 +385,96 @@ class IModelImporter {
337
385
  return result;
338
386
  }
339
387
  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);
388
+ const proposedAspectsByClass = new Map();
389
+ aspectPropsArray.forEach((props, index) => {
390
+ const proposedAspects = proposedAspectsByClass.get(props.classFullName) ?? [];
391
+ proposedAspects.push({ props, index });
392
+ proposedAspectsByClass.set(props.classFullName, proposedAspects);
344
393
  });
345
394
  // 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);
395
+ for (const [aspectClassFullName, proposedAspects,] of proposedAspectsByClass) {
350
396
  const currentAspects = this.targetDb.elements
351
397
  .getAspects(elementId, aspectClassFullName)
352
398
  .map((props, index) => ({ props, index }))
353
399
  .filter(({ props }) => filterFunc(props));
354
400
  if (proposedAspects.length >= currentAspects.length) {
355
- proposedAspects.forEach(({ props, index: resultIndex }, index) => {
401
+ for (let index = 0; index < proposedAspects.length; index++) {
402
+ const { props, index: resultIndex } = proposedAspects[index];
356
403
  let id;
357
404
  if (index < currentAspects.length) {
358
405
  id = currentAspects[index].props.id;
359
406
  props.id = id;
360
407
  if (hasEntityChanged(currentAspects[index].props, props)) {
361
- this.onUpdateElementAspect(props);
408
+ await this.onUpdateElementAspect(props);
362
409
  }
363
410
  id = props.id;
364
411
  }
365
412
  else {
366
- id = this.onInsertElementAspect(props);
413
+ id = await this.onInsertElementAspect(props);
367
414
  }
368
415
  result[resultIndex] = id;
369
- });
416
+ }
370
417
  }
371
418
  else {
372
- currentAspects.forEach(({ props, index: resultIndex }, index) => {
373
- let id;
419
+ for (let index = 0; index < currentAspects.length; index++) {
420
+ const { props } = currentAspects[index];
374
421
  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);
422
+ const { props: proposedProps, index: resultIndex } = proposedAspects[index];
423
+ const id = props.id;
424
+ proposedProps.id = id;
425
+ if (hasEntityChanged(props, proposedProps)) {
426
+ await this.onUpdateElementAspect(proposedProps);
379
427
  }
380
428
  result[resultIndex] = id;
381
429
  }
382
430
  else {
383
- this.onDeleteElementAspect(props);
431
+ await this.onDeleteElementAspect(props);
384
432
  }
385
- });
433
+ }
386
434
  }
387
- });
388
- assert(result.every((r) => typeof r !== undefined));
435
+ }
436
+ (0, node_assert_1.strict)(result.every((r) => r !== undefined));
389
437
  return result;
390
438
  }
391
439
  /** Insert the ElementAspect into the target iModel.
392
440
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertElementAspect`.
393
441
  */
394
- onInsertElementAspect(aspectProps) {
442
+ async onInsertElementAspect(aspectProps) {
395
443
  try {
396
- const id = this.targetDb.elements.insertAspect(aspectProps);
444
+ const id = this._editTxn.insertAspect(aspectProps);
397
445
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatElementAspectForLogger(aspectProps)}`);
398
- this.trackProgress();
446
+ await this.trackProgress();
399
447
  return id;
400
448
  }
401
449
  catch (error) {
402
450
  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);
451
+ core_bentley_1.ITwinError.throwError({
452
+ iTwinErrorId: {
453
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
454
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
455
+ },
456
+ message: `ElementAspect class "${aspectProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
457
+ cause: error,
458
+ });
406
459
  }
407
- throw error; // throw original error
460
+ throw error;
408
461
  }
409
462
  }
410
463
  /** Update the ElementAspect within the target iModel.
411
464
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateElementAspect`.
412
465
  */
413
- onUpdateElementAspect(aspectProps) {
414
- this.targetDb.elements.updateAspect(aspectProps);
466
+ async onUpdateElementAspect(aspectProps) {
467
+ this._editTxn.updateAspect(aspectProps);
415
468
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatElementAspectForLogger(aspectProps)}`);
416
- this.trackProgress();
469
+ await this.trackProgress();
417
470
  }
418
471
  /** Delete the specified ElementAspect from the target iModel.
419
472
  * @note A subclass may override this method to customize delete behavior but should call `super.onDeleteElementAspect`.
420
473
  */
421
- onDeleteElementAspect(targetElementAspect) {
422
- this.targetDb.elements.deleteAspect(targetElementAspect.id);
474
+ async onDeleteElementAspect(targetElementAspect) {
475
+ this._editTxn.deleteAspect(targetElementAspect.id);
423
476
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted ${this.formatElementAspectForLogger(targetElementAspect)}`);
424
- this.trackProgress();
477
+ await this.trackProgress();
425
478
  }
426
479
  /** Format an ElementAspect for the Logger. */
427
480
  formatElementAspectForLogger(elementAspectProps) {
@@ -430,7 +483,7 @@ class IModelImporter {
430
483
  /** Import the specified RelationshipProps (either as an insert or an update) into the target iModel.
431
484
  * @returns The instance Id of the inserted or updated Relationship.
432
485
  */
433
- importRelationship(relationshipProps) {
486
+ async importRelationship(relationshipProps) {
434
487
  if (undefined === relationshipProps.sourceId ||
435
488
  !core_bentley_1.Id64.isValidId64(relationshipProps.sourceId)) {
436
489
  core_bentley_1.Logger.logInfo(loggerCategory, `Ignoring ${relationshipProps.classFullName} instance because of invalid RelationshipProps.sourceId`);
@@ -451,7 +504,7 @@ class IModelImporter {
451
504
  // if relationship found, update it
452
505
  relationshipProps.id = relationship.id;
453
506
  if (hasEntityChanged(relationship, relationshipProps)) {
454
- this.onUpdateRelationship(relationshipProps);
507
+ await this.onUpdateRelationship(relationshipProps);
455
508
  }
456
509
  return relationshipProps.id;
457
510
  }
@@ -463,62 +516,73 @@ class IModelImporter {
463
516
  * @returns The instance Id of the newly inserted relationship.
464
517
  * @note A subclass may override this method to customize insert behavior but should call `super.onInsertRelationship`.
465
518
  */
466
- onInsertRelationship(relationshipProps) {
519
+ async onInsertRelationship(relationshipProps) {
467
520
  try {
468
- const targetRelInstanceId = this.targetDb.relationships.insertInstance(relationshipProps);
521
+ const targetRelInstanceId = this._editTxn.insertRelationship(relationshipProps);
469
522
  core_bentley_1.Logger.logInfo(loggerCategory, `Inserted ${this.formatRelationshipForLogger(relationshipProps)}`);
470
- this.trackProgress();
523
+ await this.trackProgress();
471
524
  return targetRelInstanceId;
472
525
  }
473
526
  catch (error) {
474
527
  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);
528
+ core_bentley_1.ITwinError.throwError({
529
+ iTwinErrorId: {
530
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
531
+ key: IModelTransformerError_1.IModelTransformerError.TargetClassNotFound,
532
+ },
533
+ message: `Relationship class "${relationshipProps.classFullName}" not found in the target iModel. Was the latest version of the schema imported?`,
534
+ cause: error,
535
+ });
478
536
  }
479
- throw error; // throw original error
537
+ throw error;
480
538
  }
481
539
  }
482
540
  /** Update an existing Relationship in the target iModel from the specified RelationshipProps.
483
541
  * @note A subclass may override this method to customize update behavior but should call `super.onUpdateRelationship`.
484
542
  */
485
- onUpdateRelationship(relationshipProps) {
543
+ async onUpdateRelationship(relationshipProps) {
486
544
  if (!relationshipProps.id) {
487
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.InvalidId, "Relationship instance Id not provided");
545
+ core_bentley_1.ITwinError.throwError({
546
+ iTwinErrorId: {
547
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
548
+ key: IModelTransformerError_1.IModelTransformerError.RelationshipIdRequired,
549
+ },
550
+ message: "Relationship instance Id not provided",
551
+ });
488
552
  }
489
- this.targetDb.relationships.updateInstance(relationshipProps);
553
+ this._editTxn.updateRelationship(relationshipProps);
490
554
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated ${this.formatRelationshipForLogger(relationshipProps)}`);
491
- this.trackProgress();
555
+ await this.trackProgress();
492
556
  }
493
557
  /** Delete the specified Relationship from the target iModel. */
494
- onDeleteRelationship(relationshipProps) {
558
+ async onDeleteRelationship(relationshipProps) {
495
559
  // Only passing in what deleteInstance actually uses, full relationshipProps is not necessary.
496
- this.targetDb.relationships.deleteInstance({
560
+ this._editTxn.deleteRelationship({
497
561
  id: relationshipProps.id,
498
562
  classFullName: relationshipProps.classFullName,
499
563
  });
500
564
  core_bentley_1.Logger.logInfo(loggerCategory, `Deleted relationship ${relationshipProps.classFullName} id=${relationshipProps.id}`);
501
- this.trackProgress();
565
+ await this.trackProgress();
502
566
  }
503
567
  /** Delete the specified Relationship from the target iModel. */
504
- deleteRelationship(relationshipProps) {
505
- this.onDeleteRelationship(relationshipProps);
568
+ async deleteRelationship(relationshipProps) {
569
+ await this.onDeleteRelationship(relationshipProps);
506
570
  }
507
571
  /** Format a Relationship for the Logger. */
508
572
  formatRelationshipForLogger(relProps) {
509
573
  return `${relProps.classFullName} sourceId=[${relProps.sourceId}] targetId=[${relProps.targetId}]`;
510
574
  }
511
575
  /** Tracks incremental progress */
512
- trackProgress() {
576
+ async trackProgress() {
513
577
  this._progressCounter++;
514
578
  if (0 === this._progressCounter % this.progressInterval) {
515
- this.onProgress();
579
+ await this.onProgress();
516
580
  }
517
581
  }
518
582
  /** This method is called when IModelImporter has made incremental progress based on the [[progressInterval]] setting.
519
583
  * @note A subclass may override this method to report custom progress but should call `super.onProgress`.
520
584
  */
521
- onProgress() { }
585
+ async onProgress() { }
522
586
  /** Optionally compute the projectExtents for the target iModel depending on the options for this IModelImporter.
523
587
  * @note This method is automatically called from [IModelTransformer.process]($transformer).
524
588
  * @see [IModelDb.computeProjectExtents]($backend), [[autoExtendProjectExtents]]
@@ -535,13 +599,15 @@ class IModelImporter {
535
599
  const excludeOutliers = typeof this.options.autoExtendProjectExtents === "object"
536
600
  ? this.options.autoExtendProjectExtents.excludeOutliers
537
601
  : 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);
602
+ // If excludeOutliers is true, use the extents without outliers.
603
+ // Otherwise prefer extentsWithOutliers, but fall back to extents if it's undefined or a null range.
604
+ const extentsWithOutliers = computedProjectExtents.extentsWithOutliers;
605
+ const useExtentsWithOutliers = !excludeOutliers && extentsWithOutliers && !extentsWithOutliers.isNull;
606
+ const newProjectExtents = useExtentsWithOutliers
607
+ ? extentsWithOutliers
608
+ : computedProjectExtents.extents;
543
609
  if (!newProjectExtents.isAlmostEqual(this.targetDb.projectExtents)) {
544
- this.targetDb.updateProjectExtents(newProjectExtents);
610
+ this._editTxn.updateProjectExtents(newProjectExtents);
545
611
  core_bentley_1.Logger.logInfo(loggerCategory, `Updated projectExtents=${JSON.stringify(this.targetDb.projectExtents)}`);
546
612
  }
547
613
  if (!excludeOutliers &&
@@ -566,7 +632,6 @@ class IModelImporter {
566
632
  */
567
633
  optimizeGeometry(options) {
568
634
  if (options.inlineUniqueGeometryParts) {
569
- /* eslint-disable-next-line @typescript-eslint/no-deprecated */
570
635
  const result = this.targetDb.inlineGeometryParts();
571
636
  core_bentley_1.Logger.logInfo(loggerCategory, `Inlined ${result.numRefsInlined} references to ${result.numCandidateParts} geometry parts and deleted ${result.numPartsDeleted} parts.`);
572
637
  }
@@ -575,7 +640,7 @@ class IModelImporter {
575
640
  for (const [elementId, codeValue] of this._duplicateCodeValueMap) {
576
641
  const element = this.targetDb.elements.getElement(elementId);
577
642
  element.code.value = codeValue;
578
- element.update();
643
+ this._editTxn.updateElement(element.toJSON());
579
644
  }
580
645
  this._duplicateCodeValueMap.clear();
581
646
  }
@@ -598,22 +663,26 @@ exports.IModelImporter = IModelImporter;
598
663
  */
599
664
  function hasEntityChanged(entity, entityProps, namesToIgnore) {
600
665
  let changed = false;
601
- entity.forEachProperty((propertyName, propertyMeta) => {
666
+ const entityClass = entity.iModel.schemaContext.getSchemaItemSync(entity.schemaItemKey);
667
+ (0, node_assert_1.strict)(ecschema_metadata_1.EntityClass.isEntityClass(entityClass) ||
668
+ ecschema_metadata_1.RelationshipClass.isRelationshipClass(entityClass), `Cannot get metadata for ${entity.classFullName}.`);
669
+ for (const property of entityClass.getPropertiesSync()) {
670
+ const propertyName = property.name;
602
671
  if (!changed) {
603
672
  if (namesToIgnore && namesToIgnore.has(propertyName)) {
604
673
  // skip
605
674
  }
606
- else if (core_common_1.PrimitiveTypeCode.Binary === propertyMeta.primitiveType) {
607
- changed = hasBinaryValueChanged(entity.asAny[propertyName], entityProps[propertyName]);
675
+ else if (ecschema_metadata_1.PropertyType.Binary === property.propertyType) {
676
+ changed = hasBinaryValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
608
677
  }
609
- else if (propertyMeta.isNavigation) {
610
- changed = hasNavigationValueChanged(entity.asAny[propertyName], entityProps[propertyName]);
678
+ else if (property.isNavigation()) {
679
+ changed = hasNavigationValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
611
680
  }
612
681
  else {
613
- changed = hasValueChanged(entity.asAny[propertyName], entityProps[propertyName]);
682
+ changed = hasValueChanged(entity.asAny[core_common_1.ECJsNames.toJsName(propertyName)], entityProps[core_common_1.ECJsNames.toJsName(propertyName)]);
614
683
  }
615
684
  }
616
- });
685
+ }
617
686
  return changed;
618
687
  }
619
688
  /** Returns true if the specified binary values are different. */
@@ -643,9 +712,21 @@ function isDefaultSubCategory(props) {
643
712
  if (props.id === undefined)
644
713
  return false;
645
714
  if (!core_bentley_1.Id64.isId64(props.id))
646
- throw new core_common_1.IModelError(core_bentley_1.IModelStatus.BadElement, "subcategory had invalid id");
715
+ core_bentley_1.ITwinError.throwError({
716
+ iTwinErrorId: {
717
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
718
+ key: IModelTransformerError_1.IModelTransformerError.InvalidSubCategory,
719
+ },
720
+ message: "subcategory had invalid id",
721
+ });
647
722
  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`);
723
+ core_bentley_1.ITwinError.throwError({
724
+ iTwinErrorId: {
725
+ scope: IModelTransformerError_1.IModelTransformerErrorScope,
726
+ key: IModelTransformerError_1.IModelTransformerError.InvalidSubCategory,
727
+ },
728
+ message: `subcategory with id ${props.id} had no parent`,
729
+ });
649
730
  return props.id === core_backend_1.IModelDb.getDefaultSubCategoryId(props.parent.id);
650
731
  }
651
732
  //# sourceMappingURL=IModelImporter.js.map