@itwin/imodel-transformer 2.0.0-dev.9 → 2.0.0-experimental.0

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 (128) hide show
  1. package/README.md +13 -27
  2. package/lib/cjs/Algo.d.ts +0 -4
  3. package/lib/cjs/Algo.d.ts.map +1 -1
  4. package/lib/cjs/Algo.js +0 -11
  5. package/lib/cjs/Algo.js.map +1 -1
  6. package/lib/cjs/BigMap.d.ts.map +1 -1
  7. package/lib/cjs/BigMap.js.map +1 -1
  8. package/lib/cjs/BranchProvenanceInitializer.d.ts.map +1 -1
  9. package/lib/cjs/BranchProvenanceInitializer.js +26 -26
  10. package/lib/cjs/BranchProvenanceInitializer.js.map +1 -1
  11. package/lib/cjs/ChangedElementForest.d.ts +14 -0
  12. package/lib/cjs/ChangedElementForest.d.ts.map +1 -0
  13. package/lib/cjs/ChangedElementForest.js +86 -0
  14. package/lib/cjs/ChangedElementForest.js.map +1 -0
  15. package/lib/cjs/ChangesetScanner.d.ts +61 -0
  16. package/lib/cjs/ChangesetScanner.d.ts.map +1 -0
  17. package/lib/cjs/ChangesetScanner.js +104 -0
  18. package/lib/cjs/ChangesetScanner.js.map +1 -0
  19. package/lib/cjs/ECReferenceTypesCache.d.ts +0 -4
  20. package/lib/cjs/ECReferenceTypesCache.d.ts.map +1 -1
  21. package/lib/cjs/ECReferenceTypesCache.js +54 -102
  22. package/lib/cjs/ECReferenceTypesCache.js.map +1 -1
  23. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.d.ts.map +1 -1
  24. package/lib/cjs/ECSqlReaderAsyncIterableIteratorAdapter.js.map +1 -1
  25. package/lib/cjs/ElementAspectCleanup.d.ts +56 -0
  26. package/lib/cjs/ElementAspectCleanup.d.ts.map +1 -0
  27. package/lib/cjs/ElementAspectCleanup.js +175 -0
  28. package/lib/cjs/ElementAspectCleanup.js.map +1 -0
  29. package/lib/cjs/ElementAspectExportCoordinator.d.ts +47 -0
  30. package/lib/cjs/ElementAspectExportCoordinator.d.ts.map +1 -0
  31. package/lib/cjs/ElementAspectExportCoordinator.js +160 -0
  32. package/lib/cjs/ElementAspectExportCoordinator.js.map +1 -0
  33. package/lib/cjs/ElementAspectExportProcessor.d.ts +44 -0
  34. package/lib/cjs/ElementAspectExportProcessor.d.ts.map +1 -0
  35. package/lib/cjs/ElementAspectExportProcessor.js +152 -0
  36. package/lib/cjs/ElementAspectExportProcessor.js.map +1 -0
  37. package/lib/cjs/ElementBulkDelete.d.ts +30 -0
  38. package/lib/cjs/ElementBulkDelete.d.ts.map +1 -0
  39. package/lib/cjs/ElementBulkDelete.js +358 -0
  40. package/lib/cjs/ElementBulkDelete.js.map +1 -0
  41. package/lib/cjs/EntityExistenceCache.d.ts +43 -0
  42. package/lib/cjs/EntityExistenceCache.d.ts.map +1 -0
  43. package/lib/cjs/EntityExistenceCache.js +91 -0
  44. package/lib/cjs/EntityExistenceCache.js.map +1 -0
  45. package/lib/cjs/EntityUnifier.d.ts +7 -7
  46. package/lib/cjs/EntityUnifier.d.ts.map +1 -1
  47. package/lib/cjs/EntityUnifier.js +55 -32
  48. package/lib/cjs/EntityUnifier.js.map +1 -1
  49. package/lib/cjs/IModelCloneContext.d.ts +8 -2
  50. package/lib/cjs/IModelCloneContext.d.ts.map +1 -1
  51. package/lib/cjs/IModelCloneContext.js +42 -12
  52. package/lib/cjs/IModelCloneContext.js.map +1 -1
  53. package/lib/cjs/IModelExporter.d.ts +143 -30
  54. package/lib/cjs/IModelExporter.d.ts.map +1 -1
  55. package/lib/cjs/IModelExporter.js +871 -172
  56. package/lib/cjs/IModelExporter.js.map +1 -1
  57. package/lib/cjs/IModelImporter.d.ts +87 -31
  58. package/lib/cjs/IModelImporter.d.ts.map +1 -1
  59. package/lib/cjs/IModelImporter.js +361 -139
  60. package/lib/cjs/IModelImporter.js.map +1 -1
  61. package/lib/cjs/IModelTransformContext.d.ts +54 -0
  62. package/lib/cjs/IModelTransformContext.d.ts.map +1 -0
  63. package/lib/cjs/IModelTransformContext.js +3 -0
  64. package/lib/cjs/IModelTransformContext.js.map +1 -0
  65. package/lib/cjs/IModelTransformer.d.ts +132 -174
  66. package/lib/cjs/IModelTransformer.d.ts.map +1 -1
  67. package/lib/cjs/IModelTransformer.js +679 -1025
  68. package/lib/cjs/IModelTransformer.js.map +1 -1
  69. package/lib/cjs/IModelTransformerError.d.ts +84 -0
  70. package/lib/cjs/IModelTransformerError.d.ts.map +1 -0
  71. package/lib/cjs/IModelTransformerError.js +93 -0
  72. package/lib/cjs/IModelTransformerError.js.map +1 -0
  73. package/lib/cjs/ProvenanceManager.d.ts +194 -0
  74. package/lib/cjs/ProvenanceManager.d.ts.map +1 -0
  75. package/lib/cjs/ProvenanceManager.js +771 -0
  76. package/lib/cjs/ProvenanceManager.js.map +1 -0
  77. package/lib/cjs/SyncTypeResolver.d.ts +32 -0
  78. package/lib/cjs/SyncTypeResolver.d.ts.map +1 -0
  79. package/lib/cjs/SyncTypeResolver.js +92 -0
  80. package/lib/cjs/SyncTypeResolver.js.map +1 -0
  81. package/lib/cjs/TransformerLoggerCategory.d.ts +1 -1
  82. package/lib/cjs/TransformerLoggerCategory.js +1 -1
  83. package/lib/cjs/TransformerLoggerCategory.js.map +1 -1
  84. package/lib/cjs/TransformerPackageMetadata.d.ts +8 -0
  85. package/lib/cjs/TransformerPackageMetadata.d.ts.map +1 -0
  86. package/lib/cjs/TransformerPackageMetadata.js +47 -0
  87. package/lib/cjs/TransformerPackageMetadata.js.map +1 -0
  88. package/lib/cjs/imodel-transformer.d.ts +27 -0
  89. package/lib/cjs/imodel-transformer.d.ts.map +1 -1
  90. package/lib/cjs/imodel-transformer.js +61 -34
  91. package/lib/cjs/imodel-transformer.js.map +1 -1
  92. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts +29 -0
  93. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.d.ts.map +1 -0
  94. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js +212 -0
  95. package/lib/cjs/schema-processing/DynamicSchemaUnionStrategy.js.map +1 -0
  96. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts +25 -0
  97. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.d.ts.map +1 -0
  98. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js +196 -0
  99. package/lib/cjs/schema-processing/SchemaProcessingCoordinator.js.map +1 -0
  100. package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts +59 -0
  101. package/lib/cjs/schema-processing/SchemaProcessingStrategy.d.ts.map +1 -0
  102. package/lib/cjs/schema-processing/SchemaProcessingStrategy.js +28 -0
  103. package/lib/cjs/schema-processing/SchemaProcessingStrategy.js.map +1 -0
  104. package/lib/cjs/schema-processing.d.ts +7 -0
  105. package/lib/cjs/schema-processing.d.ts.map +1 -0
  106. package/lib/cjs/schema-processing.js +27 -0
  107. package/lib/cjs/schema-processing.js.map +1 -0
  108. package/package.json +70 -57
  109. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts +0 -16
  110. package/lib/cjs/DetachedExportElementAspectsStrategy.d.ts.map +0 -1
  111. package/lib/cjs/DetachedExportElementAspectsStrategy.js +0 -106
  112. package/lib/cjs/DetachedExportElementAspectsStrategy.js.map +0 -1
  113. package/lib/cjs/ElementCascadingDeleter.d.ts +0 -22
  114. package/lib/cjs/ElementCascadingDeleter.d.ts.map +0 -1
  115. package/lib/cjs/ElementCascadingDeleter.js +0 -57
  116. package/lib/cjs/ElementCascadingDeleter.js.map +0 -1
  117. package/lib/cjs/EntityMap.d.ts +0 -26
  118. package/lib/cjs/EntityMap.d.ts.map +0 -1
  119. package/lib/cjs/EntityMap.js +0 -54
  120. package/lib/cjs/EntityMap.js.map +0 -1
  121. package/lib/cjs/ExportElementAspectsStrategy.d.ts +0 -35
  122. package/lib/cjs/ExportElementAspectsStrategy.d.ts.map +0 -1
  123. package/lib/cjs/ExportElementAspectsStrategy.js +0 -52
  124. package/lib/cjs/ExportElementAspectsStrategy.js.map +0 -1
  125. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts +0 -12
  126. package/lib/cjs/ExportElementAspectsWithElementsStrategy.d.ts.map +0 -1
  127. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js +0 -47
  128. package/lib/cjs/ExportElementAspectsWithElementsStrategy.js.map +0 -1
@@ -0,0 +1,175 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
4
+ * See LICENSE.md in the project root for license terms and full copyright notice.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ElementAspectCleanup = void 0;
8
+ exports.isSameClass = isSameClass;
9
+ exports.tryGetAspect = tryGetAspect;
10
+ const core_backend_1 = require("@itwin/core-backend");
11
+ const core_bentley_1 = require("@itwin/core-bentley");
12
+ const core_common_1 = require("@itwin/core-common");
13
+ /** Deletes replaceable ElementAspects of target owners that an owner batch did not reuse, while preserving excluded classes and aspects the caller protects, such as transformer provenance.
14
+ *
15
+ * Usage per owner batch: [[collect]] the aspects the owners have before import, let the importer [[retain]] each aspect it reuses or deletes itself, then [[deleteUnretained]] to delete the rest, or [[discard]] the batch if its export failed.
16
+ * While a batch is active, [[getAspects]] answers target aspect reads for its owners from the aspects loaded by [[collect]], so the importer doesn't query each owner separately.
17
+ * The importer brackets its own aspect writes with [[beforeWrite]] and [[afterWrite]]. If anything else writes to the target during the batch, including direct `EditTxn` calls, the loaded aspects are dropped and reads go to the target for the rest of the batch.
18
+ * @internal
19
+ */
20
+ class ElementAspectCleanup {
21
+ _targetDb;
22
+ _editTxn;
23
+ _deleteAspect;
24
+ /** IDs of the batch owners' replaceable target aspects that the importer has not yet reused or deleted. [[collect]] fills it, [[retain]] removes from it, and [[deleteUnretained]] deletes what is left. */
25
+ _candidateIds = new Set();
26
+ /** Every target aspect of each batch owner that has not been written since [[collect]], keyed by owner. */
27
+ _aspectsByOwner = new Map();
28
+ /** The target connection's change count after [[collect]] or the importer's last write; undefined when no aspects are loaded. */
29
+ _expectedChangeCount;
30
+ constructor(_targetDb, _editTxn, _deleteAspect) {
31
+ this._targetDb = _targetDb;
32
+ this._editTxn = _editTxn;
33
+ this._deleteAspect = _deleteAspect;
34
+ }
35
+ /** Records the replaceable unique and multi-aspects owned by the supplied target elements as deletion candidates, replacing any previous candidates.
36
+ * Aspects of excluded classes, and aspects for which `isProtected` returns true, are never candidates.
37
+ */
38
+ async collect(targetElementIds, excludedElementAspectClassFullNames, isProtected = () => false) {
39
+ // Build the batch locally so a failed query leaves no partial batch behind.
40
+ this.discard();
41
+ if (targetElementIds.size === 0)
42
+ return;
43
+ const candidateIds = new Set();
44
+ const aspectsByOwner = new Map();
45
+ const targetExcludedElementAspectClassFullNames = [
46
+ ...excludedElementAspectClassFullNames,
47
+ ].filter((classFullName) => this._targetDb.containsClass(classFullName));
48
+ for (const aspectClassFullName of [
49
+ core_backend_1.ElementUniqueAspect.classFullName,
50
+ core_backend_1.ElementMultiAspect.classFullName,
51
+ ]) {
52
+ const { ecsql, params } = replaceableAspectQuery(aspectClassFullName, targetElementIds, targetExcludedElementAspectClassFullNames);
53
+ for await (const row of this._targetDb.createQueryReader(ecsql, params, {
54
+ usePrimaryConn: true,
55
+ })) {
56
+ candidateIds.add(row.id);
57
+ }
58
+ }
59
+ // Load every aspect, including excluded classes and protected aspects, so
60
+ // reads answer exactly what getAspects would for these owners.
61
+ for (const elementId of targetElementIds)
62
+ aspectsByOwner.set(elementId, []);
63
+ for await (const aspect of this._targetDb.elements.queryAspects({
64
+ elementIds: [...targetElementIds],
65
+ groupByOwner: true,
66
+ usePrimaryConn: true,
67
+ })) {
68
+ aspectsByOwner.get(aspect.element.id)?.push(aspect);
69
+ if (isProtected(aspect))
70
+ candidateIds.delete(aspect.id);
71
+ }
72
+ this._candidateIds = candidateIds;
73
+ this._aspectsByOwner = aspectsByOwner;
74
+ this._expectedChangeCount = this.targetChangeCount();
75
+ }
76
+ /** Returns the owner's target aspects of exactly `classFullName`, or all of them when it is omitted, in ECInstanceId order, from the aspects loaded by [[collect]].
77
+ * Returns undefined when the owner is not in the active batch or has been written since [[collect]]; the caller must then read the target.
78
+ */
79
+ getAspects(elementId, classFullName) {
80
+ this.dropLoadedAspectsIfChangedElsewhere();
81
+ const aspects = this._aspectsByOwner.get(elementId);
82
+ if (classFullName === undefined)
83
+ return aspects;
84
+ return aspects?.filter((aspect) => isSameClass(aspect, classFullName));
85
+ }
86
+ /** Call right before the importer writes an aspect of `elementId`. Later reads for that owner go to the target. */
87
+ beforeWrite(elementId) {
88
+ this.dropLoadedAspectsIfChangedElsewhere();
89
+ this._aspectsByOwner.delete(elementId);
90
+ }
91
+ /** Call right after the importer's write so that it isn't mistaken for a write made elsewhere. */
92
+ afterWrite() {
93
+ if (this._expectedChangeCount !== undefined)
94
+ this._expectedChangeCount = this.targetChangeCount();
95
+ }
96
+ /** Discards the batch without deleting anything. */
97
+ discard() {
98
+ this._candidateIds = new Set();
99
+ this._aspectsByOwner = new Map();
100
+ this._expectedChangeCount = undefined;
101
+ }
102
+ /** Drops all loaded aspects if the target changed since [[collect]] or the importer's last write, which means code other than the importer wrote to it. */
103
+ dropLoadedAspectsIfChangedElsewhere() {
104
+ if (this._expectedChangeCount === undefined ||
105
+ this.targetChangeCount() === this._expectedChangeCount)
106
+ return;
107
+ this._aspectsByOwner = new Map();
108
+ this._expectedChangeCount = undefined;
109
+ }
110
+ /** SQLite's count of rows inserted, updated, or deleted on the target connection since it opened. Reads don't change it. */
111
+ targetChangeCount() {
112
+ return this._targetDb.withPreparedSqliteStatement("SELECT total_changes()", (statement) => {
113
+ statement.step();
114
+ return statement.getValue(0).getInteger();
115
+ });
116
+ }
117
+ /** Removes an aspect from the deletion candidates because the importer reused or already deleted it. */
118
+ retain(aspectId) {
119
+ this._candidateIds.delete(aspectId);
120
+ }
121
+ /** Deletes the remaining candidates through the configured callback and clears them.
122
+ * Requires the target `EditTxn` to be active when there is anything to delete.
123
+ */
124
+ async deleteUnretained() {
125
+ const candidateIds = this._candidateIds;
126
+ this.discard();
127
+ if (candidateIds.size === 0)
128
+ return;
129
+ if (!this._editTxn.isActive) {
130
+ throw new Error("The target EditTxn must be active when deleting ElementAspects.");
131
+ }
132
+ for (const aspectId of candidateIds) {
133
+ // Deleting a unique aspect also deletes unique aspects of derived
134
+ // classes on the same element, so a candidate may already be gone.
135
+ const aspect = tryGetAspect(this._targetDb, aspectId);
136
+ if (aspect !== undefined)
137
+ await this._deleteAspect(aspect);
138
+ }
139
+ }
140
+ }
141
+ exports.ElementAspectCleanup = ElementAspectCleanup;
142
+ /** Builds the query for aspects of `aspectClassFullName` owned by `elementIds`, skipping excluded classes. */
143
+ function replaceableAspectQuery(aspectClassFullName, elementIds, excludedClassFullNames) {
144
+ const params = new core_common_1.QueryBinder().bindIdSet("elementIds", elementIds);
145
+ const whereClause = excludedClassFullNames.length > 0
146
+ ? `WHERE aspect.ECClassId IS NOT (${excludedClassFullNames.join(", ")})`
147
+ : "";
148
+ const ecsql = `SELECT aspect.ECInstanceId as id
149
+ FROM ${aspectClassFullName} aspect
150
+ INNER JOIN IdSet(:elementIds) ids ON ids.id = aspect.Element.Id
151
+ ${whereClause}`;
152
+ return { ecsql, params };
153
+ }
154
+ /** Whether an aspect is exactly of class `classFullName`, which may use either `:` or `.` as the separator.
155
+ * @internal
156
+ */
157
+ function isSameClass(aspect, classFullName) {
158
+ return (aspect.classFullName.toLowerCase() ===
159
+ classFullName.replace(".", ":").toLowerCase());
160
+ }
161
+ /** Returns the aspect, or undefined if it no longer exists.
162
+ * @internal
163
+ */
164
+ function tryGetAspect(db, aspectId) {
165
+ try {
166
+ return db.elements.getAspect(aspectId);
167
+ }
168
+ catch (error) {
169
+ if (error instanceof core_common_1.IModelError &&
170
+ error.errorNumber === core_bentley_1.IModelStatus.NotFound)
171
+ return undefined;
172
+ throw error;
173
+ }
174
+ }
175
+ //# sourceMappingURL=ElementAspectCleanup.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElementAspectCleanup.js","sourceRoot":"","sources":["../../src/ElementAspectCleanup.ts"],"names":[],"mappings":"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","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n * See LICENSE.md in the project root for license terms and full copyright notice.\n *--------------------------------------------------------------------------------------------*/\n\nimport {\n EditTxn,\n ElementAspect,\n ElementMultiAspect,\n ElementUniqueAspect,\n IModelDb,\n} from \"@itwin/core-backend\";\nimport { Id64String, IModelStatus } from \"@itwin/core-bentley\";\nimport { IModelError, QueryBinder } from \"@itwin/core-common\";\n\n/** Deletes replaceable ElementAspects of target owners that an owner batch did not reuse, while preserving excluded classes and aspects the caller protects, such as transformer provenance.\n *\n * Usage per owner batch: [[collect]] the aspects the owners have before import, let the importer [[retain]] each aspect it reuses or deletes itself, then [[deleteUnretained]] to delete the rest, or [[discard]] the batch if its export failed.\n * While a batch is active, [[getAspects]] answers target aspect reads for its owners from the aspects loaded by [[collect]], so the importer doesn't query each owner separately.\n * The importer brackets its own aspect writes with [[beforeWrite]] and [[afterWrite]]. If anything else writes to the target during the batch, including direct `EditTxn` calls, the loaded aspects are dropped and reads go to the target for the rest of the batch.\n * @internal\n */\nexport class ElementAspectCleanup {\n /** IDs of the batch owners' replaceable target aspects that the importer has not yet reused or deleted. [[collect]] fills it, [[retain]] removes from it, and [[deleteUnretained]] deletes what is left. */\n private _candidateIds = new Set<Id64String>();\n /** Every target aspect of each batch owner that has not been written since [[collect]], keyed by owner. */\n private _aspectsByOwner = new Map<Id64String, ElementAspect[]>();\n /** The target connection's change count after [[collect]] or the importer's last write; undefined when no aspects are loaded. */\n private _expectedChangeCount?: number;\n\n public constructor(\n private readonly _targetDb: IModelDb,\n private readonly _editTxn: EditTxn,\n private readonly _deleteAspect: (aspect: ElementAspect) => Promise<void>\n ) {}\n\n /** Records the replaceable unique and multi-aspects owned by the supplied target elements as deletion candidates, replacing any previous candidates.\n * Aspects of excluded classes, and aspects for which `isProtected` returns true, are never candidates.\n */\n public async collect(\n targetElementIds: ReadonlySet<Id64String>,\n excludedElementAspectClassFullNames: ReadonlySet<string>,\n isProtected: (aspect: ElementAspect) => boolean = () => false\n ): Promise<void> {\n // Build the batch locally so a failed query leaves no partial batch behind.\n this.discard();\n if (targetElementIds.size === 0) return;\n\n const candidateIds = new Set<Id64String>();\n const aspectsByOwner = new Map<Id64String, ElementAspect[]>();\n\n const targetExcludedElementAspectClassFullNames = [\n ...excludedElementAspectClassFullNames,\n ].filter((classFullName) => this._targetDb.containsClass(classFullName));\n for (const aspectClassFullName of [\n ElementUniqueAspect.classFullName,\n ElementMultiAspect.classFullName,\n ]) {\n const { ecsql, params } = replaceableAspectQuery(\n aspectClassFullName,\n targetElementIds,\n targetExcludedElementAspectClassFullNames\n );\n for await (const row of this._targetDb.createQueryReader(ecsql, params, {\n usePrimaryConn: true,\n })) {\n candidateIds.add(row.id);\n }\n }\n\n // Load every aspect, including excluded classes and protected aspects, so\n // reads answer exactly what getAspects would for these owners.\n for (const elementId of targetElementIds) aspectsByOwner.set(elementId, []);\n for await (const aspect of this._targetDb.elements.queryAspects({\n elementIds: [...targetElementIds],\n groupByOwner: true,\n usePrimaryConn: true,\n })) {\n aspectsByOwner.get(aspect.element.id)?.push(aspect);\n if (isProtected(aspect)) candidateIds.delete(aspect.id);\n }\n\n this._candidateIds = candidateIds;\n this._aspectsByOwner = aspectsByOwner;\n this._expectedChangeCount = this.targetChangeCount();\n }\n\n /** Returns the owner's target aspects of exactly `classFullName`, or all of them when it is omitted, in ECInstanceId order, from the aspects loaded by [[collect]].\n * Returns undefined when the owner is not in the active batch or has been written since [[collect]]; the caller must then read the target.\n */\n public getAspects(\n elementId: Id64String,\n classFullName?: string\n ): ElementAspect[] | undefined {\n this.dropLoadedAspectsIfChangedElsewhere();\n const aspects = this._aspectsByOwner.get(elementId);\n if (classFullName === undefined) return aspects;\n return aspects?.filter((aspect) => isSameClass(aspect, classFullName));\n }\n\n /** Call right before the importer writes an aspect of `elementId`. Later reads for that owner go to the target. */\n public beforeWrite(elementId: Id64String): void {\n this.dropLoadedAspectsIfChangedElsewhere();\n this._aspectsByOwner.delete(elementId);\n }\n\n /** Call right after the importer's write so that it isn't mistaken for a write made elsewhere. */\n public afterWrite(): void {\n if (this._expectedChangeCount !== undefined)\n this._expectedChangeCount = this.targetChangeCount();\n }\n\n /** Discards the batch without deleting anything. */\n public discard(): void {\n this._candidateIds = new Set<Id64String>();\n this._aspectsByOwner = new Map<Id64String, ElementAspect[]>();\n this._expectedChangeCount = undefined;\n }\n\n /** Drops all loaded aspects if the target changed since [[collect]] or the importer's last write, which means code other than the importer wrote to it. */\n private dropLoadedAspectsIfChangedElsewhere(): void {\n if (\n this._expectedChangeCount === undefined ||\n this.targetChangeCount() === this._expectedChangeCount\n )\n return;\n this._aspectsByOwner = new Map<Id64String, ElementAspect[]>();\n this._expectedChangeCount = undefined;\n }\n\n /** SQLite's count of rows inserted, updated, or deleted on the target connection since it opened. Reads don't change it. */\n private targetChangeCount(): number {\n return this._targetDb.withPreparedSqliteStatement(\n \"SELECT total_changes()\",\n (statement) => {\n statement.step();\n return statement.getValue(0).getInteger();\n }\n );\n }\n\n /** Removes an aspect from the deletion candidates because the importer reused or already deleted it. */\n public retain(aspectId: Id64String): void {\n this._candidateIds.delete(aspectId);\n }\n\n /** Deletes the remaining candidates through the configured callback and clears them.\n * Requires the target `EditTxn` to be active when there is anything to delete.\n */\n public async deleteUnretained(): Promise<void> {\n const candidateIds = this._candidateIds;\n this.discard();\n if (candidateIds.size === 0) return;\n\n if (!this._editTxn.isActive) {\n throw new Error(\n \"The target EditTxn must be active when deleting ElementAspects.\"\n );\n }\n for (const aspectId of candidateIds) {\n // Deleting a unique aspect also deletes unique aspects of derived\n // classes on the same element, so a candidate may already be gone.\n const aspect = tryGetAspect(this._targetDb, aspectId);\n if (aspect !== undefined) await this._deleteAspect(aspect);\n }\n }\n}\n\n/** Builds the query for aspects of `aspectClassFullName` owned by `elementIds`, skipping excluded classes. */\nfunction replaceableAspectQuery(\n aspectClassFullName: string,\n elementIds: ReadonlySet<Id64String>,\n excludedClassFullNames: readonly string[]\n): { ecsql: string; params: QueryBinder } {\n const params = new QueryBinder().bindIdSet(\"elementIds\", elementIds);\n const whereClause =\n excludedClassFullNames.length > 0\n ? `WHERE aspect.ECClassId IS NOT (${excludedClassFullNames.join(\", \")})`\n : \"\";\n const ecsql = `SELECT aspect.ECInstanceId as id\n FROM ${aspectClassFullName} aspect\n INNER JOIN IdSet(:elementIds) ids ON ids.id = aspect.Element.Id\n ${whereClause}`;\n return { ecsql, params };\n}\n\n/** Whether an aspect is exactly of class `classFullName`, which may use either `:` or `.` as the separator.\n * @internal\n */\nexport function isSameClass(\n aspect: { readonly classFullName: string },\n classFullName: string\n): boolean {\n return (\n aspect.classFullName.toLowerCase() ===\n classFullName.replace(\".\", \":\").toLowerCase()\n );\n}\n\n/** Returns the aspect, or undefined if it no longer exists.\n * @internal\n */\nexport function tryGetAspect(\n db: IModelDb,\n aspectId: Id64String\n): ElementAspect | undefined {\n try {\n return db.elements.getAspect(aspectId);\n } catch (error) {\n if (\n error instanceof IModelError &&\n error.errorNumber === IModelStatus.NotFound\n )\n return undefined;\n throw error;\n }\n}\n"]}
@@ -0,0 +1,47 @@
1
+ import { Id64String } from "@itwin/core-bentley";
2
+ /** Completes an owner group. `exported` is false when exporting the group's aspects failed; the callback must then discard its state without throwing. */
3
+ export type ElementAspectExportCompletion = (exported: boolean) => Promise<void>;
4
+ /** Prepares an owner group before its aspects are exported. It may return a completion callback that runs after the group's aspects are exported, or after the export fails. */
5
+ export type ElementAspectExportPreparation = (excludedElementAspectClassFullNames: ReadonlySet<string>, ownerElementIds: ReadonlySet<Id64String>) => Promise<ElementAspectExportCompletion | void>;
6
+ /** Coordinates scoped batches of accepted ElementAspect owners, including preparation before aspect export.
7
+ * @internal
8
+ */
9
+ export declare class ElementAspectExportCoordinator {
10
+ private readonly _defaultBatchSize;
11
+ private readonly _excludedClassFullNames;
12
+ private readonly _exportAspects;
13
+ private readonly _resetCaches;
14
+ private readonly _acceptedOwnerIds;
15
+ private readonly _processedOwnerIds;
16
+ private _batchSize;
17
+ private _depth;
18
+ private _prepare?;
19
+ constructor(_defaultBatchSize: number, _excludedClassFullNames: () => ReadonlySet<string>, _exportAspects: (ownerElementIds: ReadonlySet<Id64String>) => Promise<void>, _resetCaches?: () => void);
20
+ /** Whether an accepted-owner collection scope is active. */
21
+ get isActive(): boolean;
22
+ /** Sets the callback that prepares each accepted-owner group before its aspects are exported and optionally completes it afterward. */
23
+ setPreparation(prepare: ElementAspectExportPreparation): void;
24
+ /** Begins an accepted-owner collection scope.
25
+ * Nested scopes share the outer scope's state and batch size.
26
+ */
27
+ begin(batchSize?: number): void;
28
+ /** Ends an accepted-owner collection scope.
29
+ * Ending the outermost scope exports pending owners and resets all scope state.
30
+ */
31
+ end(): Promise<void>;
32
+ /** Discards the active scope and its pending owner state without exporting it. */
33
+ abort(): void;
34
+ /** Runs element export within an accepted-owner collection scope.
35
+ * A scope created by this call is aborted if element export fails; an existing outer scope remains responsible for its own failure handling.
36
+ */
37
+ run(exportElements: () => Promise<void>): Promise<void>;
38
+ /** Adds an accepted source owner to the current group and exports the group when it reaches the configured batch size. */
39
+ addAcceptedOwner(elementId: Id64String): Promise<void>;
40
+ /** Runs preparation and aspect export for an explicit owner set in bounded groups. */
41
+ exportOwners(ownerElementIds: ReadonlySet<Id64String>): Promise<void>;
42
+ private exportOwnerBatch;
43
+ private flush;
44
+ private resetScopeState;
45
+ private reset;
46
+ }
47
+ //# sourceMappingURL=ElementAspectExportCoordinator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElementAspectExportCoordinator.d.ts","sourceRoot":"","sources":["../../src/ElementAspectExportCoordinator.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAEjD,0JAA0J;AAC1J,MAAM,MAAM,6BAA6B,GAAG,CAC1C,QAAQ,EAAE,OAAO,KACd,OAAO,CAAC,IAAI,CAAC,CAAC;AAEnB,gLAAgL;AAChL,MAAM,MAAM,8BAA8B,GAAG,CAC3C,mCAAmC,EAAE,WAAW,CAAC,MAAM,CAAC,EACxD,eAAe,EAAE,WAAW,CAAC,UAAU,CAAC,KACrC,OAAO,CAAC,6BAA6B,GAAG,IAAI,CAAC,CAAC;AAEnD;;GAEG;AACH,qBAAa,8BAA8B;IAQvC,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAClC,OAAO,CAAC,QAAQ,CAAC,uBAAuB;IACxC,OAAO,CAAC,QAAQ,CAAC,cAAc;IAG/B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAZ/B,OAAO,CAAC,QAAQ,CAAC,iBAAiB,CAAyB;IAC3D,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAyB;IAC5D,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,MAAM,CAAK;IACnB,OAAO,CAAC,QAAQ,CAAC,CAAiC;IAElD,YACmB,iBAAiB,EAAE,MAAM,EACzB,uBAAuB,EAAE,MAAM,WAAW,CAAC,MAAM,CAAC,EAClD,cAAc,EAAE,CAC/B,eAAe,EAAE,WAAW,CAAC,UAAU,CAAC,KACrC,OAAO,CAAC,IAAI,CAAC,EACD,YAAY,GAAE,MAAM,IAAe,EAGrD;IAED,4DAA4D;IAC5D,IAAW,QAAQ,IAAI,OAAO,CAE7B;IAED,uIAAuI;IAChI,cAAc,CAAC,OAAO,EAAE,8BAA8B,GAAG,IAAI,CAEnE;IAED;;OAEG;IACI,KAAK,CAAC,SAAS,SAAyB,GAAG,IAAI,CAOrD;IAED;;OAEG;IACU,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAehC;IAED,kFAAkF;IAC3E,KAAK,IAAI,IAAI,CAEnB;IAED;;OAEG;IACU,GAAG,CAAC,cAAc,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAcnE;IAED,0HAA0H;IAC7G,gBAAgB,CAAC,SAAS,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAOlE;IAED,sFAAsF;IACzE,YAAY,CACvB,eAAe,EAAE,WAAW,CAAC,UAAU,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CA8Bf;YAEa,gBAAgB;YAoBhB,KAAK;IAMnB,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,KAAK;CAKd"}
@@ -0,0 +1,160 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
4
+ * See LICENSE.md in the project root for license terms and full copyright notice.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ElementAspectExportCoordinator = void 0;
8
+ /** Coordinates scoped batches of accepted ElementAspect owners, including preparation before aspect export.
9
+ * @internal
10
+ */
11
+ class ElementAspectExportCoordinator {
12
+ _defaultBatchSize;
13
+ _excludedClassFullNames;
14
+ _exportAspects;
15
+ _resetCaches;
16
+ _acceptedOwnerIds = new Set();
17
+ _processedOwnerIds = new Set();
18
+ _batchSize;
19
+ _depth = 0;
20
+ _prepare;
21
+ constructor(_defaultBatchSize, _excludedClassFullNames, _exportAspects, _resetCaches = () => { }) {
22
+ this._defaultBatchSize = _defaultBatchSize;
23
+ this._excludedClassFullNames = _excludedClassFullNames;
24
+ this._exportAspects = _exportAspects;
25
+ this._resetCaches = _resetCaches;
26
+ this._batchSize = _defaultBatchSize;
27
+ }
28
+ /** Whether an accepted-owner collection scope is active. */
29
+ get isActive() {
30
+ return this._depth > 0;
31
+ }
32
+ /** Sets the callback that prepares each accepted-owner group before its aspects are exported and optionally completes it afterward. */
33
+ setPreparation(prepare) {
34
+ this._prepare = prepare;
35
+ }
36
+ /** Begins an accepted-owner collection scope.
37
+ * Nested scopes share the outer scope's state and batch size.
38
+ */
39
+ begin(batchSize = this._defaultBatchSize) {
40
+ if (this._depth === 0) {
41
+ this.resetScopeState();
42
+ this._resetCaches();
43
+ this._batchSize = batchSize;
44
+ }
45
+ this._depth++;
46
+ }
47
+ /** Ends an accepted-owner collection scope.
48
+ * Ending the outermost scope exports pending owners and resets all scope state.
49
+ */
50
+ async end() {
51
+ if (this._depth === 0) {
52
+ throw new Error("No scoped element export is active.");
53
+ }
54
+ this._depth--;
55
+ if (this._depth > 0)
56
+ return;
57
+ try {
58
+ if (this._acceptedOwnerIds.size > 0) {
59
+ await this.flush();
60
+ }
61
+ }
62
+ finally {
63
+ this.reset();
64
+ }
65
+ }
66
+ /** Discards the active scope and its pending owner state without exporting it. */
67
+ abort() {
68
+ this.reset();
69
+ }
70
+ /** Runs element export within an accepted-owner collection scope.
71
+ * A scope created by this call is aborted if element export fails; an existing outer scope remains responsible for its own failure handling.
72
+ */
73
+ async run(exportElements) {
74
+ if (this.isActive) {
75
+ await exportElements();
76
+ return;
77
+ }
78
+ this.begin();
79
+ try {
80
+ await exportElements();
81
+ }
82
+ catch (error) {
83
+ this.abort();
84
+ throw error;
85
+ }
86
+ await this.end();
87
+ }
88
+ /** Adds an accepted source owner to the current group and exports the group when it reaches the configured batch size. */
89
+ async addAcceptedOwner(elementId) {
90
+ if (!this.isActive || this._processedOwnerIds.has(elementId))
91
+ return;
92
+ this._acceptedOwnerIds.add(elementId);
93
+ if (this._acceptedOwnerIds.size >= this._batchSize) {
94
+ await this.flush();
95
+ }
96
+ }
97
+ /** Runs preparation and aspect export for an explicit owner set in bounded groups. */
98
+ async exportOwners(ownerElementIds) {
99
+ let ownerIds = ownerElementIds;
100
+ if (this.isActive && ownerElementIds.size > 0) {
101
+ const unprocessedOwnerIds = new Set();
102
+ for (const elementId of ownerElementIds) {
103
+ if (!this._processedOwnerIds.has(elementId)) {
104
+ unprocessedOwnerIds.add(elementId);
105
+ this._processedOwnerIds.add(elementId);
106
+ }
107
+ }
108
+ if (unprocessedOwnerIds.size === 0)
109
+ return;
110
+ ownerIds = unprocessedOwnerIds;
111
+ }
112
+ if (ownerIds.size <= this._defaultBatchSize) {
113
+ await this.exportOwnerBatch(ownerIds);
114
+ return;
115
+ }
116
+ let ownerBatch = new Set();
117
+ for (const elementId of ownerIds) {
118
+ ownerBatch.add(elementId);
119
+ if (ownerBatch.size === this._defaultBatchSize) {
120
+ await this.exportOwnerBatch(ownerBatch);
121
+ ownerBatch = new Set();
122
+ }
123
+ }
124
+ if (ownerBatch.size > 0) {
125
+ await this.exportOwnerBatch(ownerBatch);
126
+ }
127
+ }
128
+ async exportOwnerBatch(ownerElementIds) {
129
+ const complete = await this._prepare?.(this._excludedClassFullNames(), ownerElementIds);
130
+ try {
131
+ await this._exportAspects(ownerElementIds);
132
+ }
133
+ catch (error) {
134
+ try {
135
+ await complete?.(false);
136
+ }
137
+ catch {
138
+ // Keep the export error; it explains why the group failed.
139
+ }
140
+ throw error;
141
+ }
142
+ await complete?.(true);
143
+ }
144
+ async flush() {
145
+ const ownerElementIds = new Set(this._acceptedOwnerIds);
146
+ this._acceptedOwnerIds.clear();
147
+ await this.exportOwners(ownerElementIds);
148
+ }
149
+ resetScopeState() {
150
+ this._acceptedOwnerIds.clear();
151
+ this._processedOwnerIds.clear();
152
+ }
153
+ reset() {
154
+ this._depth = 0;
155
+ this.resetScopeState();
156
+ this._batchSize = this._defaultBatchSize;
157
+ }
158
+ }
159
+ exports.ElementAspectExportCoordinator = ElementAspectExportCoordinator;
160
+ //# sourceMappingURL=ElementAspectExportCoordinator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElementAspectExportCoordinator.js","sourceRoot":"","sources":["../../src/ElementAspectExportCoordinator.ts"],"names":[],"mappings":";AAAA;;;gGAGgG;;;AAehG;;GAEG;AACH;IAQqB,iBAAiB;IACjB,uBAAuB;IACvB,cAAc;IAGd,YAAY;IAZd,iBAAiB,GAAG,IAAI,GAAG,EAAc,CAAC;IAC1C,kBAAkB,GAAG,IAAI,GAAG,EAAc,CAAC;IACpD,UAAU,CAAS;IACnB,MAAM,GAAG,CAAC,CAAC;IACX,QAAQ,CAAkC;IAElD,YACmB,iBAAyB,EACzB,uBAAkD,EAClD,cAEC,EACD,YAAY,GAAe,GAAG,EAAE,GAAE,CAAC;iCALnC,iBAAiB;uCACjB,uBAAuB;8BACvB,cAAc;4BAGd,YAAY;QAE7B,IAAI,CAAC,UAAU,GAAG,iBAAiB,CAAC;IACtC,CAAC;IAED,4DAA4D;IAC5D,IAAW,QAAQ;QACjB,OAAO,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;IACzB,CAAC;IAED,uIAAuI;IAChI,cAAc,CAAC,OAAuC;QAC3D,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,SAAS,GAAG,IAAI,CAAC,iBAAiB;QAC7C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,IAAI,CAAC,eAAe,EAAE,CAAC;YACvB,IAAI,CAAC,YAAY,EAAE,CAAC;YACpB,IAAI,CAAC,UAAU,GAAG,SAAS,CAAC;QAC9B,CAAC;QACD,IAAI,CAAC,MAAM,EAAE,CAAC;IAChB,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,GAAG;QACd,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACtB,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;QACzD,CAAC;QAED,IAAI,CAAC,MAAM,EAAE,CAAC;QACd,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC;YAAE,OAAO;QAE5B,IAAI,CAAC;YACH,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;gBACpC,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;YACrB,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,KAAK,EAAE,CAAC;QACf,CAAC;IACH,CAAC;IAED,kFAAkF;IAC3E,KAAK;QACV,IAAI,CAAC,KAAK,EAAE,CAAC;IACf,CAAC;IAED;;OAEG;IACI,KAAK,CAAC,GAAG,CAAC,cAAmC;QAClD,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;YAClB,MAAM,cAAc,EAAE,CAAC;YACvB,OAAO;QACT,CAAC;QAED,IAAI,CAAC,KAAK,EAAE,CAAC;QACb,IAAI,CAAC;YACH,MAAM,cAAc,EAAE,CAAC;QACzB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,KAAK,EAAE,CAAC;YACb,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,IAAI,CAAC,GAAG,EAAE,CAAC;IACnB,CAAC;IAED,0HAA0H;IACnH,KAAK,CAAC,gBAAgB,CAAC,SAAqB;QACjD,IAAI,CAAC,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC;YAAE,OAAO;QAErE,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACtC,IAAI,IAAI,CAAC,iBAAiB,CAAC,IAAI,IAAI,IAAI,CAAC,UAAU,EAAE,CAAC;YACnD,MAAM,IAAI,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC;IACH,CAAC;IAED,sFAAsF;IAC/E,KAAK,CAAC,YAAY,CACvB,eAAwC;QAExC,IAAI,QAAQ,GAAG,eAAe,CAAC;QAC/B,IAAI,IAAI,CAAC,QAAQ,IAAI,eAAe,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YAC9C,MAAM,mBAAmB,GAAG,IAAI,GAAG,EAAc,CAAC;YAClD,KAAK,MAAM,SAAS,IAAI,eAAe,EAAE,CAAC;gBACxC,IAAI,CAAC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;oBAC5C,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;oBACnC,IAAI,CAAC,kBAAkB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;gBACzC,CAAC;YACH,CAAC;YACD,IAAI,mBAAmB,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO;YAC3C,QAAQ,GAAG,mBAAmB,CAAC;QACjC,CAAC;QAED,IAAI,QAAQ,CAAC,IAAI,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC5C,MAAM,IAAI,CAAC,gBAAgB,CAAC,QAAQ,CAAC,CAAC;YACtC,OAAO;QACT,CAAC;QAED,IAAI,UAAU,GAAG,IAAI,GAAG,EAAc,CAAC;QACvC,KAAK,MAAM,SAAS,IAAI,QAAQ,EAAE,CAAC;YACjC,UAAU,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;YAC1B,IAAI,UAAU,CAAC,IAAI,KAAK,IAAI,CAAC,iBAAiB,EAAE,CAAC;gBAC/C,MAAM,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;gBACxC,UAAU,GAAG,IAAI,GAAG,EAAc,CAAC;YACrC,CAAC;QACH,CAAC;QACD,IAAI,UAAU,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;YACxB,MAAM,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;QAC1C,CAAC;IACH,CAAC;IAEO,KAAK,CAAC,gBAAgB,CAC5B,eAAwC;QAExC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,QAAQ,EAAE,CACpC,IAAI,CAAC,uBAAuB,EAAE,EAC9B,eAAe,CAChB,CAAC;QACF,IAAI,CAAC;YACH,MAAM,IAAI,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;QAC7C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC;gBACH,MAAM,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC;YAC1B,CAAC;YAAC,MAAM,CAAC;gBACP,2DAA2D;YAC7D,CAAC;YACD,MAAM,KAAK,CAAC;QACd,CAAC;QACD,MAAM,QAAQ,EAAE,CAAC,IAAI,CAAC,CAAC;IACzB,CAAC;IAEO,KAAK,CAAC,KAAK;QACjB,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,IAAI,CAAC,iBAAiB,CAAC,CAAC;QACxD,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC/B,MAAM,IAAI,CAAC,YAAY,CAAC,eAAe,CAAC,CAAC;IAC3C,CAAC;IAEO,eAAe;QACrB,IAAI,CAAC,iBAAiB,CAAC,KAAK,EAAE,CAAC;QAC/B,IAAI,CAAC,kBAAkB,CAAC,KAAK,EAAE,CAAC;IAClC,CAAC;IAEO,KAAK;QACX,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;QAChB,IAAI,CAAC,eAAe,EAAE,CAAC;QACvB,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,iBAAiB,CAAC;IAC3C,CAAC;CACF","sourcesContent":["/*---------------------------------------------------------------------------------------------\n * Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n * See LICENSE.md in the project root for license terms and full copyright notice.\n *--------------------------------------------------------------------------------------------*/\n\nimport { Id64String } from \"@itwin/core-bentley\";\n\n/** Completes an owner group. `exported` is false when exporting the group's aspects failed; the callback must then discard its state without throwing. */\nexport type ElementAspectExportCompletion = (\n exported: boolean\n) => Promise<void>;\n\n/** Prepares an owner group before its aspects are exported. It may return a completion callback that runs after the group's aspects are exported, or after the export fails. */\nexport type ElementAspectExportPreparation = (\n excludedElementAspectClassFullNames: ReadonlySet<string>,\n ownerElementIds: ReadonlySet<Id64String>\n) => Promise<ElementAspectExportCompletion | void>;\n\n/** Coordinates scoped batches of accepted ElementAspect owners, including preparation before aspect export.\n * @internal\n */\nexport class ElementAspectExportCoordinator {\n private readonly _acceptedOwnerIds = new Set<Id64String>();\n private readonly _processedOwnerIds = new Set<Id64String>();\n private _batchSize: number;\n private _depth = 0;\n private _prepare?: ElementAspectExportPreparation;\n\n public constructor(\n private readonly _defaultBatchSize: number,\n private readonly _excludedClassFullNames: () => ReadonlySet<string>,\n private readonly _exportAspects: (\n ownerElementIds: ReadonlySet<Id64String>\n ) => Promise<void>,\n private readonly _resetCaches: () => void = () => {}\n ) {\n this._batchSize = _defaultBatchSize;\n }\n\n /** Whether an accepted-owner collection scope is active. */\n public get isActive(): boolean {\n return this._depth > 0;\n }\n\n /** Sets the callback that prepares each accepted-owner group before its aspects are exported and optionally completes it afterward. */\n public setPreparation(prepare: ElementAspectExportPreparation): void {\n this._prepare = prepare;\n }\n\n /** Begins an accepted-owner collection scope.\n * Nested scopes share the outer scope's state and batch size.\n */\n public begin(batchSize = this._defaultBatchSize): void {\n if (this._depth === 0) {\n this.resetScopeState();\n this._resetCaches();\n this._batchSize = batchSize;\n }\n this._depth++;\n }\n\n /** Ends an accepted-owner collection scope.\n * Ending the outermost scope exports pending owners and resets all scope state.\n */\n public async end(): Promise<void> {\n if (this._depth === 0) {\n throw new Error(\"No scoped element export is active.\");\n }\n\n this._depth--;\n if (this._depth > 0) return;\n\n try {\n if (this._acceptedOwnerIds.size > 0) {\n await this.flush();\n }\n } finally {\n this.reset();\n }\n }\n\n /** Discards the active scope and its pending owner state without exporting it. */\n public abort(): void {\n this.reset();\n }\n\n /** Runs element export within an accepted-owner collection scope.\n * A scope created by this call is aborted if element export fails; an existing outer scope remains responsible for its own failure handling.\n */\n public async run(exportElements: () => Promise<void>): Promise<void> {\n if (this.isActive) {\n await exportElements();\n return;\n }\n\n this.begin();\n try {\n await exportElements();\n } catch (error) {\n this.abort();\n throw error;\n }\n await this.end();\n }\n\n /** Adds an accepted source owner to the current group and exports the group when it reaches the configured batch size. */\n public async addAcceptedOwner(elementId: Id64String): Promise<void> {\n if (!this.isActive || this._processedOwnerIds.has(elementId)) return;\n\n this._acceptedOwnerIds.add(elementId);\n if (this._acceptedOwnerIds.size >= this._batchSize) {\n await this.flush();\n }\n }\n\n /** Runs preparation and aspect export for an explicit owner set in bounded groups. */\n public async exportOwners(\n ownerElementIds: ReadonlySet<Id64String>\n ): Promise<void> {\n let ownerIds = ownerElementIds;\n if (this.isActive && ownerElementIds.size > 0) {\n const unprocessedOwnerIds = new Set<Id64String>();\n for (const elementId of ownerElementIds) {\n if (!this._processedOwnerIds.has(elementId)) {\n unprocessedOwnerIds.add(elementId);\n this._processedOwnerIds.add(elementId);\n }\n }\n if (unprocessedOwnerIds.size === 0) return;\n ownerIds = unprocessedOwnerIds;\n }\n\n if (ownerIds.size <= this._defaultBatchSize) {\n await this.exportOwnerBatch(ownerIds);\n return;\n }\n\n let ownerBatch = new Set<Id64String>();\n for (const elementId of ownerIds) {\n ownerBatch.add(elementId);\n if (ownerBatch.size === this._defaultBatchSize) {\n await this.exportOwnerBatch(ownerBatch);\n ownerBatch = new Set<Id64String>();\n }\n }\n if (ownerBatch.size > 0) {\n await this.exportOwnerBatch(ownerBatch);\n }\n }\n\n private async exportOwnerBatch(\n ownerElementIds: ReadonlySet<Id64String>\n ): Promise<void> {\n const complete = await this._prepare?.(\n this._excludedClassFullNames(),\n ownerElementIds\n );\n try {\n await this._exportAspects(ownerElementIds);\n } catch (error) {\n try {\n await complete?.(false);\n } catch {\n // Keep the export error; it explains why the group failed.\n }\n throw error;\n }\n await complete?.(true);\n }\n\n private async flush(): Promise<void> {\n const ownerElementIds = new Set(this._acceptedOwnerIds);\n this._acceptedOwnerIds.clear();\n await this.exportOwners(ownerElementIds);\n }\n\n private resetScopeState(): void {\n this._acceptedOwnerIds.clear();\n this._processedOwnerIds.clear();\n }\n\n private reset(): void {\n this._depth = 0;\n this.resetScopeState();\n this._batchSize = this._defaultBatchSize;\n }\n}\n"]}
@@ -0,0 +1,44 @@
1
+ import { ElementAspect, ElementMultiAspect, ElementUniqueAspect, IModelDb } from "@itwin/core-backend";
2
+ import { Id64String } from "@itwin/core-bentley";
3
+ interface AspectChanges {
4
+ readonly insertIds: ReadonlySet<Id64String>;
5
+ readonly updateIds: ReadonlySet<Id64String>;
6
+ }
7
+ export interface ElementAspectExportProcessorHandler {
8
+ shouldExportElementAspect(aspect: ElementAspect): Promise<boolean>;
9
+ onExportElementUniqueAspect(uniqueAspect: ElementUniqueAspect, isUpdate?: boolean | undefined): Promise<void>;
10
+ onExportElementMultiAspects(multiAspects: ElementMultiAspect[]): Promise<void>;
11
+ trackProgress: () => Promise<void>;
12
+ }
13
+ /** Queries and exports ElementAspects for accepted source owners, applying class and handler filters and grouping multi-aspects by owner.
14
+ * @internal
15
+ */
16
+ export declare class ElementAspectExportProcessor {
17
+ /** ElementAspect classes excluded from source queries. */
18
+ private readonly _excludedElementAspectClassFullNames;
19
+ private _hasExportableAspects;
20
+ private _expandedExcludedElementAspectClassFullNames;
21
+ private _aspectChanges;
22
+ /** ElementAspect class names excluded from source queries. */
23
+ get excludedElementAspectClassFullNames(): ReadonlySet<string>;
24
+ private readonly _sourceDb;
25
+ private readonly _handler;
26
+ constructor(sourceDb: IModelDb, handler: ElementAspectExportProcessorHandler);
27
+ /** Exports ElementAspects owned by the supplied elements.
28
+ * Multi-aspects are emitted in one callback group per owner.
29
+ */
30
+ exportAllElementAspects(ownerElementIds: ReadonlySet<Id64String>): Promise<void>;
31
+ /** Sets the aspect changes used to distinguish inserted and updated unique aspects during change export. */
32
+ setAspectChanges(aspectChanges?: AspectChanges): void;
33
+ /** Clears source schema and content caches at an outer export-scope boundary. */
34
+ resetCaches(): void;
35
+ /** Excludes an ElementAspect class from subsequent queries and export callbacks. */
36
+ excludeElementAspectClass(classFullName: string): void;
37
+ private getExcludedClassFilter;
38
+ private getHasExportableAspects;
39
+ private queryHasExportableAspects;
40
+ private getExpandedExcludedElementAspectClassFullNames;
41
+ private queryExpandedExcludedElementAspectClassFullNames;
42
+ }
43
+ export {};
44
+ //# sourceMappingURL=ElementAspectExportProcessor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ElementAspectExportProcessor.d.ts","sourceRoot":"","sources":["../../src/ElementAspectExportProcessor.ts"],"names":[],"mappings":"AAKA,OAAO,EACL,aAAa,EACb,kBAAkB,EAClB,mBAAmB,EACnB,QAAQ,EACT,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AAIjD,UAAU,aAAa;IACrB,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;IAC5C,QAAQ,CAAC,SAAS,EAAE,WAAW,CAAC,UAAU,CAAC,CAAC;CAC7C;AAED,MAAM,WAAW,mCAAmC;IAClD,yBAAyB,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnE,2BAA2B,CACzB,YAAY,EAAE,mBAAmB,EACjC,QAAQ,CAAC,EAAE,OAAO,GAAG,SAAS,GAC7B,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,2BAA2B,CACzB,YAAY,EAAE,kBAAkB,EAAE,GACjC,OAAO,CAAC,IAAI,CAAC,CAAC;IACjB,aAAa,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACpC;AAED;;GAEG;AACH,qBAAa,4BAA4B;IACvC,0DAA0D;IAC1D,OAAO,CAAC,QAAQ,CAAC,oCAAoC,CAAqB;IAC1E,OAAO,CAAC,qBAAqB,CAA+B;IAC5D,OAAO,CAAC,4CAA4C,CAEtC;IACd,OAAO,CAAC,cAAc,CAA4B;IAElD,8DAA8D;IAC9D,IAAW,mCAAmC,IAAI,WAAW,CAAC,MAAM,CAAC,CAEpE;IACD,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAW;IACrC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsC;IAE/D,YACE,QAAQ,EAAE,QAAQ,EAClB,OAAO,EAAE,mCAAmC,EAI7C;IAED;;OAEG;IACU,uBAAuB,CAClC,eAAe,EAAE,WAAW,CAAC,UAAU,CAAC,GACvC,OAAO,CAAC,IAAI,CAAC,CAkDf;IAED,4GAA4G;IACrG,gBAAgB,CAAC,aAAa,CAAC,EAAE,aAAa,GAAG,IAAI,CAE3D;IAED,iFAAiF;IAC1E,WAAW,IAAI,IAAI,CAGzB;IAED,oFAAoF;IAC7E,yBAAyB,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAM5D;IAED,OAAO,CAAC,sBAAsB;YAkBhB,uBAAuB;YAKvB,yBAAyB;YAuBzB,8CAA8C;YAQ9C,gDAAgD;CAmB/D"}
@@ -0,0 +1,152 @@
1
+ "use strict";
2
+ /*---------------------------------------------------------------------------------------------
3
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
4
+ * See LICENSE.md in the project root for license terms and full copyright notice.
5
+ *--------------------------------------------------------------------------------------------*/
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ElementAspectExportProcessor = void 0;
8
+ const core_backend_1 = require("@itwin/core-backend");
9
+ const ECSqlReaderAsyncIterableIteratorAdapter_1 = require("./ECSqlReaderAsyncIterableIteratorAdapter");
10
+ const core_common_1 = require("@itwin/core-common");
11
+ /** Queries and exports ElementAspects for accepted source owners, applying class and handler filters and grouping multi-aspects by owner.
12
+ * @internal
13
+ */
14
+ class ElementAspectExportProcessor {
15
+ /** ElementAspect classes excluded from source queries. */
16
+ _excludedElementAspectClassFullNames = new Set();
17
+ _hasExportableAspects;
18
+ _expandedExcludedElementAspectClassFullNames;
19
+ _aspectChanges;
20
+ /** ElementAspect class names excluded from source queries. */
21
+ get excludedElementAspectClassFullNames() {
22
+ return this._excludedElementAspectClassFullNames;
23
+ }
24
+ _sourceDb;
25
+ _handler;
26
+ constructor(sourceDb, handler) {
27
+ this._sourceDb = sourceDb;
28
+ this._handler = handler;
29
+ }
30
+ /** Exports ElementAspects owned by the supplied elements.
31
+ * Multi-aspects are emitted in one callback group per owner.
32
+ */
33
+ async exportAllElementAspects(ownerElementIds) {
34
+ if (ownerElementIds.size === 0 || !(await this.getHasExportableAspects()))
35
+ return;
36
+ let multiAspectOwnerId;
37
+ let multiAspects = [];
38
+ const exportMultiAspects = async () => {
39
+ if (multiAspects.length === 0)
40
+ return;
41
+ await this._handler.onExportElementMultiAspects(multiAspects);
42
+ await this._handler.trackProgress();
43
+ multiAspects = [];
44
+ multiAspectOwnerId = undefined;
45
+ };
46
+ const excludedAspectClassFullNames = await this.getExpandedExcludedElementAspectClassFullNames();
47
+ for await (const aspect of this._sourceDb.elements.queryAspects({
48
+ elementIds: new Set(ownerElementIds),
49
+ excludedAspectClassFullNames,
50
+ groupByOwner: true,
51
+ usePrimaryConn: true,
52
+ })) {
53
+ if (!(await this._handler.shouldExportElementAspect(aspect)))
54
+ continue;
55
+ if (multiAspectOwnerId !== undefined &&
56
+ multiAspectOwnerId !== aspect.element.id) {
57
+ await exportMultiAspects();
58
+ }
59
+ if (aspect instanceof core_backend_1.ElementUniqueAspect) {
60
+ const isUpdate = this._aspectChanges?.updateIds.has(aspect.id)
61
+ ? true
62
+ : this._aspectChanges?.insertIds.has(aspect.id)
63
+ ? false
64
+ : undefined;
65
+ await this._handler.onExportElementUniqueAspect(aspect, isUpdate);
66
+ await this._handler.trackProgress();
67
+ }
68
+ else if (aspect instanceof core_backend_1.ElementMultiAspect) {
69
+ multiAspectOwnerId = aspect.element.id;
70
+ multiAspects.push(aspect);
71
+ }
72
+ else {
73
+ throw new Error(`Unexpected ElementAspect type ${aspect.classFullName}`);
74
+ }
75
+ }
76
+ await exportMultiAspects();
77
+ }
78
+ /** Sets the aspect changes used to distinguish inserted and updated unique aspects during change export. */
79
+ setAspectChanges(aspectChanges) {
80
+ this._aspectChanges = aspectChanges;
81
+ }
82
+ /** Clears source schema and content caches at an outer export-scope boundary. */
83
+ resetCaches() {
84
+ this._hasExportableAspects = undefined;
85
+ this._expandedExcludedElementAspectClassFullNames = undefined;
86
+ }
87
+ /** Excludes an ElementAspect class from subsequent queries and export callbacks. */
88
+ excludeElementAspectClass(classFullName) {
89
+ const normalizedClassFullName = classFullName.replace(".", ":");
90
+ if (this._excludedElementAspectClassFullNames.has(normalizedClassFullName))
91
+ return;
92
+ this._excludedElementAspectClassFullNames.add(normalizedClassFullName);
93
+ this.resetCaches();
94
+ }
95
+ getExcludedClassFilter(queryParams) {
96
+ const excludedClassParameters = [];
97
+ let excludedClassIndex = 0;
98
+ for (const classFullName of this._excludedElementAspectClassFullNames) {
99
+ const parameterName = `excludedAspectClass${excludedClassIndex++}`;
100
+ excludedClassParameters.push(`ec_classid(:${parameterName})`);
101
+ queryParams.bindString(parameterName, classFullName);
102
+ }
103
+ return excludedClassParameters.length === 0
104
+ ? ""
105
+ : `AND NOT EXISTS (
106
+ SELECT 1 FROM meta.ClassHasAllBaseClasses excluded
107
+ WHERE excluded.SourceECInstanceId = aspect.ECClassId
108
+ AND excluded.TargetECInstanceId IN (${excludedClassParameters.join(",")})
109
+ )`;
110
+ }
111
+ async getHasExportableAspects() {
112
+ this._hasExportableAspects ??= this.queryHasExportableAspects();
113
+ return this._hasExportableAspects;
114
+ }
115
+ async queryHasExportableAspects() {
116
+ const queryParams = new core_common_1.QueryBinder();
117
+ const excludedClassFilter = this.getExcludedClassFilter(queryParams);
118
+ const reader = this._sourceDb.createQueryReader(`SELECT ECInstanceId FROM (
119
+ SELECT aspect.ECInstanceId
120
+ FROM Bis.ElementMultiAspect aspect
121
+ WHERE TRUE ${excludedClassFilter}
122
+ UNION ALL
123
+ SELECT aspect.ECInstanceId
124
+ FROM Bis.ElementUniqueAspect aspect
125
+ WHERE TRUE ${excludedClassFilter}
126
+ )
127
+ LIMIT 1`, queryParams, { usePrimaryConn: true });
128
+ for await (const _row of (0, ECSqlReaderAsyncIterableIteratorAdapter_1.ensureECSqlReaderIsAsyncIterableIterator)(reader))
129
+ return true;
130
+ return false;
131
+ }
132
+ async getExpandedExcludedElementAspectClassFullNames() {
133
+ this._expandedExcludedElementAspectClassFullNames ??=
134
+ this.queryExpandedExcludedElementAspectClassFullNames();
135
+ return this._expandedExcludedElementAspectClassFullNames;
136
+ }
137
+ async queryExpandedExcludedElementAspectClassFullNames() {
138
+ const excludedClassFullNames = new Set();
139
+ for (const classFullName of this._excludedElementAspectClassFullNames) {
140
+ const reader = this._sourceDb.createQueryReader(`SELECT ec_classname(classes.ECInstanceId, 's:c') AS classFullName
141
+ FROM meta.ClassHasAllBaseClasses inheritance
142
+ JOIN meta.ECClassDef classes
143
+ ON classes.ECInstanceId = inheritance.SourceECInstanceId
144
+ WHERE inheritance.TargetECInstanceId = ec_classid(:excludedClassName)`, new core_common_1.QueryBinder().bindString("excludedClassName", classFullName), { usePrimaryConn: true });
145
+ for await (const row of (0, ECSqlReaderAsyncIterableIteratorAdapter_1.ensureECSqlReaderIsAsyncIterableIterator)(reader))
146
+ excludedClassFullNames.add(row.classFullName);
147
+ }
148
+ return excludedClassFullNames;
149
+ }
150
+ }
151
+ exports.ElementAspectExportProcessor = ElementAspectExportProcessor;
152
+ //# sourceMappingURL=ElementAspectExportProcessor.js.map