@itwin/core-backend 5.9.0-dev.4 → 5.9.0-dev.5

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 (426) hide show
  1. package/lib/cjs/BriefcaseManager.js +3 -3
  2. package/lib/cjs/BriefcaseManager.js.map +1 -1
  3. package/lib/cjs/Category.d.ts +37 -7
  4. package/lib/cjs/Category.d.ts.map +1 -1
  5. package/lib/cjs/Category.js +33 -42
  6. package/lib/cjs/Category.js.map +1 -1
  7. package/lib/cjs/ChangesetECAdaptor.d.ts.map +1 -1
  8. package/lib/cjs/ChangesetECAdaptor.js +5 -2
  9. package/lib/cjs/ChangesetECAdaptor.js.map +1 -1
  10. package/lib/cjs/ChannelControl.d.ts +29 -0
  11. package/lib/cjs/ChannelControl.d.ts.map +1 -1
  12. package/lib/cjs/ChannelControl.js.map +1 -1
  13. package/lib/cjs/CheckpointManager.js +1 -1
  14. package/lib/cjs/CheckpointManager.js.map +1 -1
  15. package/lib/cjs/CodeSpecs.d.ts +32 -3
  16. package/lib/cjs/CodeSpecs.d.ts.map +1 -1
  17. package/lib/cjs/CodeSpecs.js +48 -20
  18. package/lib/cjs/CodeSpecs.js.map +1 -1
  19. package/lib/cjs/DisplayStyle.d.ts +18 -3
  20. package/lib/cjs/DisplayStyle.d.ts.map +1 -1
  21. package/lib/cjs/DisplayStyle.js +10 -21
  22. package/lib/cjs/DisplayStyle.js.map +1 -1
  23. package/lib/cjs/EditTxn.d.ts +262 -0
  24. package/lib/cjs/EditTxn.d.ts.map +1 -0
  25. package/lib/cjs/EditTxn.js +548 -0
  26. package/lib/cjs/EditTxn.js.map +1 -0
  27. package/lib/cjs/Element.d.ts +74 -5
  28. package/lib/cjs/Element.d.ts.map +1 -1
  29. package/lib/cjs/Element.js +63 -75
  30. package/lib/cjs/Element.js.map +1 -1
  31. package/lib/cjs/ElementAspect.d.ts +10 -0
  32. package/lib/cjs/ElementAspect.d.ts.map +1 -1
  33. package/lib/cjs/ElementAspect.js +21 -12
  34. package/lib/cjs/ElementAspect.js.map +1 -1
  35. package/lib/cjs/ElementTreeWalker.d.ts +56 -3
  36. package/lib/cjs/ElementTreeWalker.d.ts.map +1 -1
  37. package/lib/cjs/ElementTreeWalker.js +53 -40
  38. package/lib/cjs/ElementTreeWalker.js.map +1 -1
  39. package/lib/cjs/ExternalSource.d.ts +11 -2
  40. package/lib/cjs/ExternalSource.d.ts.map +1 -1
  41. package/lib/cjs/ExternalSource.js +10 -8
  42. package/lib/cjs/ExternalSource.js.map +1 -1
  43. package/lib/cjs/IModelDb.d.ts +66 -22
  44. package/lib/cjs/IModelDb.d.ts.map +1 -1
  45. package/lib/cjs/IModelDb.js +174 -288
  46. package/lib/cjs/IModelDb.js.map +1 -1
  47. package/lib/cjs/IModelHost.d.ts +22 -0
  48. package/lib/cjs/IModelHost.d.ts.map +1 -1
  49. package/lib/cjs/IModelHost.js +9 -0
  50. package/lib/cjs/IModelHost.js.map +1 -1
  51. package/lib/cjs/IpcHost.js +2 -2
  52. package/lib/cjs/IpcHost.js.map +1 -1
  53. package/lib/cjs/LineStyle.d.ts +47 -7
  54. package/lib/cjs/LineStyle.d.ts.map +1 -1
  55. package/lib/cjs/LineStyle.js +38 -33
  56. package/lib/cjs/LineStyle.js.map +1 -1
  57. package/lib/cjs/Material.d.ts +8 -1
  58. package/lib/cjs/Material.d.ts.map +1 -1
  59. package/lib/cjs/Material.js +6 -12
  60. package/lib/cjs/Material.js.map +1 -1
  61. package/lib/cjs/Model.d.ts +59 -20
  62. package/lib/cjs/Model.d.ts.map +1 -1
  63. package/lib/cjs/Model.js +38 -80
  64. package/lib/cjs/Model.js.map +1 -1
  65. package/lib/cjs/Relationship.d.ts +72 -7
  66. package/lib/cjs/Relationship.d.ts.map +1 -1
  67. package/lib/cjs/Relationship.js +39 -21
  68. package/lib/cjs/Relationship.js.map +1 -1
  69. package/lib/cjs/SchemaSync.js +4 -4
  70. package/lib/cjs/SchemaSync.js.map +1 -1
  71. package/lib/cjs/SheetIndex.d.ts +9 -0
  72. package/lib/cjs/SheetIndex.d.ts.map +1 -1
  73. package/lib/cjs/SheetIndex.js +38 -35
  74. package/lib/cjs/SheetIndex.js.map +1 -1
  75. package/lib/cjs/StashManager.js +1 -1
  76. package/lib/cjs/StashManager.js.map +1 -1
  77. package/lib/cjs/Texture.d.ts +6 -0
  78. package/lib/cjs/Texture.d.ts.map +1 -1
  79. package/lib/cjs/Texture.js +6 -14
  80. package/lib/cjs/Texture.js.map +1 -1
  81. package/lib/cjs/TxnManager.d.ts +5 -5
  82. package/lib/cjs/TxnManager.d.ts.map +1 -1
  83. package/lib/cjs/TxnManager.js +23 -7
  84. package/lib/cjs/TxnManager.js.map +1 -1
  85. package/lib/cjs/ViewDefinition.d.ts +21 -1
  86. package/lib/cjs/ViewDefinition.d.ts.map +1 -1
  87. package/lib/cjs/ViewDefinition.js +27 -66
  88. package/lib/cjs/ViewDefinition.js.map +1 -1
  89. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts +14 -6
  90. package/lib/cjs/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  91. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js +33 -27
  92. package/lib/cjs/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  93. package/lib/cjs/annotations/TextAnnotationElement.d.ts.map +1 -1
  94. package/lib/cjs/annotations/TextAnnotationElement.js +6 -5
  95. package/lib/cjs/annotations/TextAnnotationElement.js.map +1 -1
  96. package/lib/cjs/core-backend.d.ts +1 -0
  97. package/lib/cjs/core-backend.d.ts.map +1 -1
  98. package/lib/cjs/core-backend.js +1 -0
  99. package/lib/cjs/core-backend.js.map +1 -1
  100. package/lib/cjs/domains/FunctionalElements.d.ts +6 -2
  101. package/lib/cjs/domains/FunctionalElements.d.ts.map +1 -1
  102. package/lib/cjs/domains/FunctionalElements.js +8 -13
  103. package/lib/cjs/domains/FunctionalElements.js.map +1 -1
  104. package/lib/cjs/domains/GenericElements.d.ts +11 -4
  105. package/lib/cjs/domains/GenericElements.d.ts.map +1 -1
  106. package/lib/cjs/domains/GenericElements.js +13 -24
  107. package/lib/cjs/domains/GenericElements.js.map +1 -1
  108. package/lib/cjs/internal/ChannelAdmin.d.ts +15 -0
  109. package/lib/cjs/internal/ChannelAdmin.d.ts.map +1 -1
  110. package/lib/cjs/internal/ChannelAdmin.js +5 -3
  111. package/lib/cjs/internal/ChannelAdmin.js.map +1 -1
  112. package/lib/cjs/internal/Symbols.d.ts +4 -0
  113. package/lib/cjs/internal/Symbols.d.ts.map +1 -1
  114. package/lib/cjs/internal/Symbols.js +5 -1
  115. package/lib/cjs/internal/Symbols.js.map +1 -1
  116. package/lib/cjs/internal/annotations/fields.d.ts +3 -2
  117. package/lib/cjs/internal/annotations/fields.d.ts.map +1 -1
  118. package/lib/cjs/internal/annotations/fields.js +7 -6
  119. package/lib/cjs/internal/annotations/fields.js.map +1 -1
  120. package/lib/cjs/rpc-impl/RpcBriefcaseUtility.d.ts.map +1 -1
  121. package/lib/cjs/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
  122. package/lib/esm/BriefcaseManager.js +3 -3
  123. package/lib/esm/BriefcaseManager.js.map +1 -1
  124. package/lib/esm/Category.d.ts +37 -7
  125. package/lib/esm/Category.d.ts.map +1 -1
  126. package/lib/esm/Category.js +33 -42
  127. package/lib/esm/Category.js.map +1 -1
  128. package/lib/esm/ChangesetECAdaptor.d.ts.map +1 -1
  129. package/lib/esm/ChangesetECAdaptor.js +5 -2
  130. package/lib/esm/ChangesetECAdaptor.js.map +1 -1
  131. package/lib/esm/ChannelControl.d.ts +29 -0
  132. package/lib/esm/ChannelControl.d.ts.map +1 -1
  133. package/lib/esm/ChannelControl.js.map +1 -1
  134. package/lib/esm/CheckpointManager.js +1 -1
  135. package/lib/esm/CheckpointManager.js.map +1 -1
  136. package/lib/esm/CodeSpecs.d.ts +32 -3
  137. package/lib/esm/CodeSpecs.d.ts.map +1 -1
  138. package/lib/esm/CodeSpecs.js +49 -21
  139. package/lib/esm/CodeSpecs.js.map +1 -1
  140. package/lib/esm/DisplayStyle.d.ts +18 -3
  141. package/lib/esm/DisplayStyle.d.ts.map +1 -1
  142. package/lib/esm/DisplayStyle.js +10 -21
  143. package/lib/esm/DisplayStyle.js.map +1 -1
  144. package/lib/esm/EditTxn.d.ts +262 -0
  145. package/lib/esm/EditTxn.d.ts.map +1 -0
  146. package/lib/esm/EditTxn.js +543 -0
  147. package/lib/esm/EditTxn.js.map +1 -0
  148. package/lib/esm/Element.d.ts +74 -5
  149. package/lib/esm/Element.d.ts.map +1 -1
  150. package/lib/esm/Element.js +64 -76
  151. package/lib/esm/Element.js.map +1 -1
  152. package/lib/esm/ElementAspect.d.ts +10 -0
  153. package/lib/esm/ElementAspect.d.ts.map +1 -1
  154. package/lib/esm/ElementAspect.js +22 -13
  155. package/lib/esm/ElementAspect.js.map +1 -1
  156. package/lib/esm/ElementTreeWalker.d.ts +56 -3
  157. package/lib/esm/ElementTreeWalker.d.ts.map +1 -1
  158. package/lib/esm/ElementTreeWalker.js +53 -40
  159. package/lib/esm/ElementTreeWalker.js.map +1 -1
  160. package/lib/esm/ExternalSource.d.ts +11 -2
  161. package/lib/esm/ExternalSource.d.ts.map +1 -1
  162. package/lib/esm/ExternalSource.js +10 -8
  163. package/lib/esm/ExternalSource.js.map +1 -1
  164. package/lib/esm/IModelDb.d.ts +66 -22
  165. package/lib/esm/IModelDb.d.ts.map +1 -1
  166. package/lib/esm/IModelDb.js +176 -290
  167. package/lib/esm/IModelDb.js.map +1 -1
  168. package/lib/esm/IModelHost.d.ts +22 -0
  169. package/lib/esm/IModelHost.d.ts.map +1 -1
  170. package/lib/esm/IModelHost.js +9 -0
  171. package/lib/esm/IModelHost.js.map +1 -1
  172. package/lib/esm/IpcHost.js +3 -3
  173. package/lib/esm/IpcHost.js.map +1 -1
  174. package/lib/esm/LineStyle.d.ts +47 -7
  175. package/lib/esm/LineStyle.d.ts.map +1 -1
  176. package/lib/esm/LineStyle.js +38 -33
  177. package/lib/esm/LineStyle.js.map +1 -1
  178. package/lib/esm/Material.d.ts +8 -1
  179. package/lib/esm/Material.d.ts.map +1 -1
  180. package/lib/esm/Material.js +6 -12
  181. package/lib/esm/Material.js.map +1 -1
  182. package/lib/esm/Model.d.ts +59 -20
  183. package/lib/esm/Model.d.ts.map +1 -1
  184. package/lib/esm/Model.js +39 -81
  185. package/lib/esm/Model.js.map +1 -1
  186. package/lib/esm/Relationship.d.ts +72 -7
  187. package/lib/esm/Relationship.d.ts.map +1 -1
  188. package/lib/esm/Relationship.js +40 -22
  189. package/lib/esm/Relationship.js.map +1 -1
  190. package/lib/esm/SchemaSync.js +5 -5
  191. package/lib/esm/SchemaSync.js.map +1 -1
  192. package/lib/esm/SheetIndex.d.ts +9 -0
  193. package/lib/esm/SheetIndex.d.ts.map +1 -1
  194. package/lib/esm/SheetIndex.js +38 -35
  195. package/lib/esm/SheetIndex.js.map +1 -1
  196. package/lib/esm/StashManager.js +1 -1
  197. package/lib/esm/StashManager.js.map +1 -1
  198. package/lib/esm/Texture.d.ts +6 -0
  199. package/lib/esm/Texture.d.ts.map +1 -1
  200. package/lib/esm/Texture.js +6 -14
  201. package/lib/esm/Texture.js.map +1 -1
  202. package/lib/esm/TxnManager.d.ts +5 -5
  203. package/lib/esm/TxnManager.d.ts.map +1 -1
  204. package/lib/esm/TxnManager.js +23 -7
  205. package/lib/esm/TxnManager.js.map +1 -1
  206. package/lib/esm/ViewDefinition.d.ts +21 -1
  207. package/lib/esm/ViewDefinition.d.ts.map +1 -1
  208. package/lib/esm/ViewDefinition.js +27 -66
  209. package/lib/esm/ViewDefinition.js.map +1 -1
  210. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts +14 -6
  211. package/lib/esm/annotations/ElementDrivesTextAnnotation.d.ts.map +1 -1
  212. package/lib/esm/annotations/ElementDrivesTextAnnotation.js +33 -27
  213. package/lib/esm/annotations/ElementDrivesTextAnnotation.js.map +1 -1
  214. package/lib/esm/annotations/TextAnnotationElement.d.ts.map +1 -1
  215. package/lib/esm/annotations/TextAnnotationElement.js +6 -5
  216. package/lib/esm/annotations/TextAnnotationElement.js.map +1 -1
  217. package/lib/esm/core-backend.d.ts +1 -0
  218. package/lib/esm/core-backend.d.ts.map +1 -1
  219. package/lib/esm/core-backend.js +1 -0
  220. package/lib/esm/core-backend.js.map +1 -1
  221. package/lib/esm/domains/FunctionalElements.d.ts +6 -2
  222. package/lib/esm/domains/FunctionalElements.d.ts.map +1 -1
  223. package/lib/esm/domains/FunctionalElements.js +8 -13
  224. package/lib/esm/domains/FunctionalElements.js.map +1 -1
  225. package/lib/esm/domains/GenericElements.d.ts +11 -4
  226. package/lib/esm/domains/GenericElements.d.ts.map +1 -1
  227. package/lib/esm/domains/GenericElements.js +13 -24
  228. package/lib/esm/domains/GenericElements.js.map +1 -1
  229. package/lib/esm/internal/ChannelAdmin.d.ts +15 -0
  230. package/lib/esm/internal/ChannelAdmin.d.ts.map +1 -1
  231. package/lib/esm/internal/ChannelAdmin.js +6 -4
  232. package/lib/esm/internal/ChannelAdmin.js.map +1 -1
  233. package/lib/esm/internal/Symbols.d.ts +4 -0
  234. package/lib/esm/internal/Symbols.d.ts.map +1 -1
  235. package/lib/esm/internal/Symbols.js +4 -0
  236. package/lib/esm/internal/Symbols.js.map +1 -1
  237. package/lib/esm/internal/annotations/fields.d.ts +3 -2
  238. package/lib/esm/internal/annotations/fields.d.ts.map +1 -1
  239. package/lib/esm/internal/annotations/fields.js +7 -6
  240. package/lib/esm/internal/annotations/fields.js.map +1 -1
  241. package/lib/esm/rpc-impl/RpcBriefcaseUtility.d.ts.map +1 -1
  242. package/lib/esm/rpc-impl/RpcBriefcaseUtility.js.map +1 -1
  243. package/lib/esm/test/AnnotationTestUtils.js.map +1 -1
  244. package/lib/esm/test/ElementDrivesElement.test.d.ts +20 -19
  245. package/lib/esm/test/ElementDrivesElement.test.d.ts.map +1 -1
  246. package/lib/esm/test/ElementDrivesElement.test.js +111 -96
  247. package/lib/esm/test/ElementDrivesElement.test.js.map +1 -1
  248. package/lib/esm/test/ElementLRUCache.test.js.map +1 -1
  249. package/lib/esm/test/IModelHost.test.js +56 -2
  250. package/lib/esm/test/IModelHost.test.js.map +1 -1
  251. package/lib/esm/test/IModelTestUtils.d.ts +23 -23
  252. package/lib/esm/test/IModelTestUtils.d.ts.map +1 -1
  253. package/lib/esm/test/IModelTestUtils.js +466 -449
  254. package/lib/esm/test/IModelTestUtils.js.map +1 -1
  255. package/lib/esm/test/PropertyDb.test.js +2 -2
  256. package/lib/esm/test/PropertyDb.test.js.map +1 -1
  257. package/lib/esm/test/SquashSchemaAndDataChanges.test.js +27 -18
  258. package/lib/esm/test/SquashSchemaAndDataChanges.test.js.map +1 -1
  259. package/lib/esm/test/TestChangeSetUtility.d.ts.map +1 -1
  260. package/lib/esm/test/TestChangeSetUtility.js +11 -7
  261. package/lib/esm/test/TestChangeSetUtility.js.map +1 -1
  262. package/lib/esm/test/TestEditTxn.d.ts +8 -0
  263. package/lib/esm/test/TestEditTxn.d.ts.map +1 -0
  264. package/lib/esm/test/TestEditTxn.js +34 -0
  265. package/lib/esm/test/TestEditTxn.js.map +1 -0
  266. package/lib/esm/test/TestUtils.d.ts +1 -0
  267. package/lib/esm/test/TestUtils.d.ts.map +1 -1
  268. package/lib/esm/test/TestUtils.js +8 -1
  269. package/lib/esm/test/TestUtils.js.map +1 -1
  270. package/lib/esm/test/annotations/Fields.test.js +82 -90
  271. package/lib/esm/test/annotations/Fields.test.js.map +1 -1
  272. package/lib/esm/test/annotations/FrameGeometry.test.js.map +1 -1
  273. package/lib/esm/test/annotations/TextAnnotation.test.js +156 -99
  274. package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
  275. package/lib/esm/test/annotations/TextBlock.test.js +5 -3
  276. package/lib/esm/test/annotations/TextBlock.test.js.map +1 -1
  277. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/old.config.js.map +1 -1
  278. package/lib/esm/test/assets/IncrementalSchemaLocater/configs/simple.config.js.map +1 -1
  279. package/lib/esm/test/categories/Category.test.js +63 -3
  280. package/lib/esm/test/categories/Category.test.js.map +1 -1
  281. package/lib/esm/test/codespec/CodeSpec.test.js +88 -5
  282. package/lib/esm/test/codespec/CodeSpec.test.js.map +1 -1
  283. package/lib/esm/test/ecdb/ECDb.test.js.map +1 -1
  284. package/lib/esm/test/ecdb/ECSqlAst.test.js +3 -2
  285. package/lib/esm/test/ecdb/ECSqlAst.test.js.map +1 -1
  286. package/lib/esm/test/ecdb/ECSqlQuery.test.js +2 -2
  287. package/lib/esm/test/ecdb/ECSqlQuery.test.js.map +1 -1
  288. package/lib/esm/test/ecdb/ECSqlStatement.test.js +0 -1
  289. package/lib/esm/test/ecdb/ECSqlStatement.test.js.map +1 -1
  290. package/lib/esm/test/ecdb/QueryReaders.test.js +17 -14
  291. package/lib/esm/test/ecdb/QueryReaders.test.js.map +1 -1
  292. package/lib/esm/test/ecdb/SqliteStatement.test.js +2 -2
  293. package/lib/esm/test/ecdb/SqliteStatement.test.js.map +1 -1
  294. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.d.ts.map +1 -1
  295. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js +30 -28
  296. package/lib/esm/test/ecsql/dataset/ECSqlDatasets.js.map +1 -1
  297. package/lib/esm/test/ecsql/src/ECSqlTestGenerator.js.map +1 -1
  298. package/lib/esm/test/ecsql/src/ECSqlTestParser.js.map +1 -1
  299. package/lib/esm/test/ecsql/src/ECSqlTestRunner.test.js.map +1 -1
  300. package/lib/esm/test/element/DeleteDefinitionElements.test.js +159 -143
  301. package/lib/esm/test/element/DeleteDefinitionElements.test.js.map +1 -1
  302. package/lib/esm/test/element/ElementAspect.test.js +68 -60
  303. package/lib/esm/test/element/ElementAspect.test.js.map +1 -1
  304. package/lib/esm/test/element/ElementDependencyGraph.test.d.ts.map +1 -1
  305. package/lib/esm/test/element/ElementDependencyGraph.test.js +51 -43
  306. package/lib/esm/test/element/ElementDependencyGraph.test.js.map +1 -1
  307. package/lib/esm/test/element/ElementRoundTrip.test.js +37 -38
  308. package/lib/esm/test/element/ElementRoundTrip.test.js.map +1 -1
  309. package/lib/esm/test/element/ExcludedElements.test.js +2 -2
  310. package/lib/esm/test/element/ExcludedElements.test.js.map +1 -1
  311. package/lib/esm/test/element/ExternalSource.test.js +40 -38
  312. package/lib/esm/test/element/ExternalSource.test.js.map +1 -1
  313. package/lib/esm/test/element/NullStructArray.test.js +10 -9
  314. package/lib/esm/test/element/NullStructArray.test.js.map +1 -1
  315. package/lib/esm/test/element/ProjectInformationRecord.test.js +5 -2
  316. package/lib/esm/test/element/ProjectInformationRecord.test.js.map +1 -1
  317. package/lib/esm/test/element/SheetInformationAspect.test.js +43 -11
  318. package/lib/esm/test/element/SheetInformationAspect.test.js.map +1 -1
  319. package/lib/esm/test/element/UrlLink.test.js +2 -2
  320. package/lib/esm/test/element/UrlLink.test.js.map +1 -1
  321. package/lib/esm/test/font/IModelDbFonts.test.js +87 -73
  322. package/lib/esm/test/font/IModelDbFonts.test.js.map +1 -1
  323. package/lib/esm/test/hubaccess/ApplyChangeset.test.js +164 -125
  324. package/lib/esm/test/hubaccess/ApplyChangeset.test.js.map +1 -1
  325. package/lib/esm/test/hubaccess/BriefcaseManager.test.js +2 -4
  326. package/lib/esm/test/hubaccess/BriefcaseManager.test.js.map +1 -1
  327. package/lib/esm/test/hubaccess/Rebase.test.js +313 -247
  328. package/lib/esm/test/hubaccess/Rebase.test.js.map +1 -1
  329. package/lib/esm/test/hubaccess/SemanticRebase.test.js +322 -247
  330. package/lib/esm/test/hubaccess/SemanticRebase.test.js.map +1 -1
  331. package/lib/esm/test/imodel/Code.test.js +31 -31
  332. package/lib/esm/test/imodel/Code.test.js.map +1 -1
  333. package/lib/esm/test/imodel/ElementTreeWalker.test.js +57 -48
  334. package/lib/esm/test/imodel/ElementTreeWalker.test.js.map +1 -1
  335. package/lib/esm/test/imodel/IModel.test.js +419 -344
  336. package/lib/esm/test/imodel/IModel.test.js.map +1 -1
  337. package/lib/esm/test/imodel/ProjectExtents.test.js +2 -2
  338. package/lib/esm/test/imodel/ProjectExtents.test.js.map +1 -1
  339. package/lib/esm/test/incrementalSchemaLocater/ECSqlQueries.test.js.map +1 -1
  340. package/lib/esm/test/incrementalSchemaLocater/TestContext.d.ts.map +1 -1
  341. package/lib/esm/test/incrementalSchemaLocater/TestContext.js +2 -2
  342. package/lib/esm/test/incrementalSchemaLocater/TestContext.js.map +1 -1
  343. package/lib/esm/test/index.d.ts +1 -0
  344. package/lib/esm/test/index.d.ts.map +1 -1
  345. package/lib/esm/test/index.js +1 -0
  346. package/lib/esm/test/index.js.map +1 -1
  347. package/lib/esm/test/schema/ClassRegistry.test.js +23 -22
  348. package/lib/esm/test/schema/ClassRegistry.test.js.map +1 -1
  349. package/lib/esm/test/schema/FunctionalDomain.test.js +36 -34
  350. package/lib/esm/test/schema/FunctionalDomain.test.js.map +1 -1
  351. package/lib/esm/test/schema/GenericDomain.test.js +114 -94
  352. package/lib/esm/test/schema/GenericDomain.test.js.map +1 -1
  353. package/lib/esm/test/schema/IModelSchemaContext.test.js +2 -1
  354. package/lib/esm/test/schema/IModelSchemaContext.test.js.map +1 -1
  355. package/lib/esm/test/schema/SchemaImportCallbacks.test.js +207 -192
  356. package/lib/esm/test/schema/SchemaImportCallbacks.test.js.map +1 -1
  357. package/lib/esm/test/sheetindex/SheetIndex.test.d.ts +1 -3
  358. package/lib/esm/test/sheetindex/SheetIndex.test.d.ts.map +1 -1
  359. package/lib/esm/test/sheetindex/SheetIndex.test.js +312 -247
  360. package/lib/esm/test/sheetindex/SheetIndex.test.js.map +1 -1
  361. package/lib/esm/test/standalone/ChangeMerge.test.js +101 -82
  362. package/lib/esm/test/standalone/ChangeMerge.test.js.map +1 -1
  363. package/lib/esm/test/standalone/ChangesetReader.test.js +114 -85
  364. package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
  365. package/lib/esm/test/standalone/DisplayStyle.test.js +43 -40
  366. package/lib/esm/test/standalone/DisplayStyle.test.js.map +1 -1
  367. package/lib/esm/test/standalone/Drawing.test.js +4 -3
  368. package/lib/esm/test/standalone/Drawing.test.js.map +1 -1
  369. package/lib/esm/test/standalone/EditTxn.test.d.ts +2 -0
  370. package/lib/esm/test/standalone/EditTxn.test.d.ts.map +1 -0
  371. package/lib/esm/test/standalone/EditTxn.test.js +219 -0
  372. package/lib/esm/test/standalone/EditTxn.test.js.map +1 -0
  373. package/lib/esm/test/standalone/ElementMesh.test.js +16 -13
  374. package/lib/esm/test/standalone/ElementMesh.test.js.map +1 -1
  375. package/lib/esm/test/standalone/ExportGraphics.test.js +26 -20
  376. package/lib/esm/test/standalone/ExportGraphics.test.js.map +1 -1
  377. package/lib/esm/test/standalone/GeometryChangeEvents.test.js +11 -15
  378. package/lib/esm/test/standalone/GeometryChangeEvents.test.js.map +1 -1
  379. package/lib/esm/test/standalone/GeometryStream.test.js +212 -165
  380. package/lib/esm/test/standalone/GeometryStream.test.js.map +1 -1
  381. package/lib/esm/test/standalone/HubMock.test.js +31 -25
  382. package/lib/esm/test/standalone/HubMock.test.js.map +1 -1
  383. package/lib/esm/test/standalone/IModelLimits.test.js +11 -8
  384. package/lib/esm/test/standalone/IModelLimits.test.js.map +1 -1
  385. package/lib/esm/test/standalone/IModelWrite.test.d.ts +2 -2
  386. package/lib/esm/test/standalone/IModelWrite.test.d.ts.map +1 -1
  387. package/lib/esm/test/standalone/IModelWrite.test.js +184 -142
  388. package/lib/esm/test/standalone/IModelWrite.test.js.map +1 -1
  389. package/lib/esm/test/standalone/InlineGeometryPartReferences.test.js +25 -22
  390. package/lib/esm/test/standalone/InlineGeometryPartReferences.test.js.map +1 -1
  391. package/lib/esm/test/standalone/IntegrityCheck.test.js +20 -18
  392. package/lib/esm/test/standalone/IntegrityCheck.test.js.map +1 -1
  393. package/lib/esm/test/standalone/MergeConflict.test.d.ts +2 -2
  394. package/lib/esm/test/standalone/MergeConflict.test.d.ts.map +1 -1
  395. package/lib/esm/test/standalone/MergeConflict.test.js +49 -33
  396. package/lib/esm/test/standalone/MergeConflict.test.js.map +1 -1
  397. package/lib/esm/test/standalone/RenderMaterialElement.test.js +5 -5
  398. package/lib/esm/test/standalone/RenderMaterialElement.test.js.map +1 -1
  399. package/lib/esm/test/standalone/RenderTimeline.test.js +3 -2
  400. package/lib/esm/test/standalone/RenderTimeline.test.js.map +1 -1
  401. package/lib/esm/test/standalone/SectionDrawing.test.js +7 -7
  402. package/lib/esm/test/standalone/SectionDrawing.test.js.map +1 -1
  403. package/lib/esm/test/standalone/ServerBasedLocks.test.js +21 -19
  404. package/lib/esm/test/standalone/ServerBasedLocks.test.js.map +1 -1
  405. package/lib/esm/test/standalone/Settings.test.js +7 -4
  406. package/lib/esm/test/standalone/Settings.test.js.map +1 -1
  407. package/lib/esm/test/standalone/SettingsSchemas.test.js +2 -1
  408. package/lib/esm/test/standalone/SettingsSchemas.test.js.map +1 -1
  409. package/lib/esm/test/standalone/SnapshotDb.test.js +3 -1
  410. package/lib/esm/test/standalone/SnapshotDb.test.js.map +1 -1
  411. package/lib/esm/test/standalone/StandaloneDb.test.js +7 -6
  412. package/lib/esm/test/standalone/StandaloneDb.test.js.map +1 -1
  413. package/lib/esm/test/standalone/Texture.test.js +5 -4
  414. package/lib/esm/test/standalone/Texture.test.js.map +1 -1
  415. package/lib/esm/test/standalone/TileCache.test.d.ts.map +1 -1
  416. package/lib/esm/test/standalone/TileCache.test.js +5 -3
  417. package/lib/esm/test/standalone/TileCache.test.js.map +1 -1
  418. package/lib/esm/test/standalone/TileTree.test.js +35 -31
  419. package/lib/esm/test/standalone/TileTree.test.js.map +1 -1
  420. package/lib/esm/test/standalone/TxnManager.test.js +700 -653
  421. package/lib/esm/test/standalone/TxnManager.test.js.map +1 -1
  422. package/lib/esm/test/standalone/ViewDefinition.test.js +295 -229
  423. package/lib/esm/test/standalone/ViewDefinition.test.js.map +1 -1
  424. package/lib/esm/test/standalone/Workspace.test.js +25 -23
  425. package/lib/esm/test/standalone/Workspace.test.js.map +1 -1
  426. package/package.json +12 -12
@@ -59,6 +59,7 @@ import * as path from "path";
59
59
  import * as semver from "semver";
60
60
  import * as sinon from "sinon";
61
61
  import { DbResult, Guid, Id64, IModelStatus, Logger, OpenMode, ProcessDetector } from "@itwin/core-bentley";
62
+ import { EditTxn, withEditTxn } from "../../EditTxn";
62
63
  import { BisCodeSpec, BriefcaseIdValue, Code, CodeScopeSpec, CodeSpec, ColorByName, ColorDef, DisplayStyleSettings, EcefLocation, FontMap, FontType, GeoCoordStatus, GeographicCRS, GeometryParams, GeometryStreamBuilder, ImageSourceFormat, IModel, IModelError, QueryBinder, RelatedElement, RenderMode, SchemaState, SubCategoryAppearance, TextureMapping, TextureMapUnits, ViewFlags, } from "@itwin/core-common";
63
64
  import { Geometry, LineString3d, Loop, Matrix4d, Point3d, PolyfaceBuilder, Range3d, StrokeOptions, Transform, YawPitchRollAngles, } from "@itwin/core-geometry";
64
65
  import { V2CheckpointManager } from "../../CheckpointManager";
@@ -92,8 +93,7 @@ async function generateTestSnapshot(targetFileName, seedAssetName) {
92
93
  const snapshotFile = IModelTestUtils.prepareOutputFile("IModel", targetFileName);
93
94
  const imodel = IModelTestUtils.createSnapshotFromSeed(snapshotFile, seedFile);
94
95
  const schemaPathname = path.join(KnownTestLocations.assetsDir, "TestBim.ecschema.xml");
95
- await imodel.importSchemas([schemaPathname]); // will throw an exception if import fails
96
- imodel.saveChanges();
96
+ await imodel.importSchemas([schemaPathname]);
97
97
  return imodel;
98
98
  }
99
99
  describe("iModel", () => {
@@ -272,30 +272,33 @@ describe("iModel", () => {
272
272
  assert.notEqual(a2, el3);
273
273
  assert.equal(a2.id, el3.id);
274
274
  roundtripThroughJson(el3);
275
+ const txn = new EditTxn(imodel2, "code scope mutation test");
276
+ txn.start();
275
277
  const newEl = el3.toJSON();
276
278
  newEl.federationGuid = undefined;
277
279
  newEl.code = { scope: "bad scope", spec: "0x10", value: "new code" };
278
- expect(() => imodel2.elements.insertElement(newEl)).throws("invalid code scope").to.have.property("metadata");
280
+ expect(() => txn.insertElement(newEl)).throws("invalid code scope").to.have.property("metadata");
279
281
  newEl.code.scope = "0x34322"; // valid id, but element doesn't exist
280
- expect(() => imodel2.elements.insertElement(newEl)).throws("invalid code scope").to.have.property("metadata");
282
+ expect(() => txn.insertElement(newEl)).throws("invalid code scope").to.have.property("metadata");
281
283
  newEl.code.scope = el3.federationGuid;
282
- const newId = imodel2.elements.insertElement(newEl); // code scope from FederationGuid should get converted to ElementId
284
+ const newId = txn.insertElement(newEl); // code scope from FederationGuid should get converted to ElementId
283
285
  const a4 = imodel2.elements.getElementProps(newId);
284
286
  expect(a4.code.scope).equal(el3.id);
285
287
  a4.code.scope = "0x13343";
286
- expect(() => imodel2.elements.updateElement(a4)).throws("invalid code scope").to.have.property("metadata");
288
+ expect(() => txn.updateElement(a4)).throws("invalid code scope").to.have.property("metadata");
287
289
  a4.code.scope = "0x1";
288
- imodel2.elements.updateElement(a4); // should change the code scope to new element
290
+ txn.updateElement(a4); // should change the code scope to new element
289
291
  let a5 = imodel2.elements.getElementProps(newId);
290
292
  expect(a5.code.scope).equal("0x1");
291
293
  // only pass minimum, but expect model and classFullName to be added.
292
294
  const newProps = { id: a4.id, code: a4.code, classFullName: undefined, model: undefined };
293
295
  newProps.code.scope = el3.federationGuid; // should convert FederationGuid to ElementId
294
- imodel2.elements.updateElement(newProps);
296
+ txn.updateElement(newProps);
295
297
  expect(newProps.classFullName).eq(a4.classFullName);
296
298
  expect(newProps.model).eq(a4.model);
297
299
  a5 = imodel2.elements.getElementProps(newId);
298
300
  expect(a5.code.scope).equal(el3.id);
301
+ txn.end();
299
302
  });
300
303
  it("should optionally detect class mismatches", () => {
301
304
  // tryGetElement
@@ -342,20 +345,22 @@ describe("iModel", () => {
342
345
  const seedElement = imodel2.elements.getElement("0x1d");
343
346
  assert.exists(seedElement);
344
347
  assert.isTrue(seedElement.federationGuid === "18eb4650-b074-414f-b961-d9cfaa6c8746");
345
- for (let i = 0; i < 25; i++) {
346
- const elementProps = {
347
- classFullName: "Generic:PhysicalObject",
348
- model: seedElement.model,
349
- category: seedElement.category,
350
- code: Code.createEmpty(),
351
- federationGuid: Guid.createValue(),
352
- userLabel: `UserLabel-${i}`,
353
- };
354
- const element = imodel2.elements.createElement(elementProps);
355
- element.setUserProperties("performanceTest", { s: `String-${i}`, n: i });
356
- const elementId = imodel2.elements.insertElement(element.toJSON());
357
- assert.isTrue(Id64.isValidId64(elementId));
358
- }
348
+ withEditTxn(imodel2, (txn) => {
349
+ for (let i = 0; i < 25; i++) {
350
+ const elementProps = {
351
+ classFullName: "Generic:PhysicalObject",
352
+ model: seedElement.model,
353
+ category: seedElement.category,
354
+ code: Code.createEmpty(),
355
+ federationGuid: Guid.createValue(),
356
+ userLabel: `UserLabel-${i}`,
357
+ };
358
+ const element = imodel2.elements.createElement(elementProps);
359
+ element.setUserProperties("performanceTest", { s: `String-${i}`, n: i });
360
+ const elementId = txn.insertElement(element.toJSON());
361
+ assert.isTrue(Id64.isValidId64(elementId));
362
+ }
363
+ });
359
364
  });
360
365
  it("should insert a RenderMaterial", () => {
361
366
  const model = imodel2.models.getModel(IModel.dictionaryId);
@@ -397,7 +402,7 @@ describe("iModel", () => {
397
402
  reflectColor,
398
403
  patternMap: textureMapProps,
399
404
  };
400
- const renderMaterialId = RenderMaterialElement.insert(imodel2, IModel.dictionaryId, testMaterialName, renderMaterialParams);
405
+ const renderMaterialId = withEditTxn(imodel2, (txn) => RenderMaterialElement.insert(txn, IModel.dictionaryId, testMaterialName, renderMaterialParams));
401
406
  const renderMaterial = imodel2.elements.getElement(renderMaterialId);
402
407
  assert((renderMaterial instanceof RenderMaterialElement) === true, "did not retrieve an instance of RenderMaterial");
403
408
  expect(renderMaterial.paletteName).to.equal(testPaletteName);
@@ -435,9 +440,11 @@ describe("iModel", () => {
435
440
  const testTextureName = "fake texture name";
436
441
  const testTextureFormat = ImageSourceFormat.Png;
437
442
  const testTextureDescription = "empty description";
438
- const texId = Texture.insertTexture(imodel5, IModel.dictionaryId, testTextureName, testTextureFormat, samplePngTexture.base64, testTextureDescription);
443
+ const txn = new EditTxn(imodel5, "apply material to new element");
444
+ txn.start();
445
+ const texId = Texture.insertTexture(txn, IModel.dictionaryId, testTextureName, testTextureFormat, samplePngTexture.base64, testTextureDescription);
439
446
  /* eslint-disable @typescript-eslint/naming-convention */
440
- const matId = RenderMaterialElement.insert(imodel5, IModel.dictionaryId, "test material name", {
447
+ const matId = RenderMaterialElement.insert(txn, IModel.dictionaryId, "test material name", {
441
448
  paletteName: "TestPaletteName",
442
449
  patternMap: {
443
450
  TextureId: texId,
@@ -474,8 +481,8 @@ describe("iModel", () => {
474
481
  Point3d.create(0, 0, 0),
475
482
  ]));
476
483
  }
477
- const modelId = PhysicalModel.insert(imodel5, IModelDb.rootSubjectId, "test_render_material_model_name");
478
- const categoryId = SpatialCategory.insert(imodel5, IModel.dictionaryId, "GeoJSON Feature", { color: ColorDef.white.toJSON() });
484
+ const modelId = PhysicalModel.insert(txn, IModelDb.rootSubjectId, "test_render_material_model_name");
485
+ const categoryId = SpatialCategory.insert(txn, IModel.dictionaryId, "GeoJSON Feature", { color: ColorDef.white.toJSON() });
479
486
  /** generate a geometry stream containing the polyface */
480
487
  const gsBuilder = new GeometryStreamBuilder();
481
488
  const params = new GeometryParams(categoryId);
@@ -492,8 +499,8 @@ describe("iModel", () => {
492
499
  category: categoryId,
493
500
  geom: geometry,
494
501
  };
495
- imodel5.elements.insertElement(props);
496
- imodel5.saveChanges();
502
+ txn.insertElement(props);
503
+ txn.end();
497
504
  });
498
505
  it("should insert a DisplayStyle", () => {
499
506
  const model = imodel2.models.getModel(IModel.dictionaryId);
@@ -513,7 +520,9 @@ describe("iModel", () => {
513
520
  styles: settings,
514
521
  },
515
522
  };
516
- const styleId = imodel2.elements.insertElement(props);
523
+ const txn = new EditTxn(imodel2, "insert and update DisplayStyle");
524
+ txn.start();
525
+ const styleId = txn.insertElement(props);
517
526
  let style = imodel2.elements.getElement(styleId);
518
527
  expect(style instanceof DisplayStyle3d).to.be.true;
519
528
  expect(style.code.spec).equal(imodel2.codeSpecs.getByName(BisCodeSpec.displayStyle).id);
@@ -524,7 +533,8 @@ describe("iModel", () => {
524
533
  style.settings.backgroundColor = ColorDef.red;
525
534
  style.settings.monochromeColor = ColorDef.green;
526
535
  expect(style.jsonProperties.styles.viewflags.renderMode).to.equal(RenderMode.SmoothShade);
527
- imodel2.elements.updateElement(style.toJSON());
536
+ txn.updateElement(style.toJSON());
537
+ txn.end();
528
538
  style = imodel2.elements.getElement(styleId);
529
539
  expect(style instanceof DisplayStyle3d).to.be.true;
530
540
  expect(style.settings.viewFlags.renderMode).to.equal(RenderMode.SmoothShade);
@@ -565,25 +575,27 @@ describe("iModel", () => {
565
575
  [{ backgroundColor: ColorDef.from(1, 2, 3, 4).tbgr }, defaultViewFlags, false],
566
576
  ];
567
577
  let suffix = 123;
568
- for (const test of testCases) {
569
- const expected = test[0] ?? {};
570
- const styleId = DisplayStyle3d.insert(imodel2, IModel.dictionaryId, `TestStyle${suffix++}`, expected);
571
- const style = imodel2.elements.getElement(styleId).toJSON();
572
- expect(style.jsonProperties.styles).not.to.be.undefined;
573
- expect(style.jsonProperties).not.to.be.undefined;
574
- expect(style.jsonProperties.styles).not.to.be.undefined;
575
- const actual = style.jsonProperties.styles;
576
- expect(actual.viewflags).not.to.be.undefined;
577
- const expectedVf = ViewFlags.fromJSON(test[1]);
578
- const actualVf = ViewFlags.fromJSON(actual.viewflags);
579
- expect(actualVf.toJSON()).to.deep.equal(expectedVf.toJSON());
580
- const expectedBGColor = expected.backgroundColor instanceof ColorDef ? expected.backgroundColor.toJSON() : expected.backgroundColor;
581
- expect(actual.backgroundColor).to.equal(expectedBGColor);
582
- // DisplayStyleSettings constructor always initializes json.mapImagery.
583
- expect(actual.mapImagery).to.deep.equal(expected.mapImagery ?? defaultMapImagery);
584
- expect(actual.excludedElements).to.deep.equal(expected.excludedElements);
585
- expect(actual.timePoint).to.deep.equal(expected.timePoint);
586
- }
578
+ withEditTxn(imodel2, (txn) => {
579
+ for (const test of testCases) {
580
+ const expected = test[0] ?? {};
581
+ const styleId = DisplayStyle3d.insert(txn, IModel.dictionaryId, `TestStyle${suffix++}`, expected);
582
+ const style = imodel2.elements.getElement(styleId).toJSON();
583
+ expect(style.jsonProperties.styles).not.to.be.undefined;
584
+ expect(style.jsonProperties).not.to.be.undefined;
585
+ expect(style.jsonProperties.styles).not.to.be.undefined;
586
+ const actual = style.jsonProperties.styles;
587
+ expect(actual.viewflags).not.to.be.undefined;
588
+ const expectedVf = ViewFlags.fromJSON(test[1]);
589
+ const actualVf = ViewFlags.fromJSON(actual.viewflags);
590
+ expect(actualVf.toJSON()).to.deep.equal(expectedVf.toJSON());
591
+ const expectedBGColor = expected.backgroundColor instanceof ColorDef ? expected.backgroundColor.toJSON() : expected.backgroundColor;
592
+ expect(actual.backgroundColor).to.equal(expectedBGColor);
593
+ // DisplayStyleSettings constructor always initializes json.mapImagery.
594
+ expect(actual.mapImagery).to.deep.equal(expected.mapImagery ?? defaultMapImagery);
595
+ expect(actual.excludedElements).to.deep.equal(expected.excludedElements);
596
+ expect(actual.timePoint).to.deep.equal(expected.timePoint);
597
+ }
598
+ });
587
599
  });
588
600
  it("should have a valid root subject element", () => {
589
601
  const rootSubject = imodel1.elements.getRootSubject();
@@ -906,7 +918,9 @@ describe("iModel", () => {
906
918
  newTestElem.asAny.arrayOfStructs = arrayOfStructs;
907
919
  newTestElem.asAny.dtUtc = new Date("2015-03-25");
908
920
  newTestElem.asAny.p3d = new Point3d(1, 2, 3);
909
- const newTestElemId = imodel4.elements.insertElement(newTestElem.toJSON());
921
+ const txn = new EditTxn(imodel4, "insert and update auto-handled properties");
922
+ txn.start();
923
+ const newTestElemId = txn.insertElement(newTestElem.toJSON());
910
924
  assert.isTrue(Id64.isValidId64(newTestElemId), "insert worked");
911
925
  const newTestElemFetched = imodel4.elements.getElement(newTestElemId);
912
926
  assert.isDefined(newTestElemFetched);
@@ -925,7 +939,7 @@ describe("iModel", () => {
925
939
  const editElem = newTestElemFetched;
926
940
  editElem.asAny.location = loc2;
927
941
  try {
928
- imodel4.elements.updateElement(editElem.toJSON());
942
+ txn.updateElement(editElem.toJSON());
929
943
  }
930
944
  catch {
931
945
  assert.fail("Element.update failed");
@@ -937,46 +951,50 @@ describe("iModel", () => {
937
951
  // Make array shorter
938
952
  assert.equal(afterUpdateElemFetched.asAny.arrayOfInt.length, 300);
939
953
  afterUpdateElemFetched.asAny.arrayOfInt = [99, 3];
940
- imodel4.elements.updateElement(afterUpdateElemFetched.toJSON());
954
+ txn.updateElement(afterUpdateElemFetched.toJSON());
941
955
  const afterShortenArray = imodel4.elements.getElement(afterUpdateElemFetched.id);
942
956
  assert.equal(afterUpdateElemFetched.asAny.arrayOfInt.length, 2);
943
957
  assert.deepEqual(afterShortenArray.asAny.arrayOfInt, [99, 3]);
944
958
  // Make array longer
945
959
  afterShortenArray.asAny.arrayOfInt = [1, 2, 3];
946
- imodel4.elements.updateElement(afterShortenArray.toJSON());
960
+ txn.updateElement(afterShortenArray.toJSON());
947
961
  const afterLengthenArray = imodel4.elements.getElement(afterShortenArray.id);
948
962
  assert.equal(afterLengthenArray.asAny.arrayOfInt.length, 3);
949
963
  assert.deepEqual(afterLengthenArray.asAny.arrayOfInt, [1, 2, 3]);
950
964
  // ------------ delete -----------------
951
965
  const elid = afterUpdateElemFetched.id;
952
- imodel4.elements.deleteElement(elid);
966
+ txn.deleteElement(elid);
953
967
  assert.throws(() => imodel4.elements.getElement(elid), IModelError);
968
+ txn.end();
954
969
  });
955
970
  it("should handle parent and child deletion properly", () => {
956
- const categoryId = SpatialCategory.insert(imodel4, IModel.dictionaryId, "MyTestCategory", new SubCategoryAppearance());
971
+ const txn = new EditTxn(imodel4, "handle parent and child deletion");
972
+ txn.start();
973
+ const categoryId = SpatialCategory.insert(txn, IModel.dictionaryId, "MyTestCategory", new SubCategoryAppearance());
957
974
  const category = imodel4.elements.getElement(categoryId);
958
975
  const subCategory = imodel4.elements.getElement(category.myDefaultSubCategoryId());
959
- expect(() => imodel4.elements.deleteElement(categoryId)).throws("error deleting element").to.have.property("metadata");
976
+ expect(() => txn.deleteElement(categoryId)).throws("error deleting element").to.have.property("metadata");
960
977
  assert.exists(imodel4.elements.getElement(categoryId), "Category deletes should be blocked in native code");
961
978
  assert.exists(imodel4.elements.getElement(subCategory.id), "Children should not be deleted if parent delete is blocked");
962
- const modelId = PhysicalModel.insert(imodel4, IModel.rootSubjectId, "MyTestPhysicalModel");
979
+ const modelId = PhysicalModel.insert(txn, IModel.rootSubjectId, "MyTestPhysicalModel");
963
980
  const elementProps = {
964
981
  classFullName: PhysicalObject.classFullName,
965
982
  model: modelId,
966
983
  category: categoryId,
967
984
  code: Code.createEmpty(),
968
985
  };
969
- const parentId = imodel4.elements.insertElement(elementProps);
986
+ const parentId = txn.insertElement(elementProps);
970
987
  elementProps.parent = new ElementOwnsChildElements(parentId);
971
- const childId1 = imodel4.elements.insertElement(elementProps);
972
- const childId2 = imodel4.elements.insertElement(elementProps);
988
+ const childId1 = txn.insertElement(elementProps);
989
+ const childId2 = txn.insertElement(elementProps);
973
990
  assert.exists(imodel4.elements.getElement(parentId));
974
991
  assert.exists(imodel4.elements.getElement(childId1));
975
992
  assert.exists(imodel4.elements.getElement(childId2));
976
- imodel4.elements.deleteElement(parentId);
993
+ txn.deleteElement(parentId);
977
994
  assert.throws(() => imodel4.elements.getElement(parentId), IModelError);
978
995
  assert.throws(() => imodel4.elements.getElement(childId1), IModelError);
979
996
  assert.throws(() => imodel4.elements.getElement(childId2), IModelError);
997
+ txn.end();
980
998
  });
981
999
  function checkElementMetaData(entityClass) {
982
1000
  assert.isNotNull(entityClass);
@@ -1002,29 +1020,31 @@ describe("iModel", () => {
1002
1020
  it("should get metadata for a relationship", async () => {
1003
1021
  const imodelPath = IModelTestUtils.prepareOutputFile("IModel", "relationshipMetadata.bim");
1004
1022
  const imodel = SnapshotDb.createEmpty(imodelPath, { rootSubject: { name: "relationshipMetadata" } });
1005
- const partitionId = imodel.elements.insertElement({
1006
- classFullName: "BisCore:PhysicalPartition",
1007
- model: IModel.repositoryModelId,
1008
- parent: {
1009
- relClassName: "BisCore:SubjectOwnsPartitionElements",
1010
- id: IModel.rootSubjectId,
1011
- },
1012
- code: new Code({
1013
- spec: imodel.codeSpecs.getByName(BisCodeSpec.informationPartitionElement).id,
1014
- scope: IModel.rootSubjectId,
1015
- value: "physical model",
1016
- }),
1017
- });
1018
- for await (const row of imodel.createQueryReader(`SELECT * FROM bis.Element LIMIT ${1}`)) {
1019
- const relId = imodel.relationships.insertInstance({
1020
- classFullName: "BisCore:ElementHasLinks",
1021
- sourceId: partitionId,
1022
- targetId: row.ECInstanceId,
1023
+ await withEditTxn(imodel, async (txn) => {
1024
+ const partitionId = txn.insertElement({
1025
+ classFullName: "BisCore:PhysicalPartition",
1026
+ model: IModel.repositoryModelId,
1027
+ parent: {
1028
+ relClassName: "BisCore:SubjectOwnsPartitionElements",
1029
+ id: IModel.rootSubjectId,
1030
+ },
1031
+ code: new Code({
1032
+ spec: imodel.codeSpecs.getByName(BisCodeSpec.informationPartitionElement).id,
1033
+ scope: IModel.rootSubjectId,
1034
+ value: "physical model",
1035
+ }),
1023
1036
  });
1024
- const relationship = imodel.relationships.getInstance("BisCore:ElementHasLinks", relId);
1025
- const metadata = await relationship.getMetaData();
1026
- assert.isDefined(metadata, "metadata should be defined");
1027
- }
1037
+ for await (const row of imodel.createQueryReader(`SELECT * FROM bis.Element LIMIT ${1}`)) {
1038
+ const relId = txn.insertRelationship({
1039
+ classFullName: "BisCore:ElementHasLinks",
1040
+ sourceId: partitionId,
1041
+ targetId: row.ECInstanceId,
1042
+ });
1043
+ const relationship = imodel.relationships.getInstance("BisCore:ElementHasLinks", relId);
1044
+ const metadata = await relationship.getMetaData();
1045
+ assert.isDefined(metadata, "metadata should be defined");
1046
+ }
1047
+ });
1028
1048
  imodel.close();
1029
1049
  });
1030
1050
  it("should get metadata for class", () => {
@@ -1036,31 +1056,33 @@ describe("iModel", () => {
1036
1056
  it("should iterate through metadata for a relationship", async () => {
1037
1057
  const imodelPath = IModelTestUtils.prepareOutputFile("IModel", "relationshipMetadata.bim");
1038
1058
  const imodel = SnapshotDb.createEmpty(imodelPath, { rootSubject: { name: "relationshipMetadata" } });
1039
- const partitionId = imodel.elements.insertElement({
1040
- classFullName: "BisCore:PhysicalPartition",
1041
- model: IModel.repositoryModelId,
1042
- parent: {
1043
- relClassName: "BisCore:SubjectOwnsPartitionElements",
1044
- id: IModel.rootSubjectId,
1045
- },
1046
- code: new Code({
1047
- spec: imodel.codeSpecs.getByName(BisCodeSpec.informationPartitionElement).id,
1048
- scope: IModel.rootSubjectId,
1049
- value: "physical model",
1050
- }),
1051
- });
1052
- for await (const row of imodel.createQueryReader(`SELECT * FROM bis.Element LIMIT ${1}`)) {
1053
- const relId = imodel.relationships.insertInstance({
1054
- classFullName: "BisCore:ElementHasLinks",
1055
- sourceId: partitionId,
1056
- targetId: row.ECInstanceId,
1057
- });
1058
- const relationship = imodel.relationships.getInstance("BisCore:ElementHasLinks", relId);
1059
- relationship.forEach((propName, propMeta) => {
1060
- assert.isDefined(propName, "Property name should be defined");
1061
- assert.isDefined(propMeta, "Property metadata should be defined");
1059
+ await withEditTxn(imodel, async (txn) => {
1060
+ const partitionId = txn.insertElement({
1061
+ classFullName: "BisCore:PhysicalPartition",
1062
+ model: IModel.repositoryModelId,
1063
+ parent: {
1064
+ relClassName: "BisCore:SubjectOwnsPartitionElements",
1065
+ id: IModel.rootSubjectId,
1066
+ },
1067
+ code: new Code({
1068
+ spec: imodel.codeSpecs.getByName(BisCodeSpec.informationPartitionElement).id,
1069
+ scope: IModel.rootSubjectId,
1070
+ value: "physical model",
1071
+ }),
1062
1072
  });
1063
- }
1073
+ for await (const row of imodel.createQueryReader(`SELECT * FROM bis.Element LIMIT ${1}`)) {
1074
+ const relId = txn.insertRelationship({
1075
+ classFullName: "BisCore:ElementHasLinks",
1076
+ sourceId: partitionId,
1077
+ targetId: row.ECInstanceId,
1078
+ });
1079
+ const relationship = imodel.relationships.getInstance("BisCore:ElementHasLinks", relId);
1080
+ relationship.forEach((propName, propMeta) => {
1081
+ assert.isDefined(propName, "Property name should be defined");
1082
+ assert.isDefined(propMeta, "Property metadata should be defined");
1083
+ });
1084
+ }
1085
+ });
1064
1086
  imodel.close();
1065
1087
  });
1066
1088
  it("update the project extents", async () => {
@@ -1072,7 +1094,7 @@ describe("iModel", () => {
1072
1094
  newExtents.high.x += 1087;
1073
1095
  newExtents.high.y += 19;
1074
1096
  newExtents.high.z += .001;
1075
- imodel1.updateProjectExtents(newExtents);
1097
+ await withEditTxn(imodel1, async (txn) => txn.updateProjectExtents(newExtents));
1076
1098
  const updatedProps = imodel1[_nativeDb].getIModelProps();
1077
1099
  assert.isTrue(updatedProps.hasOwnProperty("projectExtents"), "Returned property JSON object has project extents");
1078
1100
  const updatedExtents = Range3d.fromJSON(updatedProps.projectExtents);
@@ -1264,26 +1286,28 @@ describe("iModel", () => {
1264
1286
  });
1265
1287
  it("should create and insert CodeSpecs", () => {
1266
1288
  const testImodel = imodel2;
1289
+ const txn = new EditTxn(testImodel, "create and insert CodeSpecs");
1290
+ txn.start();
1267
1291
  const codeSpec = CodeSpec.create(testImodel, "CodeSpec1", CodeScopeSpec.Type.Model);
1268
- const codeSpecId = testImodel.codeSpecs.insert(codeSpec); // throws in case of error
1292
+ const codeSpecId = testImodel.codeSpecs.insert(txn, codeSpec); // throws in case of error
1269
1293
  assert.deepEqual(codeSpecId, codeSpec.id);
1270
1294
  assert.equal(codeSpec.scopeType, CodeScopeSpec.Type.Model);
1271
1295
  assert.equal(codeSpec.scopeReq, CodeScopeSpec.ScopeRequirement.ElementId);
1272
1296
  // Should not be able to insert a duplicate.
1273
1297
  const codeSpecDup = CodeSpec.create(testImodel, "CodeSpec1", CodeScopeSpec.Type.Model);
1274
- assert.throws(() => testImodel.codeSpecs.insert(codeSpecDup), "CodeSpec already exists");
1298
+ assert.throws(() => testImodel.codeSpecs.insert(txn, codeSpecDup), "CodeSpec already exists");
1275
1299
  // We should be able to insert another CodeSpec with a different name.
1276
1300
  const codeSpec2 = CodeSpec.create(testImodel, "CodeSpec2", CodeScopeSpec.Type.Model, CodeScopeSpec.ScopeRequirement.FederationGuid);
1277
- const codeSpec2Id = testImodel.codeSpecs.insert(codeSpec2); // throws in case of error
1301
+ const codeSpec2Id = testImodel.codeSpecs.insert(txn, codeSpec2); // throws in case of error
1278
1302
  assert.deepEqual(codeSpec2Id, codeSpec2.id);
1279
1303
  assert.notDeepEqual(codeSpec2Id, codeSpecId);
1280
1304
  // make sure CodeScopeSpec.Type.Repository works
1281
1305
  const codeSpec3 = CodeSpec.create(testImodel, "CodeSpec3", CodeScopeSpec.Type.Repository, CodeScopeSpec.ScopeRequirement.FederationGuid);
1282
- const codeSpec3Id = testImodel.codeSpecs.insert(codeSpec3); // throws in case of error
1306
+ const codeSpec3Id = testImodel.codeSpecs.insert(txn, codeSpec3); // throws in case of error
1283
1307
  assert.notDeepEqual(codeSpec2Id, codeSpec3Id);
1284
1308
  const codeSpec4 = testImodel.codeSpecs.getById(codeSpec3Id);
1285
1309
  codeSpec4.name = "CodeSpec4";
1286
- const codeSpec4Id = testImodel.codeSpecs.insert(codeSpec4); // throws in case of error
1310
+ const codeSpec4Id = testImodel.codeSpecs.insert(txn, codeSpec4); // throws in case of error
1287
1311
  assert.notDeepEqual(codeSpec3Id, codeSpec4Id);
1288
1312
  assert.equal(codeSpec4.scopeType, CodeScopeSpec.Type.Repository);
1289
1313
  assert.equal(codeSpec4.scopeReq, CodeScopeSpec.ScopeRequirement.FederationGuid);
@@ -1299,6 +1323,7 @@ describe("iModel", () => {
1299
1323
  assert.isTrue(testImodel.codeSpecs.hasId(codeSpec3.id));
1300
1324
  assert.isTrue(testImodel.codeSpecs.hasId(codeSpec4.id));
1301
1325
  assert.isFalse(testImodel.codeSpecs.hasId(Id64.invalid));
1326
+ txn.end();
1302
1327
  });
1303
1328
  it("validate CodeSpec properties", async () => {
1304
1329
  const iModelFileName = IModelTestUtils.prepareOutputFile("IModel", "ReadWriteCodeSpec.bim");
@@ -1307,13 +1332,12 @@ describe("iModel", () => {
1307
1332
  if (true) {
1308
1333
  const iModelDb = IModelTestUtils.createSnapshotFromSeed(iModelFileName, IModelTestUtils.resolveAssetFile("CompatibilityTestSeed.bim"));
1309
1334
  const codeSpec = CodeSpec.create(iModelDb, codeSpecName, CodeScopeSpec.Type.Model, CodeScopeSpec.ScopeRequirement.FederationGuid);
1310
- const codeSpecId = iModelDb.codeSpecs.insert(codeSpec);
1335
+ const codeSpecId = withEditTxn(iModelDb, (txn) => iModelDb.codeSpecs.insert(txn, codeSpec));
1311
1336
  assert.isTrue(Id64.isValidId64(codeSpec.id));
1312
1337
  assert.equal(codeSpec.id, codeSpecId);
1313
1338
  assert.equal(codeSpec.name, codeSpecName);
1314
1339
  assert.equal(codeSpec.scopeType, CodeScopeSpec.Type.Model);
1315
1340
  assert.equal(codeSpec.scopeReq, CodeScopeSpec.ScopeRequirement.FederationGuid);
1316
- iModelDb.saveChanges();
1317
1341
  iModelDb.close();
1318
1342
  }
1319
1343
  // Reopen iModel (ensure CodeSpec cache is cleared) and reconfirm CodeSpec properties
@@ -1344,7 +1368,9 @@ describe("iModel", () => {
1344
1368
  });
1345
1369
  it("should do CRUD on models", () => {
1346
1370
  const testImodel = imodel2;
1347
- const [modeledElementId, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(testImodel, Code.createEmpty(), true);
1371
+ const txn = new EditTxn(testImodel, "CRUD on models");
1372
+ txn.start();
1373
+ const [modeledElementId, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(txn, Code.createEmpty(), true);
1348
1374
  const newModelPersist = testImodel.models.getModel(newModelId);
1349
1375
  // Check that it has the properties that we set.
1350
1376
  assert.equal(newModelPersist.classFullName, PhysicalModel.classFullName);
@@ -1352,15 +1378,15 @@ describe("iModel", () => {
1352
1378
  assert.deepEqual(newModelPersist.modeledElement.id, modeledElementId);
1353
1379
  // Update the model
1354
1380
  newModelPersist.isPrivate = false;
1355
- testImodel.models.updateModel(newModelPersist.toJSON());
1381
+ txn.updateModel(newModelPersist.toJSON());
1356
1382
  // ... and check that it updated the model in the db
1357
1383
  const newModelPersist2 = testImodel.models.getModel(newModelId);
1358
1384
  assert.isFalse(newModelPersist2.isPrivate);
1359
1385
  // Delete the model
1360
- testImodel.models.deleteModel(newModelId);
1386
+ txn.deleteModel(newModelId);
1361
1387
  // Test insertModel error handling
1362
1388
  try {
1363
- testImodel.models.insertModel({
1389
+ txn.insertModel({
1364
1390
  classFullName: DefinitionModel.classFullName,
1365
1391
  modeledElement: { id: "0x10000000bad" },
1366
1392
  });
@@ -1368,26 +1394,22 @@ describe("iModel", () => {
1368
1394
  catch (error) {
1369
1395
  assert.isTrue(error instanceof IModelError || error.iTwinErrorId !== undefined);
1370
1396
  }
1397
+ txn.end();
1371
1398
  });
1372
1399
  it("should create model with custom relationship to modeled element", async () => {
1373
1400
  const testImodel = imodel1;
1401
+ const txn = new EditTxn(testImodel, "custom relationship to modeled element");
1374
1402
  assert.doesNotThrow(() => testImodel.schemaContext.getSchemaItemSync("TestBim:TestModelModelsElement", EntityClass), "TestModelModelsElement is expected to be defined in TestBim.ecschema.xml");
1375
- let newModelId1;
1376
- let newModelId2;
1377
- let relClassName1;
1378
- let relClassName2;
1379
- if (true) {
1380
- const newPartition1 = IModelTestUtils.createAndInsertPhysicalPartition(testImodel, Code.createEmpty());
1381
- relClassName1 = "TestBim:TestModelModelsElement";
1382
- const modeledElementRef = new RelatedElement({ id: newPartition1, relClassName: relClassName1 });
1383
- newModelId1 = IModelTestUtils.createAndInsertPhysicalModel(testImodel, modeledElementRef);
1384
- assert.isTrue(Id64.isValidId64(newModelId1));
1385
- }
1386
- if (true) {
1387
- [, newModelId2] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(testImodel, Code.createEmpty());
1388
- const newModel2 = testImodel.models.getModel(newModelId2);
1389
- relClassName2 = newModel2.modeledElement.relClassName;
1390
- }
1403
+ txn.start();
1404
+ const newPartition1 = IModelTestUtils.createAndInsertPhysicalPartition(txn, Code.createEmpty());
1405
+ const relClassName1 = "TestBim:TestModelModelsElement";
1406
+ const modeledElementRef = new RelatedElement({ id: newPartition1, relClassName: relClassName1 });
1407
+ const newModelId1 = IModelTestUtils.createAndInsertPhysicalModel(txn, modeledElementRef);
1408
+ assert.isTrue(Id64.isValidId64(newModelId1));
1409
+ const [, newModelId2] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(txn, Code.createEmpty());
1410
+ txn.end();
1411
+ const newModel2 = testImodel.models.getModel(newModelId2);
1412
+ const relClassName2 = newModel2.modeledElement.relClassName;
1391
1413
  const model1 = testImodel.models.getModel(newModelId1);
1392
1414
  const model2 = testImodel.models.getModel(newModelId2);
1393
1415
  const foundRelClassName1 = model1.modeledElement.relClassName;
@@ -1398,11 +1420,12 @@ describe("iModel", () => {
1398
1420
  it("should create link table relationship instances", () => {
1399
1421
  const snapshotFile2 = IModelTestUtils.prepareOutputFile("IModel", "CreateLinkTable.bim");
1400
1422
  const testImodel = StandaloneDb.createEmpty(snapshotFile2, { rootSubject: { name: "test1" }, enableTransactions: true });
1401
- const elements = testImodel.elements;
1423
+ const txn = new EditTxn(testImodel, "link table relationship instances");
1424
+ txn.start();
1402
1425
  // Create a new physical model
1403
- const newModelId = PhysicalModel.insert(testImodel, IModel.rootSubjectId, "TestModel");
1426
+ const newModelId = PhysicalModel.insert(txn, IModel.rootSubjectId, "TestModel");
1404
1427
  // create a SpatialCategory
1405
- const spatialCategoryId = SpatialCategory.insert(testImodel, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorByName.darkRed }));
1428
+ const spatialCategoryId = SpatialCategory.insert(txn, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorByName.darkRed }));
1406
1429
  // Create a couple of physical elements.
1407
1430
  const elementProps = {
1408
1431
  classFullName: PhysicalObject.classFullName,
@@ -1410,16 +1433,16 @@ describe("iModel", () => {
1410
1433
  category: spatialCategoryId,
1411
1434
  code: Code.createEmpty(),
1412
1435
  };
1413
- const id0 = elements.insertElement(elementProps);
1414
- const id1 = elements.insertElement(elementProps);
1415
- const id2 = elements.insertElement(elementProps);
1436
+ const id0 = txn.insertElement(elementProps);
1437
+ const id1 = txn.insertElement(elementProps);
1438
+ const id2 = txn.insertElement(elementProps);
1416
1439
  const geometricModel = testImodel.models.getModel(newModelId);
1417
1440
  assert.throws(() => geometricModel.queryExtents()); // no geometry
1418
1441
  // Create grouping relationships from 0 to 1 and from 0 to 2
1419
1442
  const r1 = ElementGroupsMembers.create(testImodel, id0, id1, 1);
1420
- r1.insert();
1443
+ r1.id = txn.insertRelationship(r1.toJSON());
1421
1444
  const r2 = ElementGroupsMembers.create(testImodel, id0, id2);
1422
- r2.insert();
1445
+ r2.id = txn.insertRelationship(r2.toJSON());
1423
1446
  // Look up by id
1424
1447
  const g1 = ElementGroupsMembers.getInstance(testImodel, r1.id);
1425
1448
  const g2 = ElementGroupsMembers.getInstance(testImodel, r2.id);
@@ -1434,53 +1457,57 @@ describe("iModel", () => {
1434
1457
  assert.deepEqual(g1byst, g1);
1435
1458
  // Update relationship instance property
1436
1459
  r1.asAny.memberPriority = 2;
1437
- r1.update();
1460
+ txn.updateRelationship(r1.toJSON());
1438
1461
  const g11 = ElementGroupsMembers.getInstance(testImodel, r1.id);
1439
1462
  assert.equal(g11.memberPriority, 2, "g11.memberPriority");
1440
- testImodel.saveChanges("step 1");
1463
+ txn.saveChanges("step 1");
1441
1464
  // Delete relationship instance property
1442
- g11.delete();
1443
- testImodel.saveChanges("step 2");
1465
+ txn.deleteRelationship(g11.toJSON());
1466
+ txn.saveChanges("step 2");
1444
1467
  assert.throws(() => ElementGroupsMembers.getInstance(testImodel, r1.id), IModelError);
1445
- const d0 = elements.insertElement(elementProps);
1446
- const d1 = elements.insertElement(elementProps);
1468
+ const d0 = txn.insertElement(elementProps);
1469
+ const d1 = txn.insertElement(elementProps);
1447
1470
  const ede1 = ElementDrivesElement.create(testImodel, d0, d1, 0);
1448
- ede1.insert();
1449
- testImodel.saveChanges("step 3");
1450
- ede1.delete();
1451
- testImodel.saveChanges("step 4");
1471
+ ede1.id = txn.insertRelationship(ede1.toJSON());
1472
+ txn.saveChanges("step 3");
1473
+ txn.deleteRelationship(ede1.toJSON());
1474
+ txn.end("save", "step 4");
1452
1475
  testImodel.close();
1453
1476
  });
1454
1477
  it("should insert DefinitionSets", () => {
1455
1478
  const iModelFileName = IModelTestUtils.prepareOutputFile("IModel", "DefinitionSets.bim");
1456
1479
  const iModelDb = SnapshotDb.createEmpty(iModelFileName, { rootSubject: { name: "DefinitionSets" }, createClassViews: true });
1457
- const definitionContainerId = DefinitionContainer.insert(iModelDb, IModel.dictionaryId, Code.createEmpty());
1480
+ const txn = new EditTxn(iModelDb, "definition sets");
1481
+ txn.start();
1482
+ const definitionContainerId = DefinitionContainer.insert(txn, IModel.dictionaryId, Code.createEmpty());
1458
1483
  assert.exists(iModelDb.elements.getElement(definitionContainerId));
1459
1484
  assert.exists(iModelDb.models.getModel(definitionContainerId));
1460
- const categoryId1 = SpatialCategory.insert(iModelDb, definitionContainerId, "Category1", new SubCategoryAppearance());
1461
- const categoryId2 = SpatialCategory.insert(iModelDb, definitionContainerId, "Category2", new SubCategoryAppearance());
1462
- const categoryId3 = SpatialCategory.insert(iModelDb, definitionContainerId, "Category3", new SubCategoryAppearance());
1463
- const definitionGroupId = DefinitionGroup.create(iModelDb, definitionContainerId, Code.createEmpty()).insert();
1464
- DefinitionGroupGroupsDefinitions.insert(iModelDb, definitionGroupId, categoryId1);
1465
- DefinitionGroupGroupsDefinitions.insert(iModelDb, definitionGroupId, categoryId2);
1466
- DefinitionGroupGroupsDefinitions.insert(iModelDb, definitionGroupId, categoryId3);
1485
+ const categoryId1 = SpatialCategory.insert(txn, definitionContainerId, "Category1", new SubCategoryAppearance());
1486
+ const categoryId2 = SpatialCategory.insert(txn, definitionContainerId, "Category2", new SubCategoryAppearance());
1487
+ const categoryId3 = SpatialCategory.insert(txn, definitionContainerId, "Category3", new SubCategoryAppearance());
1488
+ const definitionGroupId = DefinitionGroup.create(iModelDb, definitionContainerId, Code.createEmpty()).insert(txn);
1489
+ DefinitionGroupGroupsDefinitions.insert(txn, definitionGroupId, categoryId1);
1490
+ DefinitionGroupGroupsDefinitions.insert(txn, definitionGroupId, categoryId2);
1491
+ DefinitionGroupGroupsDefinitions.insert(txn, definitionGroupId, categoryId3);
1467
1492
  // eslint-disable-next-line @typescript-eslint/no-deprecated
1468
1493
  const numMembers = iModelDb.withPreparedStatement(`SELECT COUNT(*) FROM ${DefinitionGroupGroupsDefinitions.classFullName}`, (statement) => {
1469
1494
  return statement.step() === DbResult.BE_SQLITE_ROW ? statement.getValue(0).getInteger() : 0;
1470
1495
  });
1471
1496
  assert.equal(numMembers, 3);
1472
- iModelDb.saveChanges();
1497
+ txn.end();
1473
1498
  iModelDb.close();
1474
1499
  });
1475
1500
  it("should set EC properties of various types", async () => {
1476
1501
  const testImodel = imodel1;
1477
1502
  assert.doesNotThrow(() => testImodel.schemaContext.getSchemaItemSync("TestBim:TestPhysicalObject", EntityClass), "TestPhysicalObject is expected to be defined in TestBim.ecschema.xml");
1503
+ const txn = new EditTxn(testImodel, "set EC properties of various types");
1504
+ txn.start();
1478
1505
  // Create a new physical model
1479
- const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(testImodel, Code.createEmpty(), true);
1506
+ const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(txn, Code.createEmpty(), true);
1480
1507
  // Find or create a SpatialCategory
1481
1508
  let spatialCategoryId = SpatialCategory.queryCategoryIdByName(testImodel, IModel.dictionaryId, "MySpatialCategory");
1482
1509
  if (undefined === spatialCategoryId) {
1483
- spatialCategoryId = SpatialCategory.insert(testImodel, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance());
1510
+ spatialCategoryId = SpatialCategory.insert(txn, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance());
1484
1511
  }
1485
1512
  const trelClassName = "TestBim:TestPhysicalObjectRelatedToTestPhysicalObject";
1486
1513
  let id1;
@@ -1493,14 +1520,14 @@ describe("iModel", () => {
1493
1520
  category: spatialCategoryId,
1494
1521
  code: Code.createEmpty(),
1495
1522
  };
1496
- id1 = testImodel.elements.insertElement(testImodel.elements.createElement(elementProps).toJSON());
1523
+ id1 = txn.insertElement(testImodel.elements.createElement(elementProps).toJSON());
1497
1524
  assert.isTrue(Id64.isValidId64(id1));
1498
1525
  // The second one should point to the first.
1499
1526
  elementProps.id = Id64.invalid;
1500
1527
  elementProps.relatedElement = { id: id1, relClassName: trelClassName };
1501
1528
  elementProps.parent = { id: id1, relClassName: trelClassName };
1502
1529
  elementProps.longProp = 4294967295; // make sure that we can save values in the range 0 ... UINT_MAX
1503
- id2 = testImodel.elements.insertElement(testImodel.elements.createElement(elementProps).toJSON());
1530
+ id2 = txn.insertElement(testImodel.elements.createElement(elementProps).toJSON());
1504
1531
  assert.isTrue(Id64.isValidId64(id2));
1505
1532
  }
1506
1533
  if (true) {
@@ -1519,7 +1546,7 @@ describe("iModel", () => {
1519
1546
  // Change el2 to point to itself.
1520
1547
  const el2Modified = testImodel.elements.getElement(id2);
1521
1548
  el2Modified.asAny.relatedElement = { id: id2, relClassName: trelClassName };
1522
- testImodel.elements.updateElement(el2Modified.toJSON());
1549
+ txn.updateElement(el2Modified.toJSON());
1523
1550
  // Test that el2 points to itself.
1524
1551
  const el2after = testImodel.elements.getElement(id2);
1525
1552
  assert.deepEqual(el2after.asAny.relatedElement.id, id2);
@@ -1529,11 +1556,12 @@ describe("iModel", () => {
1529
1556
  // Test that we can null out the navigation property
1530
1557
  const el2Modified = testImodel.elements.getElement(id2);
1531
1558
  el2Modified.asAny.relatedElement = null;
1532
- testImodel.elements.updateElement(el2Modified.toJSON());
1559
+ txn.updateElement(el2Modified.toJSON());
1533
1560
  // Test that el2 has no relatedElement property value
1534
1561
  const el2after = testImodel.elements.getElement(id2);
1535
1562
  assert.isUndefined(el2after.asAny.relatedElement);
1536
1563
  }
1564
+ txn.end();
1537
1565
  });
1538
1566
  it("should be able to create a snapshot IModel", async () => {
1539
1567
  const args = {
@@ -1559,23 +1587,25 @@ describe("iModel", () => {
1559
1587
  assert.equal(dbguid.byteLength, 16, "query guid property");
1560
1588
  const myPropsStr = { name: "MyProp", namespace: "test1", id: 1, subId: 1 };
1561
1589
  const myStrVal = "this is a test";
1562
- iModel.saveFileProperty(myPropsStr, myStrVal);
1563
- const readFromDb = iModel.queryFilePropertyString(myPropsStr);
1564
- assert.equal(readFromDb, myStrVal, "query string after save");
1565
1590
  const myPropsBlob = { name: "MyBlob", namespace: "test1", id: 10 };
1566
1591
  const testRange = new Uint8Array(500);
1567
1592
  testRange.fill(11);
1568
- iModel.saveFileProperty(myPropsBlob, undefined, testRange);
1569
- const blobFromDb = iModel.queryFilePropertyBlob(myPropsBlob);
1570
- assert.deepEqual(blobFromDb, testRange, "query blob after save");
1571
- let next = iModel.queryNextAvailableFileProperty(myPropsBlob);
1572
- assert.equal(11, next, "queryNextAvailableFileProperty blob");
1573
- next = iModel.queryNextAvailableFileProperty(myPropsStr);
1574
- assert.equal(2, next, "queryNextAvailableFileProperty str");
1575
- iModel.deleteFileProperty(myPropsStr);
1576
- assert.isUndefined(iModel.queryFilePropertyString(myPropsStr), "property was deleted");
1577
- next = iModel.queryNextAvailableFileProperty(myPropsStr);
1578
- assert.equal(0, next, "queryNextAvailableFileProperty, should return 0 when none present");
1593
+ withEditTxn(iModel, (txn) => {
1594
+ txn.saveFileProperty(myPropsStr, myStrVal);
1595
+ const readFromDb = iModel.queryFilePropertyString(myPropsStr);
1596
+ assert.equal(readFromDb, myStrVal, "query string after save");
1597
+ txn.saveFileProperty(myPropsBlob, undefined, testRange);
1598
+ const blobFromDb = iModel.queryFilePropertyBlob(myPropsBlob);
1599
+ assert.deepEqual(blobFromDb, testRange, "query blob after save");
1600
+ let next = iModel.queryNextAvailableFileProperty(myPropsBlob);
1601
+ assert.equal(11, next, "queryNextAvailableFileProperty blob");
1602
+ next = iModel.queryNextAvailableFileProperty(myPropsStr);
1603
+ assert.equal(2, next, "queryNextAvailableFileProperty str");
1604
+ txn.deleteFileProperty(myPropsStr);
1605
+ assert.isUndefined(iModel.queryFilePropertyString(myPropsStr), "property was deleted");
1606
+ next = iModel.queryNextAvailableFileProperty(myPropsStr);
1607
+ assert.equal(0, next, "queryNextAvailableFileProperty, should return 0 when none present");
1608
+ });
1579
1609
  const testLocal = "TestLocal";
1580
1610
  const testValue = "this is a test";
1581
1611
  const nativeDb = iModel[_nativeDb];
@@ -1638,8 +1668,9 @@ describe("iModel", () => {
1638
1668
  assert.isFalse(eventListenedTo);
1639
1669
  iModel.geographicCoordinateSystem = gcs;
1640
1670
  assert.isTrue(eventListenedTo);
1641
- iModel.updateIModelProps();
1642
- iModel.saveChanges();
1671
+ withEditTxn(iModel, (txn) => {
1672
+ txn.updateIModelProps();
1673
+ });
1643
1674
  iModel.close();
1644
1675
  const iModel2 = SnapshotDb.openFile(testFile);
1645
1676
  assert.isTrue(iModel2.geographicCoordinateSystem !== undefined);
@@ -1710,9 +1741,10 @@ describe("iModel", () => {
1710
1741
  datumOrGCS = datum;
1711
1742
  const testFile = IModelTestUtils.prepareOutputFile("IModel", fileName);
1712
1743
  const iModel = SnapshotDb.createEmpty(testFile, args);
1713
- iModel.setGeographicCoordinateSystem(fileGCS);
1714
- iModel.updateIModelProps();
1715
- iModel.saveChanges();
1744
+ withEditTxn(iModel, (txn) => {
1745
+ iModel.setGeographicCoordinateSystem(fileGCS);
1746
+ txn.updateIModelProps();
1747
+ });
1716
1748
  const testPoint1 = [];
1717
1749
  testPoint1.push(inputCoord);
1718
1750
  const requestProps1 = { target: datumOrGCS, iModelCoords: testPoint1 };
@@ -1969,8 +2001,9 @@ describe("iModel", () => {
1969
2001
  const iModel = SnapshotDb.createEmpty(testFile, args);
1970
2002
  assert.isTrue(iModel.ecefLocation === undefined);
1971
2003
  iModel.ecefLocation = ecef;
1972
- iModel.updateIModelProps();
1973
- iModel.saveChanges();
2004
+ withEditTxn(iModel, (txn) => {
2005
+ txn.updateIModelProps();
2006
+ });
1974
2007
  iModel.close();
1975
2008
  const iModel2 = SnapshotDb.openFile(testFile);
1976
2009
  assert.isTrue(iModel2.ecefLocation !== undefined);
@@ -1993,8 +2026,9 @@ describe("iModel", () => {
1993
2026
  const iModel = SnapshotDb.createEmpty(testFile, args);
1994
2027
  assert.isTrue(iModel.ecefLocation === undefined);
1995
2028
  iModel.ecefLocation = ecef;
1996
- iModel.updateIModelProps();
1997
- iModel.saveChanges();
2029
+ withEditTxn(iModel, (txn) => {
2030
+ txn.updateIModelProps();
2031
+ });
1998
2032
  iModel.close();
1999
2033
  const iModel2 = SnapshotDb.openFile(testFile);
2000
2034
  assert.isTrue(iModel2.ecefLocation !== undefined);
@@ -2048,16 +2082,18 @@ describe("iModel", () => {
2048
2082
  const testFile = IModelTestUtils.prepareOutputFile("IModel", "TestSnapshot4.bim");
2049
2083
  const iModel = SnapshotDb.createEmpty(testFile, args);
2050
2084
  iModel.ecefLocation = ecef;
2051
- iModel.updateIModelProps();
2052
- iModel.saveChanges();
2085
+ withEditTxn(iModel, (txn) => {
2086
+ txn.updateIModelProps();
2087
+ });
2053
2088
  iModel.close();
2054
2089
  const iModel2 = SnapshotDb.openForApplyChangesets(testFile);
2055
2090
  assert.isTrue(iModel2.ecefLocation !== undefined);
2056
2091
  assert.isTrue(iModel2.ecefLocation.isAlmostEqual(ecef));
2057
2092
  assert.isTrue(iModel2.geographicCoordinateSystem === undefined);
2058
2093
  iModel2.geographicCoordinateSystem = gcs;
2059
- iModel2.updateIModelProps();
2060
- iModel2.saveChanges();
2094
+ withEditTxn(iModel2, (txn) => {
2095
+ txn.updateIModelProps();
2096
+ });
2061
2097
  iModel2.close();
2062
2098
  const iModel3 = SnapshotDb.openFile(testFile);
2063
2099
  assert.isTrue(iModel3.geographicCoordinateSystem !== undefined);
@@ -2078,6 +2114,7 @@ describe("iModel", () => {
2078
2114
  getIModelId: () => iModelId,
2079
2115
  getITwinId: () => iTwinId,
2080
2116
  getCurrentChangeset: () => changeset,
2117
+ hasUnsavedChanges: () => false,
2081
2118
  setIModelDb: () => { },
2082
2119
  closeFile: () => { },
2083
2120
  clearECDbCache: () => { },
@@ -2164,20 +2201,22 @@ describe("iModel", () => {
2164
2201
  };
2165
2202
  const seconds = (s) => s * 1000;
2166
2203
  const db = StandaloneDb.createEmpty(standaloneFile, { rootSubject: { name: "Standalone" } });
2167
- db.saveChanges();
2204
+ const txn = new EditTxn(db, "busy timeout test");
2205
+ txn.start();
2168
2206
  // lock db so another connection cannot write to it.
2169
- db.saveFileProperty({ name: "test", namespace: "test" }, "");
2207
+ txn.saveFileProperty({ name: "test", namespace: "test" }, "");
2170
2208
  assert.isAtMost(tryOpen(standaloneFile, { busyTimeout: seconds(0) }), seconds(1), "open should fail with busy error instantly");
2171
2209
  assert.isAtLeast(tryOpen(standaloneFile, { busyTimeout: seconds(1) }), seconds(1), "open should fail with atleast 1 sec delay due to retry");
2172
2210
  assert.isAtLeast(tryOpen(standaloneFile, { busyTimeout: seconds(2) }), seconds(2), "open should fail with atleast 2 sec delay due to retry");
2173
2211
  assert.isAtLeast(tryOpen(standaloneFile, { busyTimeout: seconds(3) }), seconds(3), "open should fail with atleast 3 sec delay due to retry");
2174
- db.abandonChanges();
2212
+ txn.end("abandon");
2175
2213
  db.close();
2176
2214
  });
2177
2215
  it("Cache cleared on abandonChanges", () => {
2178
2216
  const standaloneFile = IModelTestUtils.prepareOutputFile("IModel", "StandaloneReadWrite.bim");
2179
2217
  const db = StandaloneDb.createEmpty(standaloneFile, { rootSubject: { name: "Standalone" } });
2180
- db.saveChanges();
2218
+ const txn = new EditTxn(db, "cache cleared on abandonChanges");
2219
+ txn.start();
2181
2220
  const code = Code.createEmpty();
2182
2221
  code.value = "foo";
2183
2222
  const props = {
@@ -2185,29 +2224,32 @@ describe("iModel", () => {
2185
2224
  model: IModel.dictionaryId,
2186
2225
  code,
2187
2226
  };
2188
- const id = db.elements.insertElement(props);
2227
+ const id = txn.insertElement(props);
2189
2228
  const element1 = db.elements.getElementProps(id);
2190
- db.abandonChanges();
2229
+ txn.end("abandon");
2191
2230
  code.value = "bar";
2192
2231
  const props2 = {
2193
2232
  classFullName: GenericGraphicalType2d.classFullName,
2194
2233
  model: IModel.dictionaryId,
2195
2234
  code,
2196
2235
  };
2197
- const id2 = db.elements.insertElement(props2);
2236
+ const retryTxn = new EditTxn(db, "cache cleared on abandonChanges retry");
2237
+ retryTxn.start();
2238
+ const id2 = retryTxn.insertElement(props2);
2198
2239
  expect(id2).to.equal(id);
2199
2240
  const element2 = db.elements.getElementProps(id2);
2200
2241
  expect(element2).to.not.equal(element1);
2201
2242
  // Make sure that the statement caches are not cleared
2202
2243
  expect(db._sqliteStatementCache.size).to.be.greaterThan(0);
2203
2244
  expect(db._statementCache.size).to.be.greaterThan(0);
2204
- db.abandonChanges();
2245
+ retryTxn.end("abandon");
2205
2246
  db.close();
2206
2247
  });
2207
2248
  it("Only instance caches should be cleared with clearCaches instanceCachesOnly parameter", () => {
2208
2249
  const standaloneFile = IModelTestUtils.prepareOutputFile("IModel", "StandaloneReadWrite.bim");
2209
2250
  const db = StandaloneDb.createEmpty(standaloneFile, { rootSubject: { name: "Standalone" } });
2210
- db.saveChanges();
2251
+ const txn = new EditTxn(db, "clearCaches instanceCachesOnly");
2252
+ txn.start();
2211
2253
  const code = Code.createEmpty();
2212
2254
  code.value = "foo";
2213
2255
  const props = {
@@ -2215,7 +2257,7 @@ describe("iModel", () => {
2215
2257
  model: IModel.dictionaryId,
2216
2258
  code,
2217
2259
  };
2218
- const id = db.elements.insertElement(props);
2260
+ const id = txn.insertElement(props);
2219
2261
  db.elements.getElementProps(id);
2220
2262
  db.models.getModelProps(IModel.dictionaryId);
2221
2263
  expect(db.elements[_cache].size).to.be.greaterThan(0);
@@ -2230,7 +2272,7 @@ describe("iModel", () => {
2230
2272
  // Make sure that the statement caches are not cleared
2231
2273
  expect(db._sqliteStatementCache.size).to.be.greaterThan(0);
2232
2274
  expect(db._statementCache.size).to.be.greaterThan(0);
2233
- db.abandonChanges();
2275
+ txn.end("abandon");
2234
2276
  db.close();
2235
2277
  });
2236
2278
  it("Standalone iModel properties", () => {
@@ -2371,28 +2413,29 @@ describe("iModel", () => {
2371
2413
  iModel.close();
2372
2414
  });
2373
2415
  it("Run plain SQL", () => {
2374
- imodel1.withPreparedSqliteStatement("CREATE TABLE Test(Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Code INTEGER)", (stmt) => {
2375
- assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2376
- });
2377
- imodel1.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(?,?)", (stmt) => {
2378
- stmt.bindValue(1, "Dummy 1");
2379
- stmt.bindValue(2, 100);
2380
- assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2381
- });
2382
- imodel1.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(?,?)", (stmt) => {
2383
- stmt.bindValues(["Dummy 2", 200]);
2384
- assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2385
- });
2386
- imodel1.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(:p1,:p2)", (stmt) => {
2387
- stmt.bindValue(":p1", "Dummy 3");
2388
- stmt.bindValue(":p2", 300);
2389
- assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2390
- });
2391
- imodel1.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(:p1,:p2)", (stmt) => {
2392
- stmt.bindValues({ ":p1": "Dummy 4", ":p2": 400 });
2393
- assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2416
+ withEditTxn(imodel1, () => {
2417
+ imodel1.withPreparedSqliteStatement("CREATE TABLE Test(Id INTEGER PRIMARY KEY, Name TEXT NOT NULL, Code INTEGER)", (stmt) => {
2418
+ assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2419
+ });
2420
+ imodel1.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(?,?)", (stmt) => {
2421
+ stmt.bindValue(1, "Dummy 1");
2422
+ stmt.bindValue(2, 100);
2423
+ assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2424
+ });
2425
+ imodel1.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(?,?)", (stmt) => {
2426
+ stmt.bindValues(["Dummy 2", 200]);
2427
+ assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2428
+ });
2429
+ imodel1.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(:p1,:p2)", (stmt) => {
2430
+ stmt.bindValue(":p1", "Dummy 3");
2431
+ stmt.bindValue(":p2", 300);
2432
+ assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2433
+ });
2434
+ imodel1.withPreparedSqliteStatement("INSERT INTO Test(Name,Code) VALUES(:p1,:p2)", (stmt) => {
2435
+ stmt.bindValues({ ":p1": "Dummy 4", ":p2": 400 });
2436
+ assert.equal(stmt.step(), DbResult.BE_SQLITE_DONE);
2437
+ });
2394
2438
  });
2395
- imodel1.saveChanges();
2396
2439
  imodel1.withPreparedSqliteStatement("SELECT Id,Name,Code FROM Test ORDER BY Id", (stmt) => {
2397
2440
  for (let i = 1; i <= 4; i++) {
2398
2441
  assert.equal(stmt.step(), DbResult.BE_SQLITE_ROW);
@@ -2501,7 +2544,9 @@ describe("iModel", () => {
2501
2544
  assert.isFalse(imodel1.containsClass("InvalidSchemaName:Element"));
2502
2545
  });
2503
2546
  it("should update Element code", () => {
2504
- const elementId = imodel4.elements.insertElement({
2547
+ const txn = new EditTxn(imodel4, "update element code");
2548
+ txn.start();
2549
+ const elementId = txn.insertElement({
2505
2550
  classFullName: "DgnPlatformTest:TestInformationRecord",
2506
2551
  model: IModel.repositoryModelId,
2507
2552
  code: Code.createEmpty(),
@@ -2509,16 +2554,19 @@ describe("iModel", () => {
2509
2554
  let element = imodel4.elements.getElement(elementId, InformationRecordElement);
2510
2555
  assert.isTrue(Code.isValid(element.code));
2511
2556
  assert.isTrue(Code.isEmpty(element.code));
2512
- const codeSpecId = imodel4.codeSpecs.insert("TestCodeSpec", CodeScopeSpec.Type.Model);
2557
+ const codeSpecId = imodel4.codeSpecs.insert(txn, "TestCodeSpec", CodeScopeSpec.Type.Model);
2513
2558
  const codeValue = `${element.className}-1`;
2514
2559
  element.code = new Code({ spec: codeSpecId, scope: IModel.repositoryModelId, value: codeValue });
2515
- element.update();
2560
+ element.update(txn);
2561
+ txn.end();
2516
2562
  element = imodel4.elements.getElement(elementId, InformationRecordElement);
2517
2563
  assert.isTrue(Code.isValid(element.code));
2518
2564
  assert.isFalse(Code.isEmpty(element.code));
2519
2565
  assert.equal(element.code.value, codeValue);
2520
2566
  });
2521
2567
  it("should update UserLabel", () => {
2568
+ const txn = new EditTxn(imodel1, "update user label");
2569
+ txn.start();
2522
2570
  // type coercion reminder!
2523
2571
  const s = "";
2524
2572
  assert.isTrue(s === "");
@@ -2529,16 +2577,16 @@ describe("iModel", () => {
2529
2577
  model: IModel.dictionaryId,
2530
2578
  code: SpatialCategory.createCode(imodel1, IModel.dictionaryId, "TestCategoryForClearUserLabel"),
2531
2579
  };
2532
- const elementId = imodel1.elements.insertElement(elementProps);
2580
+ const elementId = txn.insertElement(elementProps);
2533
2581
  let element = imodel1.elements.getElement(elementId);
2534
2582
  assert.isUndefined(element.userLabel);
2535
2583
  // update element with a defined userLabel
2536
2584
  element.userLabel = "UserLabel";
2537
- element.update();
2585
+ element.update(txn);
2538
2586
  element = imodel1.elements.getElement(elementId);
2539
2587
  assert.equal(element.userLabel, "UserLabel");
2540
2588
  // make sure userLabel is not updated when not part of the specified ElementProps
2541
- imodel1.elements.updateElement({
2589
+ txn.updateElement({
2542
2590
  id: element.id,
2543
2591
  classFullName: element.classFullName,
2544
2592
  model: element.model,
@@ -2558,11 +2606,14 @@ describe("iModel", () => {
2558
2606
  expect(elProps.isInstanceOfEntity).undefined;
2559
2607
  // remove userlabel by setting it to the blank string
2560
2608
  element.userLabel = "";
2561
- element.update();
2609
+ element.update(txn);
2610
+ txn.end();
2562
2611
  element = imodel1.elements.getElement(elementId);
2563
2612
  assert.isUndefined(element.userLabel); // NOTE: userLabel is cleared when the empty string is specified
2564
2613
  });
2565
2614
  it("should update FederationGuid", () => {
2615
+ const txn = new EditTxn(imodel1, "update federation guid");
2616
+ txn.start();
2566
2617
  // insert element with an undefined FederationGuid
2567
2618
  const elementProps = {
2568
2619
  classFullName: SpatialCategory.classFullName,
@@ -2570,7 +2621,7 @@ describe("iModel", () => {
2570
2621
  federationGuid: Guid.empty,
2571
2622
  code: SpatialCategory.createCode(imodel1, IModel.dictionaryId, "TestCategoryForClearFederationGuid"),
2572
2623
  };
2573
- const elementId = imodel1.elements.insertElement(elementProps);
2624
+ const elementId = txn.insertElement(elementProps);
2574
2625
  let element = imodel1.elements.getElement(elementId);
2575
2626
  assert.isUndefined(element.federationGuid);
2576
2627
  assert.isFalse(element.isPrivate);
@@ -2578,12 +2629,12 @@ describe("iModel", () => {
2578
2629
  const federationGuid = Guid.createValue();
2579
2630
  element.federationGuid = federationGuid;
2580
2631
  element.isPrivate = true;
2581
- element.update();
2632
+ element.update(txn);
2582
2633
  element = imodel1.elements.getElement(elementId);
2583
2634
  assert.equal(element.federationGuid, federationGuid);
2584
2635
  assert.isTrue(element.isPrivate);
2585
2636
  // make sure FederationGuid is not updated when not part of the specified ElementProps
2586
- imodel1.elements.updateElement({
2637
+ txn.updateElement({
2587
2638
  id: element.id,
2588
2639
  classFullName: element.classFullName,
2589
2640
  model: element.model,
@@ -2595,20 +2646,23 @@ describe("iModel", () => {
2595
2646
  // remove federationGuid by setting it to undefined in ElementProps
2596
2647
  const elProps = element.toJSON();
2597
2648
  elProps.federationGuid = undefined;
2598
- imodel1.elements.updateElement(elProps);
2649
+ txn.updateElement(elProps);
2599
2650
  element = imodel1.elements.getElement(elementId);
2600
2651
  assert.isUndefined(element.federationGuid);
2601
2652
  // ensure that update doesn't change federationGuid from an element immediately after insert (toJSON should remove undefined value)
2602
2653
  const subject5 = Subject.create(imodel1, IModel.rootSubjectId, "Subject5");
2603
- const s5Id = subject5.insert();
2654
+ const s5Id = subject5.insert(txn);
2604
2655
  const s5pre = imodel1.elements.getElement(s5Id);
2605
2656
  subject5.description = "new descr";
2606
- subject5.update();
2657
+ subject5.update(txn);
2658
+ txn.end();
2607
2659
  const s5post = imodel1.elements.getElement(s5Id);
2608
2660
  expect(s5pre.federationGuid).equal(s5post.federationGuid);
2609
2661
  expect(s5post.description).equal(subject5.description);
2610
2662
  });
2611
2663
  it("should support partial update", () => {
2664
+ const txn = new EditTxn(imodel1, "partial element update");
2665
+ txn.start();
2612
2666
  // Insert Subject elements - initializing Description and UserLabel to similar values
2613
2667
  let subject1 = Subject.create(imodel1, IModel.rootSubjectId, "Subject1", "Description1");
2614
2668
  let subject2 = Subject.create(imodel1, IModel.rootSubjectId, "Subject2", "Description2");
@@ -2624,10 +2678,10 @@ describe("iModel", () => {
2624
2678
  subject2.federationGuid = federationGuid2;
2625
2679
  subject3.federationGuid = "";
2626
2680
  subject4.federationGuid = Guid.empty;
2627
- const subjectId1 = subject1.insert();
2628
- const subjectId2 = subject2.insert();
2629
- const subjectId3 = subject3.insert();
2630
- const subjectId4 = subject4.insert();
2681
+ const subjectId1 = subject1.insert(txn);
2682
+ const subjectId2 = subject2.insert(txn);
2683
+ const subjectId3 = subject3.insert(txn);
2684
+ const subjectId4 = subject4.insert(txn);
2631
2685
  subject1 = imodel1.elements.getElement(subjectId1, Subject);
2632
2686
  subject2 = imodel1.elements.getElement(subjectId2, Subject);
2633
2687
  subject3 = imodel1.elements.getElement(subjectId3, Subject);
@@ -2652,7 +2706,7 @@ describe("iModel", () => {
2652
2706
  assert.equal(subject2.federationGuid, federationGuid2);
2653
2707
  assert.isUndefined(subject4.federationGuid);
2654
2708
  // test partial update of Description (auto-handled)
2655
- imodel1.elements.updateElement({ id: subject1.id, description: "Description1-Updated" });
2709
+ txn.updateElement({ id: subject1.id, description: "Description1-Updated" });
2656
2710
  subject1 = imodel1.elements.getElement(subjectId1, Subject);
2657
2711
  assert.equal(subject1.description, "Description1-Updated"); // should have been updated
2658
2712
  assert.isDefined(subject1.model);
@@ -2661,7 +2715,7 @@ describe("iModel", () => {
2661
2715
  assert.equal(subject1.userLabel, "UserLabel1"); // should not have changed
2662
2716
  assert.equal(subject1.federationGuid, federationGuid1); // should not have changed
2663
2717
  // test partial update of UserLabel (custom-handled)
2664
- imodel1.elements.updateElement({ id: subject2.id, userLabel: "UserLabel2-Updated" });
2718
+ txn.updateElement({ id: subject2.id, userLabel: "UserLabel2-Updated" });
2665
2719
  subject2 = imodel1.elements.getElement(subjectId2, Subject);
2666
2720
  assert.isDefined(subject2.model);
2667
2721
  assert.isDefined(subject2.parent);
@@ -2677,10 +2731,10 @@ describe("iModel", () => {
2677
2731
  subject2.userLabel = "";
2678
2732
  subject3.userLabel = "UserLabel3";
2679
2733
  subject4.userLabel = "UserLabel4";
2680
- subject1.update();
2681
- subject2.update();
2682
- subject3.update();
2683
- subject4.update();
2734
+ subject1.update(txn);
2735
+ subject2.update(txn);
2736
+ subject3.update(txn);
2737
+ subject4.update(txn);
2684
2738
  subject1 = imodel1.elements.getElement(subjectId1, Subject);
2685
2739
  subject2 = imodel1.elements.getElement(subjectId2, Subject);
2686
2740
  subject3 = imodel1.elements.getElement(subjectId3, Subject);
@@ -2696,7 +2750,7 @@ describe("iModel", () => {
2696
2750
  assert.equal(subject4.userLabel, "UserLabel4");
2697
2751
  // test partial update of Description to undefined
2698
2752
  const s3Fed = subject3.federationGuid;
2699
- imodel1.elements.updateElement({ id: subject3.id, description: undefined });
2753
+ txn.updateElement({ id: subject3.id, description: undefined });
2700
2754
  subject3 = imodel1.elements.getElement(subjectId3, Subject);
2701
2755
  assert.isUndefined(subject3.description); // should have been updated
2702
2756
  assert.isDefined(subject3.model);
@@ -2705,7 +2759,8 @@ describe("iModel", () => {
2705
2759
  assert.equal(subject3.userLabel, "UserLabel3"); // should not have changed
2706
2760
  assert.equal(subject3.federationGuid, s3Fed); // should not have changed
2707
2761
  // test partial update of UserLabel to undefined
2708
- imodel1.elements.updateElement({ id: subject4.id, userLabel: undefined });
2762
+ txn.updateElement({ id: subject4.id, userLabel: undefined });
2763
+ txn.end();
2709
2764
  subject4 = imodel1.elements.getElement(subjectId4, Subject);
2710
2765
  assert.isDefined(subject4.model);
2711
2766
  assert.isDefined(subject4.parent);
@@ -2717,6 +2772,8 @@ describe("iModel", () => {
2717
2772
  it('should allow untrimmed codes when using "exact" codeValueBehavior', () => {
2718
2773
  const imodelPath = IModelTestUtils.prepareOutputFile("IModel", "codeValueBehavior.bim");
2719
2774
  const imodel = SnapshotDb.createEmpty(imodelPath, { rootSubject: { name: "codeValueBehaviors" } });
2775
+ const txn = new EditTxn(imodel, "codeValueBehavior");
2776
+ txn.start();
2720
2777
  const getNumberedCodeValAndProps = (n) => {
2721
2778
  const trimmedCodeVal = `CodeValue${n}`;
2722
2779
  const untrimmedCodeVal = `${trimmedCodeVal}\xa0`;
@@ -2731,22 +2788,25 @@ describe("iModel", () => {
2731
2788
  };
2732
2789
  expect(imodel.codeValueBehavior).to.equal("trim-unicode-whitespace");
2733
2790
  const code1 = getNumberedCodeValAndProps(1);
2734
- const categ1Id = imodel.elements.insertElement(code1.props);
2791
+ const categ1Id = txn.insertElement(code1.props);
2735
2792
  const categ1 = imodel.elements.getElementProps({ id: categ1Id });
2736
2793
  expect(categ1.code.value).to.equal(code1.trimmedCodeVal);
2737
2794
  imodel.codeValueBehavior = "exact";
2738
2795
  const code2 = getNumberedCodeValAndProps(2);
2739
- const categ2Id = imodel.elements.insertElement(code2.props);
2796
+ const categ2Id = txn.insertElement(code2.props);
2740
2797
  const categ2 = imodel.elements.getElementProps({ id: categ2Id });
2741
2798
  expect(categ2.code.value).to.equal(code2.untrimmedCodeVal);
2742
2799
  imodel.codeValueBehavior = "trim-unicode-whitespace";
2743
2800
  const code3 = getNumberedCodeValAndProps(3);
2744
- const categ3Id = imodel.elements.insertElement(code3.props);
2801
+ const categ3Id = txn.insertElement(code3.props);
2745
2802
  const categ3 = imodel.elements.getElement({ id: categ3Id });
2746
2803
  expect(categ3.code.value).to.equal(code3.trimmedCodeVal);
2804
+ txn.end();
2747
2805
  imodel.close();
2748
2806
  });
2749
2807
  it("should throw iTwinErrors on element CRUD opertion fails", async () => {
2808
+ const txn = new EditTxn(imodel1, "element CRUD failure cases");
2809
+ txn.start();
2750
2810
  const code = Code.createEmpty();
2751
2811
  code.value = "foo";
2752
2812
  const props = {
@@ -2754,23 +2814,24 @@ describe("iModel", () => {
2754
2814
  model: IModel.dictionaryId,
2755
2815
  code,
2756
2816
  };
2757
- imodel1.elements.insertElement(props);
2758
- expect(() => imodel1.elements.insertElement(props)).throws("Error inserting element [duplicate code]").to.have.property("iTwinErrorId");
2817
+ txn.insertElement(props);
2818
+ expect(() => txn.insertElement(props)).throws("Error inserting element [duplicate code]").to.have.property("iTwinErrorId");
2759
2819
  const updateProps = {
2760
2820
  id: Id64.fromString("0x111111"),
2761
2821
  classFullName: GenericGraphicalType2d.classFullName,
2762
2822
  model: IModel.dictionaryId,
2763
2823
  code,
2764
2824
  };
2765
- expect(() => imodel1.elements.updateElement(updateProps)).throws(`Error updating element [missing id], id: ${updateProps.id}`).to.have.property("iTwinErrorId");
2766
- expect(() => imodel1.elements.deleteElement(updateProps.id)).throws(`Error deleting element [missing id], id: ${updateProps.id}`).to.have.property("iTwinErrorId");
2767
- expect(() => imodel1.models.insertModel({ classFullName: DefinitionModel.classFullName, modeledElement: { id: "0x10000000bad" } })).throws("Error inserting model [error=10004], class=BisCore:DefinitionModel").to.have.property("iTwinErrorId");
2768
- expect(() => imodel1.models.updateModel({
2825
+ expect(() => txn.updateElement(updateProps)).throws(`Error updating element [missing id], id: ${updateProps.id}`).to.have.property("iTwinErrorId");
2826
+ expect(() => txn.deleteElement(updateProps.id)).throws(`Error deleting element [missing id], id: ${updateProps.id}`).to.have.property("iTwinErrorId");
2827
+ expect(() => txn.insertModel({ classFullName: DefinitionModel.classFullName, modeledElement: { id: "0x10000000bad" } })).throws("Error inserting model [error=10004], class=BisCore:DefinitionModel").to.have.property("iTwinErrorId");
2828
+ expect(() => txn.updateModel({
2769
2829
  id: Id64.fromString("0x111111"),
2770
2830
  modeledElement: { id: Id64.fromString("0x111111") },
2771
2831
  classFullName: ""
2772
2832
  })).throws(`Error updating model [missing id], id: ${Id64.fromString("0x111111")}`).to.have.property("iTwinErrorId");
2773
- expect(() => imodel1.models.deleteModel(Id64.fromString("0x111111"))).throws(`Error deleting model [missing id], id: ${Id64.fromString("0x111111")}`).to.have.property("iTwinErrorId");
2833
+ expect(() => txn.deleteModel(Id64.fromString("0x111111"))).throws(`Error deleting model [missing id], id: ${Id64.fromString("0x111111")}`).to.have.property("iTwinErrorId");
2834
+ txn.end("abandon");
2774
2835
  });
2775
2836
  it("throws NotFound when attempting to access element props after closing the iModel", () => {
2776
2837
  const imodelPath = IModelTestUtils.prepareOutputFile("IModel", "accessAfterClose.bim");
@@ -2783,11 +2844,12 @@ describe("iModel", () => {
2783
2844
  it("should throw \"constraint failed (BE_SQLITE_CONSTRAINT_UNIQUE)\" when inserting a relationsip instance with the same prop twice", () => {
2784
2845
  const imodelPath = IModelTestUtils.prepareOutputFile("IModel", "insertDuplicateInstance.bim");
2785
2846
  const imodel = SnapshotDb.createEmpty(imodelPath, { rootSubject: { name: "insertDuplicateInstance" } });
2786
- const elements = imodel.elements;
2847
+ const txn = new EditTxn(imodel, "insert duplicate relationship instance");
2848
+ txn.start();
2787
2849
  // Create a new physical model
2788
- const newModelId = PhysicalModel.insert(imodel, IModel.rootSubjectId, "TestModel");
2850
+ const newModelId = PhysicalModel.insert(txn, IModel.rootSubjectId, "TestModel");
2789
2851
  // create a SpatialCategory
2790
- const spatialCategoryId = SpatialCategory.insert(imodel, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorByName.darkRed }));
2852
+ const spatialCategoryId = SpatialCategory.insert(txn, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorByName.darkRed }));
2791
2853
  // Create a couple of physical elements.
2792
2854
  const elementProps = {
2793
2855
  classFullName: PhysicalObject.classFullName,
@@ -2795,16 +2857,17 @@ describe("iModel", () => {
2795
2857
  category: spatialCategoryId,
2796
2858
  code: Code.createEmpty(),
2797
2859
  };
2798
- const id0 = elements.insertElement(elementProps);
2799
- const id1 = elements.insertElement(elementProps);
2860
+ const id0 = txn.insertElement(elementProps);
2861
+ const id1 = txn.insertElement(elementProps);
2800
2862
  const props = {
2801
2863
  classFullName: "BisCore:ElementGroupsMembers",
2802
2864
  sourceId: id0,
2803
2865
  targetId: id1,
2804
2866
  memberPriority: 1,
2805
2867
  };
2806
- imodel.relationships.insertInstance(props);
2807
- expect(() => imodel.relationships.insertInstance(props)).to.throw(`Failed to insert relationship [${imodelPath}]: rc=2067, constraint failed (BE_SQLITE_CONSTRAINT_UNIQUE)`);
2868
+ txn.insertRelationship(props);
2869
+ expect(() => txn.insertRelationship(props)).to.throw(`Failed to insert relationship [${imodelPath}]: rc=2067, constraint failed (BE_SQLITE_CONSTRAINT_UNIQUE)`);
2870
+ txn.end("abandon");
2808
2871
  imodel.close();
2809
2872
  });
2810
2873
  function createElemProps(_imodel, modId, catId, className) {
@@ -2817,11 +2880,11 @@ describe("iModel", () => {
2817
2880
  };
2818
2881
  return elementProps;
2819
2882
  }
2820
- function insertElement(imodel, mId, cId, cName, propName) {
2883
+ function insertElement(imodel, mId, cId, cName, propName, txn) {
2821
2884
  const elementProps = createElemProps(imodel, mId, cId, cName);
2822
2885
  const geomElement = imodel.elements.createElement(elementProps);
2823
2886
  geomElement.name = propName; // Add a custom property to the element
2824
- const id = imodel.elements.insertElement(geomElement.toJSON());
2887
+ const id = txn.insertElement(geomElement.toJSON());
2825
2888
  assert.isTrue(Id64.isValidId64(id), "insert failed");
2826
2889
  return id;
2827
2890
  }
@@ -2893,15 +2956,14 @@ describe("iModel", () => {
2893
2956
  // Ensure ADrivesB table is empty before test
2894
2957
  validateADrivesBRowCount(testImodel, 0);
2895
2958
  // Create a physical model and spatial category if needed
2896
- const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(testImodel, Code.createEmpty(), true);
2897
- let spatialCategoryId = SpatialCategory.queryCategoryIdByName(testImodel, IModel.dictionaryId, "MySpatialCategory");
2898
- if (!spatialCategoryId) {
2899
- spatialCategoryId = SpatialCategory.insert(testImodel, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorDef.fromString("rgb(255,0,0)").toJSON() }));
2900
- }
2901
- // Insert a ChildB element to be referenced by ChildA
2902
- const idB = insertElement(testImodel, newModelId, spatialCategoryId, "TestRelationSchema:ChildB", "ChildBElement");
2959
+ const setupTxn = new EditTxn(testImodel, "setup invalid relationship class test");
2960
+ setupTxn.start();
2961
+ const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(setupTxn, Code.createEmpty(), true);
2962
+ const spatialCategoryId = SpatialCategory.queryCategoryIdByName(testImodel, IModel.dictionaryId, "MySpatialCategory")
2963
+ ?? SpatialCategory.insert(setupTxn, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance({ color: ColorDef.fromString("rgb(255,0,0)").toJSON() }));
2964
+ const idB = insertElement(testImodel, newModelId, spatialCategoryId, "TestRelationSchema:ChildB", "ChildBElement", setupTxn);
2965
+ setupTxn.end();
2903
2966
  assert.isTrue(Id64.isValidId64(idB), "Insert ChildBElement failed");
2904
- testImodel.saveChanges();
2905
2967
  // Prepare base props for ChildA
2906
2968
  const elementProps = createElemProps(testImodel, newModelId, spatialCategoryId, "TestRelationSchema:ChildA");
2907
2969
  // Test various relationship class names for navigation property
@@ -2913,52 +2975,57 @@ describe("iModel", () => {
2913
2975
  { name: "trs:CIsRelatedToD", shouldSucceed: false, expectedRows: 0 },
2914
2976
  ];
2915
2977
  for (const { name, shouldSucceed, expectedRows } of testCases) {
2916
- const elemRef = new RelatedElement({ id: idB, relClassName: name });
2917
- elementProps.navPropChildB = elemRef;
2918
- elementProps.name = "ChildAElement";
2919
- const geomElement = testImodel.elements.createElement(elementProps);
2920
- let idA;
2978
+ const txn = new EditTxn(testImodel, `invalid relationship class ${name}`);
2979
+ txn.start();
2921
2980
  try {
2922
- idA = testImodel.elements.insertElement(geomElement.toJSON());
2923
- if (shouldSucceed)
2924
- assert.isTrue(Id64.isValidId64(idA), `Insert should have succeeded for ${name}.`);
2925
- else
2926
- assert.fail(`Insert should have failed for ${name}.`);
2927
- }
2928
- catch (err) {
2929
- if (shouldSucceed)
2930
- assert.fail(`Insert should have succeeded for ${name}. Error: ${err.message}`);
2931
- // If should not succeed, error is expected
2932
- }
2933
- // Validate row count in ADrivesB table
2934
- validateADrivesBRowCount(testImodel, expectedRows);
2935
- // If insert succeeded, test update and delete scenarios
2936
- if (expectedRows === 1 && idA !== undefined) {
2937
- validateNavProp(testImodel, { id: idB, relClassName: "TestRelationSchema.ADrivesB" });
2938
- const editElem = testImodel.elements.getElement(idA);
2939
- editElem.navPropChildB = new RelatedElement({ id: idB, relClassName: "trs.FakeClass" });
2940
- editElem.name = "ChildAElementUpdated";
2941
- testImodel.elements.updateElement(editElem);
2942
- validateADrivesBRowCount(testImodel, 1);
2943
- validateNavProp(testImodel, { id: idB, relClassName: "TestRelationSchema.ADrivesB" });
2944
- const editedElem = testImodel.elements.getElement(idA);
2945
- assert.equal(editedElem.name, "ChildAElementUpdated", `Expected name to be "ChildAElementUpdated" after update, but got "${editedElem.name}"`);
2946
- assert.strictEqual(editedElem.navPropChildB.relClassName, "TestRelationSchema.ADrivesB", `Expected navPropChildB to be "TestRelationSchema.ADrivesB" after update, but got "${editedElem.navPropChildB}"`);
2947
- // Set the nav prop value to null
2948
- editElem.name = "ChildAElementNulled";
2949
- editElem.navPropChildB = null;
2950
- testImodel.elements.updateElement(editElem);
2951
- validateADrivesBRowCount(testImodel, 0);
2952
- const nulledElem = testImodel.elements.getElement(idA);
2953
- assert.equal(nulledElem.name, "ChildAElementNulled", `Expected name to be "ChildAElementNulled" after nulling, but got "${nulledElem.name}"`);
2954
- assert.isUndefined(nulledElem.navPropChildB, `Expected navPropChildB to be undefined after nulling, but got "${nulledElem.navPropChildB}"`);
2955
- if (shouldSucceed) {
2956
- // Delete the element
2957
- testImodel.elements.deleteElement(idA);
2958
- assert.isUndefined(testImodel.elements.tryGetElement(idA), `Expected element with id ${idA} to be deleted, but it still exists.`);
2981
+ const elemRef = new RelatedElement({ id: idB, relClassName: name });
2982
+ elementProps.navPropChildB = elemRef;
2983
+ elementProps.name = "ChildAElement";
2984
+ const geomElement = testImodel.elements.createElement(elementProps);
2985
+ let idA;
2986
+ try {
2987
+ idA = txn.insertElement(geomElement.toJSON());
2988
+ if (shouldSucceed)
2989
+ assert.isTrue(Id64.isValidId64(idA), `Insert should have succeeded for ${name}.`);
2990
+ else
2991
+ assert.fail(`Insert should have failed for ${name}.`);
2959
2992
  }
2993
+ catch (err) {
2994
+ if (shouldSucceed)
2995
+ assert.fail(`Insert should have succeeded for ${name}. Error: ${err.message}`);
2996
+ // If should not succeed, error is expected
2997
+ }
2998
+ // Validate row count in ADrivesB table
2999
+ validateADrivesBRowCount(testImodel, expectedRows);
3000
+ // If insert succeeded, test update and delete scenarios
3001
+ if (expectedRows === 1 && idA !== undefined) {
3002
+ validateNavProp(testImodel, { id: idB, relClassName: "TestRelationSchema.ADrivesB" });
3003
+ const editElem = testImodel.elements.getElement(idA);
3004
+ editElem.navPropChildB = new RelatedElement({ id: idB, relClassName: "trs.FakeClass" });
3005
+ editElem.name = "ChildAElementUpdated";
3006
+ txn.updateElement(editElem);
3007
+ validateADrivesBRowCount(testImodel, 1);
3008
+ validateNavProp(testImodel, { id: idB, relClassName: "TestRelationSchema.ADrivesB" });
3009
+ const editedElem = testImodel.elements.getElement(idA);
3010
+ assert.equal(editedElem.name, "ChildAElementUpdated", `Expected name to be "ChildAElementUpdated" after update, but got "${editedElem.name}"`);
3011
+ assert.strictEqual(editedElem.navPropChildB.relClassName, "TestRelationSchema.ADrivesB", `Expected navPropChildB to be "TestRelationSchema.ADrivesB" after update, but got "${editedElem.navPropChildB}"`);
3012
+ // Set the nav prop value to null
3013
+ editElem.name = "ChildAElementNulled";
3014
+ editElem.navPropChildB = null;
3015
+ txn.updateElement(editElem);
3016
+ validateADrivesBRowCount(testImodel, 0);
3017
+ const nulledElem = testImodel.elements.getElement(idA);
3018
+ assert.equal(nulledElem.name, "ChildAElementNulled", `Expected name to be "ChildAElementNulled" after nulling, but got "${nulledElem.name}"`);
3019
+ assert.isUndefined(nulledElem.navPropChildB, `Expected navPropChildB to be undefined after nulling, but got "${nulledElem.navPropChildB}"`);
3020
+ if (shouldSucceed) {
3021
+ txn.deleteElement(idA);
3022
+ assert.isUndefined(testImodel.elements.tryGetElement(idA), `Expected element with id ${idA} to be deleted, but it still exists.`);
3023
+ }
3024
+ }
3025
+ }
3026
+ finally {
3027
+ txn.end("abandon");
2960
3028
  }
2961
- testImodel.abandonChanges();
2962
3029
  }
2963
3030
  testImodel.close();
2964
3031
  });
@@ -2971,18 +3038,20 @@ describe("iModel", () => {
2971
3038
  let categoryB = SpatialCategory.create(imodelDb, IModel.dictionaryId, "B");
2972
3039
  categoryA.userLabel = "A";
2973
3040
  categoryB.userLabel = "B";
2974
- categoryA.insert();
2975
- categoryB.insert();
2976
- imodelDb.saveChanges();
3041
+ const txn = new EditTxn(imodelDb, "change codeValues");
3042
+ txn.start();
3043
+ categoryA.insert(txn);
3044
+ categoryB.insert(txn);
3045
+ txn.saveChanges();
2977
3046
  categoryA = imodelDb.elements.getElement(SpatialCategory.createCode(imodelDb, IModel.dictionaryId, "A"));
2978
3047
  categoryB = imodelDb.elements.getElement(SpatialCategory.createCode(imodelDb, IModel.dictionaryId, "B"));
2979
3048
  categoryA.code.value = "temp";
2980
- categoryA.update();
3049
+ categoryA.update(txn);
2981
3050
  categoryB.code.value = "A";
2982
- categoryB.update();
3051
+ categoryB.update(txn);
2983
3052
  categoryA.code.value = "B";
2984
- categoryA.update();
2985
- imodelDb.saveChanges();
3053
+ categoryA.update(txn);
3054
+ txn.end();
2986
3055
  categoryA = imodelDb.elements.getElement(SpatialCategory.createCode(imodelDb, IModel.dictionaryId, "A"));
2987
3056
  categoryB = imodelDb.elements.getElement(SpatialCategory.createCode(imodelDb, IModel.dictionaryId, "B"));
2988
3057
  expect(categoryA.userLabel).to.equal("B", `categoryA.userLabel mismatch in ${imodelDb.name}`);
@@ -3025,17 +3094,19 @@ describe("iModel", () => {
3025
3094
  function setupRelationships(numOfRelationships, multipleClasses = false) {
3026
3095
  testImodel = SnapshotDb.createEmpty(IModelTestUtils.prepareOutputFile("IModel", "DeleteRelationshipInstances.bim"), { rootSubject: { name: "DeleteRelationshipInstances" } });
3027
3096
  assert.isTrue(testImodel.isOpen);
3028
- const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(testImodel, Code.createEmpty(), true);
3097
+ const txn = new EditTxn(testImodel, "setup delete relationships");
3098
+ txn.start();
3099
+ const [, newModelId] = IModelTestUtils.createAndInsertPhysicalPartitionAndModel(txn, Code.createEmpty(), true);
3029
3100
  let spatialCategoryId = SpatialCategory.queryCategoryIdByName(testImodel, IModel.dictionaryId, "MySpatialCategory");
3030
3101
  if (!spatialCategoryId) {
3031
- spatialCategoryId = SpatialCategory.insert(testImodel, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance());
3102
+ spatialCategoryId = SpatialCategory.insert(txn, IModel.dictionaryId, "MySpatialCategory", new SubCategoryAppearance());
3032
3103
  }
3033
3104
  const relationships = [];
3034
3105
  for (let i = 0; i < numOfRelationships; ++i) {
3035
3106
  const sourceProps = createElemProps(testImodel, newModelId, spatialCategoryId, "Generic:PhysicalObject");
3036
- const sourceId = testImodel.elements.insertElement(sourceProps);
3107
+ const sourceId = txn.insertElement(sourceProps);
3037
3108
  const targetProps = createElemProps(testImodel, newModelId, spatialCategoryId, "Generic:PhysicalObject");
3038
- const targetId = testImodel.elements.insertElement(targetProps);
3109
+ const targetId = txn.insertElement(targetProps);
3039
3110
  let relationshipClass = "BisCore:ElementGroupsMembers";
3040
3111
  if (multipleClasses)
3041
3112
  relationshipClass = relationshipClasses[i % relationshipClasses.length];
@@ -3044,10 +3115,10 @@ describe("iModel", () => {
3044
3115
  sourceId,
3045
3116
  targetId,
3046
3117
  };
3047
- relationshipProps.id = testImodel.relationships.insertInstance(relationshipProps);
3118
+ relationshipProps.id = txn.insertRelationship(relationshipProps);
3048
3119
  relationships.push(relationshipProps);
3049
3120
  }
3050
- testImodel.saveChanges();
3121
+ txn.end();
3051
3122
  return relationships;
3052
3123
  }
3053
3124
  async function getRelationshipCount(iModel, relationshipClass) {
@@ -3058,16 +3129,18 @@ describe("iModel", () => {
3058
3129
  it("deleteInstances with an empty array", async () => {
3059
3130
  const relationships = setupRelationships(10);
3060
3131
  assert.equal(relationships.length, await getRelationshipCount(testImodel, "BisCore.ElementGroupsMembers"));
3061
- testImodel.relationships.deleteInstances([]);
3062
- testImodel.saveChanges();
3132
+ withEditTxn(testImodel, (txn) => {
3133
+ txn.deleteRelationships([]);
3134
+ });
3063
3135
  assert.equal(relationships.length, await getRelationshipCount(testImodel, "BisCore.ElementGroupsMembers"));
3064
3136
  });
3065
3137
  it("deleteInstances with a single relationship instance", async () => {
3066
3138
  const relationships = setupRelationships(10);
3067
3139
  assert.equal(relationships.length, await getRelationshipCount(testImodel, "BisCore.ElementGroupsMembers"), "Should delete exactly one relationship");
3068
3140
  // Delete just one relationship using deleteInstances method
3069
- testImodel.relationships.deleteInstances([relationships[0]]);
3070
- testImodel.saveChanges();
3141
+ withEditTxn(testImodel, (txn) => {
3142
+ txn.deleteRelationships([relationships[0]]);
3143
+ });
3071
3144
  const remainingCount = await getRelationshipCount(testImodel, "BisCore.ElementGroupsMembers");
3072
3145
  assert.equal(remainingCount, relationships.length - 1, "Should delete exactly one relationship");
3073
3146
  });
@@ -3078,8 +3151,9 @@ describe("iModel", () => {
3078
3151
  assert.isTrue(await getRelationshipCount(testImodel, "BisCore.ElementDrivesElement") >= Math.floor(relationships.length / 3));
3079
3152
  assert.isTrue(await getRelationshipCount(testImodel, "BisCore.ElementRefersToDocuments") >= Math.floor(relationships.length / 3));
3080
3153
  // Test deleteInstances with mixed relationship classes
3081
- testImodel.relationships.deleteInstances(relationships);
3082
- testImodel.saveChanges();
3154
+ withEditTxn(testImodel, (txn) => {
3155
+ txn.deleteRelationships(relationships);
3156
+ });
3083
3157
  // Verify all relationships were deleted regardless of their class
3084
3158
  assert.equal(0, await getRelationshipCount(testImodel, "BisCore.ElementGroupsMembers"), "All ElementGroupsMembers relationships should be deleted");
3085
3159
  assert.equal(0, await getRelationshipCount(testImodel, "BisCore.ElementDrivesElement"), "All ElementDrivesElement relationships should be deleted");
@@ -3096,8 +3170,9 @@ describe("iModel", () => {
3096
3170
  for (let i = 0; i < 250; ++i) {
3097
3171
  relationshipsToDelete.push(relationships[Math.floor(Math.random() * relationships.length)]);
3098
3172
  }
3099
- testImodel.relationships.deleteInstances(relationshipsToDelete);
3100
- testImodel.saveChanges();
3173
+ withEditTxn(testImodel, (txn) => {
3174
+ txn.deleteRelationships(relationshipsToDelete);
3175
+ });
3101
3176
  // Verify all relationships were deleted
3102
3177
  for (const relClass of relationshipsToDelete) {
3103
3178
  const reader = testImodel.createQueryReader(`SELECT ECInstanceId FROM ${relClass.classFullName} WHERE SourceECInstanceId=? AND TargetECInstanceId=?`, new QueryBinder().bindId(1, relClass.sourceId).bindId(2, relClass.targetId));