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

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