@itwin/core-backend 5.11.0-dev.9 → 5.12.0-dev.2

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 (111) hide show
  1. package/CHANGELOG.md +13 -1
  2. package/lib/cjs/BlobContainerService.d.ts +14 -1
  3. package/lib/cjs/BlobContainerService.d.ts.map +1 -1
  4. package/lib/cjs/BlobContainerService.js.map +1 -1
  5. package/lib/cjs/BriefcaseManager.d.ts +3 -1
  6. package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
  7. package/lib/cjs/BriefcaseManager.js +3 -3
  8. package/lib/cjs/BriefcaseManager.js.map +1 -1
  9. package/lib/cjs/CloudSqlite.d.ts +1 -1
  10. package/lib/cjs/CloudSqlite.d.ts.map +1 -1
  11. package/lib/cjs/CloudSqlite.js +4 -1
  12. package/lib/cjs/CloudSqlite.js.map +1 -1
  13. package/lib/cjs/DisplayStyle.d.ts.map +1 -1
  14. package/lib/cjs/DisplayStyle.js +4 -8
  15. package/lib/cjs/DisplayStyle.js.map +1 -1
  16. package/lib/cjs/EditTxn.d.ts +56 -1
  17. package/lib/cjs/EditTxn.d.ts.map +1 -1
  18. package/lib/cjs/EditTxn.js +138 -0
  19. package/lib/cjs/EditTxn.js.map +1 -1
  20. package/lib/cjs/GeoCoordConfig.d.ts +6 -0
  21. package/lib/cjs/GeoCoordConfig.d.ts.map +1 -1
  22. package/lib/cjs/GeoCoordConfig.js +6 -0
  23. package/lib/cjs/GeoCoordConfig.js.map +1 -1
  24. package/lib/cjs/IModelDb.d.ts +40 -0
  25. package/lib/cjs/IModelDb.d.ts.map +1 -1
  26. package/lib/cjs/IModelDb.js +18 -0
  27. package/lib/cjs/IModelDb.js.map +1 -1
  28. package/lib/cjs/IModelHost.d.ts +5 -3
  29. package/lib/cjs/IModelHost.d.ts.map +1 -1
  30. package/lib/cjs/IModelHost.js.map +1 -1
  31. package/lib/cjs/NativeHost.js +1 -1
  32. package/lib/cjs/NativeHost.js.map +1 -1
  33. package/lib/cjs/workspace/SettingsEditor.d.ts +4 -9
  34. package/lib/cjs/workspace/SettingsEditor.d.ts.map +1 -1
  35. package/lib/cjs/workspace/SettingsEditor.js +31 -15
  36. package/lib/cjs/workspace/SettingsEditor.js.map +1 -1
  37. package/lib/esm/BlobContainerService.d.ts +14 -1
  38. package/lib/esm/BlobContainerService.d.ts.map +1 -1
  39. package/lib/esm/BlobContainerService.js.map +1 -1
  40. package/lib/esm/BriefcaseManager.d.ts +3 -1
  41. package/lib/esm/BriefcaseManager.d.ts.map +1 -1
  42. package/lib/esm/BriefcaseManager.js +3 -3
  43. package/lib/esm/BriefcaseManager.js.map +1 -1
  44. package/lib/esm/CloudSqlite.d.ts +1 -1
  45. package/lib/esm/CloudSqlite.d.ts.map +1 -1
  46. package/lib/esm/CloudSqlite.js +4 -1
  47. package/lib/esm/CloudSqlite.js.map +1 -1
  48. package/lib/esm/DisplayStyle.d.ts.map +1 -1
  49. package/lib/esm/DisplayStyle.js +4 -8
  50. package/lib/esm/DisplayStyle.js.map +1 -1
  51. package/lib/esm/EditTxn.d.ts +56 -1
  52. package/lib/esm/EditTxn.d.ts.map +1 -1
  53. package/lib/esm/EditTxn.js +140 -2
  54. package/lib/esm/EditTxn.js.map +1 -1
  55. package/lib/esm/GeoCoordConfig.d.ts +6 -0
  56. package/lib/esm/GeoCoordConfig.d.ts.map +1 -1
  57. package/lib/esm/GeoCoordConfig.js +6 -0
  58. package/lib/esm/GeoCoordConfig.js.map +1 -1
  59. package/lib/esm/IModelDb.d.ts +40 -0
  60. package/lib/esm/IModelDb.d.ts.map +1 -1
  61. package/lib/esm/IModelDb.js +18 -0
  62. package/lib/esm/IModelDb.js.map +1 -1
  63. package/lib/esm/IModelHost.d.ts +5 -3
  64. package/lib/esm/IModelHost.d.ts.map +1 -1
  65. package/lib/esm/IModelHost.js.map +1 -1
  66. package/lib/esm/NativeHost.js +1 -1
  67. package/lib/esm/NativeHost.js.map +1 -1
  68. package/lib/esm/test/TestUtils.d.ts +17 -2
  69. package/lib/esm/test/TestUtils.d.ts.map +1 -1
  70. package/lib/esm/test/TestUtils.js +21 -2
  71. package/lib/esm/test/TestUtils.js.map +1 -1
  72. package/lib/esm/test/annotations/TextAnnotation.test.js +81 -16
  73. package/lib/esm/test/annotations/TextAnnotation.test.js.map +1 -1
  74. package/lib/esm/test/ecdb/ConcurrentQuery.test.js +4 -2
  75. package/lib/esm/test/ecdb/ConcurrentQuery.test.js.map +1 -1
  76. package/lib/esm/test/ecdb/ECDb.test.js +1 -1
  77. package/lib/esm/test/ecdb/ECDb.test.js.map +1 -1
  78. package/lib/esm/test/ecdb/ECSqlStatement.test.js +8 -2
  79. package/lib/esm/test/ecdb/ECSqlStatement.test.js.map +1 -1
  80. package/lib/esm/test/hubaccess/CodeSwap.test.d.ts +2 -0
  81. package/lib/esm/test/hubaccess/CodeSwap.test.d.ts.map +1 -0
  82. package/lib/esm/test/hubaccess/CodeSwap.test.js +431 -0
  83. package/lib/esm/test/hubaccess/CodeSwap.test.js.map +1 -0
  84. package/lib/esm/test/imodel/IModel.test.js +10 -2
  85. package/lib/esm/test/imodel/IModel.test.js.map +1 -1
  86. package/lib/esm/test/standalone/ChangeElementParentModel.test.d.ts +2 -0
  87. package/lib/esm/test/standalone/ChangeElementParentModel.test.d.ts.map +1 -0
  88. package/lib/esm/test/standalone/ChangeElementParentModel.test.js +460 -0
  89. package/lib/esm/test/standalone/ChangeElementParentModel.test.js.map +1 -0
  90. package/lib/esm/test/standalone/ChangesetReader.test.js +3 -0
  91. package/lib/esm/test/standalone/ChangesetReader.test.js.map +1 -1
  92. package/lib/esm/test/standalone/CloudSqlite.test.d.ts +2 -0
  93. package/lib/esm/test/standalone/CloudSqlite.test.d.ts.map +1 -0
  94. package/lib/esm/test/standalone/CloudSqlite.test.js +43 -0
  95. package/lib/esm/test/standalone/CloudSqlite.test.js.map +1 -0
  96. package/lib/esm/test/standalone/IModelWrite.test.d.ts.map +1 -1
  97. package/lib/esm/test/standalone/IModelWrite.test.js +2 -33
  98. package/lib/esm/test/standalone/IModelWrite.test.js.map +1 -1
  99. package/lib/esm/test/standalone/ITwinWorkspace.test.js +118 -1
  100. package/lib/esm/test/standalone/ITwinWorkspace.test.js.map +1 -1
  101. package/lib/esm/test/standalone/Workspace.test.js +2 -2
  102. package/lib/esm/test/standalone/Workspace.test.js.map +1 -1
  103. package/lib/esm/workspace/SettingsEditor.d.ts +4 -9
  104. package/lib/esm/workspace/SettingsEditor.d.ts.map +1 -1
  105. package/lib/esm/workspace/SettingsEditor.js +31 -15
  106. package/lib/esm/workspace/SettingsEditor.js.map +1 -1
  107. package/package.json +16 -16
  108. package/lib/esm/test/misc/GeoServices.test.d.ts +0 -2
  109. package/lib/esm/test/misc/GeoServices.test.d.ts.map +0 -1
  110. package/lib/esm/test/misc/GeoServices.test.js +0 -918
  111. package/lib/esm/test/misc/GeoServices.test.js.map +0 -1
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=CodeSwap.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeSwap.test.d.ts","sourceRoot":"","sources":["../../../../src/test/hubaccess/CodeSwap.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,431 @@
1
+ /*---------------------------------------------------------------------------------------------
2
+ * Copyright (c) Bentley Systems, Incorporated. All rights reserved.
3
+ * See LICENSE.md in the project root for license terms and full copyright notice.
4
+ *--------------------------------------------------------------------------------------------*/
5
+ import * as chai from "chai";
6
+ import * as chaiAsPromised from "chai-as-promised";
7
+ import { Code, CodeScopeSpec, IModel, SubCategoryAppearance } from "@itwin/core-common";
8
+ import { HubWrappers, IModelTestUtils, KnownTestLocations } from "..";
9
+ import { ChannelControl, DrawingCategory } from "../../core-backend";
10
+ import { HubMock } from "../../internal/HubMock";
11
+ import { withEditTxn } from "../../EditTxn";
12
+ chai.use(chaiAsPromised);
13
+ describe("Code value management: null, swap, undo/redo, and cross-briefcase pull", function () {
14
+ this.timeout(60000);
15
+ it("swap codeValues between two elements sharing the same codeSpec and codeScope, verify undo/redo in a single transaction", async () => {
16
+ HubMock.startup("CodeSwapTest", KnownTestLocations.outputDir);
17
+ let b1;
18
+ try {
19
+ // --- Setup iModel ---
20
+ const iModelId = await HubMock.createNewIModel({
21
+ accessToken: "user1",
22
+ iTwinId: HubMock.iTwinId,
23
+ iModelName: "CodeSwapTest",
24
+ description: "CodeSwapTest",
25
+ });
26
+ b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user1", iTwinId: HubMock.iTwinId, iModelId });
27
+ b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);
28
+ // --- Create a drawing model + category and a CodeSpec ---
29
+ let drawingModelId;
30
+ let drawingCategoryId;
31
+ let codeSpecId;
32
+ let codeScopeId; // will be the drawing model
33
+ await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
34
+ withEditTxn(b1, "setup model, category, codeSpec", (txn) => {
35
+ const modelCode = IModelTestUtils.getUniqueModelCode(b1, "DrawingModel");
36
+ const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);
37
+ drawingModelId = modelId;
38
+ let catId = DrawingCategory.queryCategoryIdByName(b1, IModel.dictionaryId, "MyDrawingCategory");
39
+ if (undefined === catId)
40
+ catId = DrawingCategory.insert(txn, IModel.dictionaryId, "MyDrawingCategory", new SubCategoryAppearance());
41
+ drawingCategoryId = catId;
42
+ codeSpecId = b1.codeSpecs.insert(txn, "MyCodeSpec", CodeScopeSpec.Type.Model);
43
+ codeScopeId = drawingModelId;
44
+ });
45
+ await b1.pushChanges({ description: "setup" });
46
+ // --- Insert two elements with same codeSpecId/codeScopeId but different codeValues ---
47
+ let elem1Id;
48
+ let elem2Id;
49
+ await b1.locks.acquireLocks({ shared: drawingModelId });
50
+ withEditTxn(b1, "insert two coded elements", (txn) => {
51
+ const elem1Props = {
52
+ classFullName: "BisCore:DrawingGraphic",
53
+ model: drawingModelId,
54
+ category: drawingCategoryId,
55
+ code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
56
+ };
57
+ const elem2Props = {
58
+ classFullName: "BisCore:DrawingGraphic",
59
+ model: drawingModelId,
60
+ category: drawingCategoryId,
61
+ code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" }),
62
+ };
63
+ elem1Id = txn.insertElement(elem1Props);
64
+ elem2Id = txn.insertElement(elem2Props);
65
+ });
66
+ await b1.pushChanges({ description: "insert two elements with CODE_A and CODE_B" });
67
+ // Verify initial state
68
+ let e1 = b1.elements.getElementProps(elem1Id);
69
+ let e2 = b1.elements.getElementProps(elem2Id);
70
+ chai.expect(e1.code.spec).to.equal(codeSpecId);
71
+ chai.expect(e1.code.scope).to.equal(codeScopeId);
72
+ chai.expect(e1.code.value).to.equal("CODE_A");
73
+ chai.expect(e2.code.spec).to.equal(codeSpecId);
74
+ chai.expect(e2.code.scope).to.equal(codeScopeId);
75
+ chai.expect(e2.code.value).to.equal("CODE_B");
76
+ await b1.locks.acquireLocks({ exclusive: [elem2Id, elem1Id] });
77
+ withEditTxn(b1, "swap code values", (txn) => {
78
+ const props = b1.elements.getElementProps(elem1Id);
79
+ props.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "" });
80
+ txn.updateElement(props);
81
+ const props1 = b1.elements.getElementProps(elem1Id);
82
+ const props2 = b1.elements.getElementProps(elem2Id);
83
+ props2.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" });
84
+ props1.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" });
85
+ txn.updateElement(props2);
86
+ txn.updateElement(props1);
87
+ });
88
+ b1.clearCaches();
89
+ // Verify after swap2
90
+ e1 = b1.elements.getElementProps(elem1Id);
91
+ e2 = b1.elements.getElementProps(elem2Id);
92
+ chai.expect(e1.code.value).to.equal("CODE_B");
93
+ chai.expect(e2.code.value).to.equal("CODE_A");
94
+ // --- Undo (reverse txn2: the swap) ---
95
+ chai.expect(b1.txns.isUndoPossible).to.be.true;
96
+ b1.txns.reverseTxns(1);
97
+ e1 = b1.elements.getElementProps(elem1Id);
98
+ e2 = b1.elements.getElementProps(elem2Id);
99
+ chai.expect(e1.code.value).to.equal("CODE_A");
100
+ chai.expect(e2.code.value).to.equal("CODE_B");
101
+ // --- Redo (reinstate txn2: the swap) ---
102
+ chai.expect(b1.txns.isRedoPossible).to.be.true;
103
+ b1.txns.reinstateTxn();
104
+ e1 = b1.elements.getElementProps(elem1Id);
105
+ e2 = b1.elements.getElementProps(elem2Id);
106
+ chai.expect(e1.code.value).to.equal("CODE_B");
107
+ chai.expect(e2.code.value).to.equal("CODE_A");
108
+ }
109
+ finally {
110
+ b1?.close();
111
+ HubMock.shutdown();
112
+ }
113
+ });
114
+ it("insert two elements with same codeSpecId and codeScopeId, null first code, swap codes, undo/redo, push", async () => {
115
+ HubMock.startup("CodeSwapTest", KnownTestLocations.outputDir);
116
+ let b1;
117
+ let b2;
118
+ try {
119
+ // --- Setup iModel ---
120
+ const iModelId = await HubMock.createNewIModel({
121
+ accessToken: "user1",
122
+ iTwinId: HubMock.iTwinId,
123
+ iModelName: "CodeSwapTest",
124
+ description: "CodeSwapTest",
125
+ });
126
+ b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user1", iTwinId: HubMock.iTwinId, iModelId });
127
+ b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);
128
+ b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user2", iTwinId: HubMock.iTwinId, iModelId });
129
+ b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);
130
+ // --- Create a drawing model + category and a CodeSpec ---
131
+ let drawingModelId;
132
+ let drawingCategoryId;
133
+ let codeSpecId;
134
+ let codeScopeId; // will be the drawing model
135
+ await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
136
+ withEditTxn(b1, "setup model, category, codeSpec", (txn) => {
137
+ const modelCode = IModelTestUtils.getUniqueModelCode(b1, "DrawingModel");
138
+ const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);
139
+ drawingModelId = modelId;
140
+ let catId = DrawingCategory.queryCategoryIdByName(b1, IModel.dictionaryId, "MyDrawingCategory");
141
+ if (undefined === catId)
142
+ catId = DrawingCategory.insert(txn, IModel.dictionaryId, "MyDrawingCategory", new SubCategoryAppearance());
143
+ drawingCategoryId = catId;
144
+ codeSpecId = b1.codeSpecs.insert(txn, "MyCodeSpec", CodeScopeSpec.Type.Model);
145
+ codeScopeId = drawingModelId;
146
+ });
147
+ await b1.pushChanges({ description: "setup" });
148
+ await b2.pullChanges();
149
+ // --- Insert two elements with same codeSpecId/codeScopeId but different codeValues ---
150
+ let elem1Id;
151
+ let elem2Id;
152
+ await b1.locks.acquireLocks({ shared: drawingModelId });
153
+ withEditTxn(b1, "insert two coded elements", (txn) => {
154
+ const elem1Props = {
155
+ classFullName: "BisCore:DrawingGraphic",
156
+ model: drawingModelId,
157
+ category: drawingCategoryId,
158
+ code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
159
+ };
160
+ const elem2Props = {
161
+ classFullName: "BisCore:DrawingGraphic",
162
+ model: drawingModelId,
163
+ category: drawingCategoryId,
164
+ code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" }),
165
+ };
166
+ elem1Id = txn.insertElement(elem1Props);
167
+ elem2Id = txn.insertElement(elem2Props);
168
+ });
169
+ await b1.pushChanges({ description: "insert two elements with CODE_A and CODE_B" });
170
+ await b2.pullChanges();
171
+ // Verify initial state
172
+ let e1 = b1.elements.getElementProps(elem1Id);
173
+ let e2 = b1.elements.getElementProps(elem2Id);
174
+ chai.expect(e1.code.spec).to.equal(codeSpecId);
175
+ chai.expect(e1.code.scope).to.equal(codeScopeId);
176
+ chai.expect(e1.code.value).to.equal("CODE_A");
177
+ chai.expect(e2.code.spec).to.equal(codeSpecId);
178
+ chai.expect(e2.code.scope).to.equal(codeScopeId);
179
+ chai.expect(e2.code.value).to.equal("CODE_B");
180
+ // --- Step 1 (txn1): Set codeValue of elem1 to empty (null-equivalent) ---
181
+ await b1.locks.acquireLocks({ exclusive: [elem1Id] });
182
+ withEditTxn(b1, "set elem1 code to null", (txn) => {
183
+ const props = b1.elements.getElementProps(elem1Id);
184
+ props.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "" });
185
+ txn.updateElement(props);
186
+ });
187
+ e1 = b1.elements.getElementProps(elem1Id);
188
+ chai.expect(e1.code.value).to.satisfy((v) => v === undefined || v === "");
189
+ // --- Step 2 (txn2): Swap codeValues — elem1 gets CODE_B, elem2 gets CODE_A ---
190
+ // elem1's intermediate empty code value avoids a uniqueness conflict while assigning CODE_A to elem2.
191
+ await b1.locks.acquireLocks({ exclusive: [elem2Id] });
192
+ withEditTxn(b1, "swap code values", (txn) => {
193
+ const props1 = b1.elements.getElementProps(elem1Id);
194
+ const props2 = b1.elements.getElementProps(elem2Id);
195
+ props2.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" });
196
+ props1.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" });
197
+ txn.updateElement(props2);
198
+ txn.updateElement(props1);
199
+ });
200
+ b1.clearCaches();
201
+ // Verify after swap
202
+ e1 = b1.elements.getElementProps(elem1Id);
203
+ e2 = b1.elements.getElementProps(elem2Id);
204
+ chai.expect(e1.code.value).to.equal("CODE_B");
205
+ chai.expect(e2.code.value).to.equal("CODE_A");
206
+ // --- Undo (reverse txn2: the swap) ---
207
+ chai.expect(b1.txns.isUndoPossible).to.be.true;
208
+ b1.txns.reverseTxns(1);
209
+ e1 = b1.elements.getElementProps(elem1Id);
210
+ e2 = b1.elements.getElementProps(elem2Id);
211
+ chai.expect(e1.code.value).to.satisfy((v) => v === undefined || v === "");
212
+ chai.expect(e2.code.value).to.equal("CODE_B");
213
+ // --- Redo (reinstate txn2: the swap) ---
214
+ chai.expect(b1.txns.isRedoPossible).to.be.true;
215
+ b1.txns.reinstateTxn();
216
+ e1 = b1.elements.getElementProps(elem1Id);
217
+ e2 = b1.elements.getElementProps(elem2Id);
218
+ chai.expect(e1.code.value).to.equal("CODE_B");
219
+ chai.expect(e2.code.value).to.equal("CODE_A");
220
+ // --- Push changes ---
221
+ await b1.pushChanges({ description: "null elem1 code and swap code values" });
222
+ await b2.pullChanges();
223
+ // No local txns remain after push
224
+ chai.expect(b1.txns.isUndoPossible).to.be.false;
225
+ chai.expect(b1.txns.isRedoPossible).to.be.false;
226
+ // Verify b2 sees the final swapped code values after pulling
227
+ const b2e1 = b2.elements.getElementProps(elem1Id);
228
+ const b2e2 = b2.elements.getElementProps(elem2Id);
229
+ chai.expect(b2e1.code.value).to.equal("CODE_B");
230
+ chai.expect(b2e2.code.value).to.equal("CODE_A");
231
+ }
232
+ finally {
233
+ b1?.close();
234
+ b2?.close();
235
+ HubMock.shutdown();
236
+ }
237
+ });
238
+ it("delete element with existing code and insert new element with same code, verify undo/redo, push, and b2 sees final state after pull", async () => {
239
+ HubMock.startup("CodeDeleteInsertTest", KnownTestLocations.outputDir);
240
+ let b1;
241
+ let b2;
242
+ try {
243
+ // --- Setup iModel ---
244
+ const iModelId = await HubMock.createNewIModel({
245
+ accessToken: "user1",
246
+ iTwinId: HubMock.iTwinId,
247
+ iModelName: "CodeDeleteInsertTest",
248
+ description: "CodeDeleteInsertTest",
249
+ });
250
+ b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user1", iTwinId: HubMock.iTwinId, iModelId });
251
+ b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);
252
+ b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user2", iTwinId: HubMock.iTwinId, iModelId });
253
+ b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);
254
+ // --- Create a drawing model + category and a CodeSpec ---
255
+ let drawingModelId;
256
+ let drawingCategoryId;
257
+ let codeSpecId;
258
+ let codeScopeId;
259
+ await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
260
+ withEditTxn(b1, "setup model, category, codeSpec", (txn) => {
261
+ const modelCode = IModelTestUtils.getUniqueModelCode(b1, "DrawingModel");
262
+ const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);
263
+ drawingModelId = modelId;
264
+ let catId = DrawingCategory.queryCategoryIdByName(b1, IModel.dictionaryId, "MyDrawingCategory");
265
+ if (undefined === catId)
266
+ catId = DrawingCategory.insert(txn, IModel.dictionaryId, "MyDrawingCategory", new SubCategoryAppearance());
267
+ drawingCategoryId = catId;
268
+ codeSpecId = b1.codeSpecs.insert(txn, "MyCodeSpec", CodeScopeSpec.Type.Model);
269
+ codeScopeId = drawingModelId;
270
+ });
271
+ await b1.pushChanges({ description: "setup" });
272
+ await b2.pullChanges();
273
+ // --- Insert an element with a code ---
274
+ let elem1Id;
275
+ await b1.locks.acquireLocks({ shared: drawingModelId });
276
+ withEditTxn(b1, "insert element with CODE_A", (txn) => {
277
+ const elem1Props = {
278
+ classFullName: "BisCore:DrawingGraphic",
279
+ model: drawingModelId,
280
+ category: drawingCategoryId,
281
+ code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
282
+ };
283
+ elem1Id = txn.insertElement(elem1Props);
284
+ });
285
+ await b1.pushChanges({ description: "insert element with CODE_A" });
286
+ await b2.pullChanges();
287
+ // Verify initial state
288
+ let e1 = b1.elements.getElementProps(elem1Id);
289
+ chai.expect(e1.code.spec).to.equal(codeSpecId);
290
+ chai.expect(e1.code.scope).to.equal(codeScopeId);
291
+ chai.expect(e1.code.value).to.equal("CODE_A");
292
+ // --- Delete elem1 and insert a new element with the same code ---
293
+ let newElemId;
294
+ await b1.locks.acquireLocks({ exclusive: [elem1Id] });
295
+ withEditTxn(b1, "delete elem1 and insert new element with same code", (txn) => {
296
+ txn.deleteElement(elem1Id);
297
+ const newElemProps = {
298
+ classFullName: "BisCore:DrawingGraphic",
299
+ model: drawingModelId,
300
+ category: drawingCategoryId,
301
+ code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
302
+ };
303
+ newElemId = txn.insertElement(newElemProps);
304
+ });
305
+ b1.clearCaches();
306
+ // Verify after delete + insert
307
+ chai.expect(() => b1.elements.getElementProps(elem1Id)).to.throw();
308
+ const newElem = b1.elements.getElementProps(newElemId);
309
+ chai.expect(newElem.code.value).to.equal("CODE_A");
310
+ // --- Undo (reverse the delete + insert) ---
311
+ chai.expect(b1.txns.isUndoPossible).to.be.true;
312
+ b1.txns.reverseTxns(1);
313
+ e1 = b1.elements.getElementProps(elem1Id);
314
+ chai.expect(e1.code.value).to.equal("CODE_A");
315
+ chai.expect(() => b1.elements.getElementProps(newElemId)).to.throw();
316
+ // --- Redo (reinstate the delete + insert) ---
317
+ chai.expect(b1.txns.isRedoPossible).to.be.true;
318
+ b1.txns.reinstateTxn();
319
+ chai.expect(() => b1.elements.getElementProps(elem1Id)).to.throw();
320
+ const redoneElem = b1.elements.getElementProps(newElemId);
321
+ chai.expect(redoneElem.code.value).to.equal("CODE_A");
322
+ // --- Push changes ---
323
+ await b1.pushChanges({ description: "delete elem1 and insert new element with same code" });
324
+ await b2.pullChanges();
325
+ // No local txns remain after push
326
+ chai.expect(b1.txns.isUndoPossible).to.be.false;
327
+ chai.expect(b1.txns.isRedoPossible).to.be.false;
328
+ // Verify b2 sees the final state after pulling
329
+ chai.expect(() => b2.elements.getElementProps(elem1Id)).to.throw();
330
+ const b2NewElem = b2.elements.getElementProps(newElemId);
331
+ chai.expect(b2NewElem.code.value).to.equal("CODE_A");
332
+ }
333
+ finally {
334
+ b1?.close();
335
+ b2?.close();
336
+ HubMock.shutdown();
337
+ }
338
+ });
339
+ it("single-transaction code swap reverts to old behaviour (swap not applied) when noUpdateLoop is true", async () => {
340
+ HubMock.startup("CodeSwapNoUpdateLoopTest", KnownTestLocations.outputDir);
341
+ let b1;
342
+ let b2;
343
+ try {
344
+ // --- Setup iModel ---
345
+ const iModelId = await HubMock.createNewIModel({
346
+ accessToken: "user1",
347
+ iTwinId: HubMock.iTwinId,
348
+ iModelName: "CodeSwapNoUpdateLoopTest",
349
+ description: "CodeSwapNoUpdateLoopTest",
350
+ });
351
+ b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user1", iTwinId: HubMock.iTwinId, iModelId });
352
+ b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);
353
+ b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: "user2", iTwinId: HubMock.iTwinId, iModelId });
354
+ b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);
355
+ let drawingModelId;
356
+ let drawingCategoryId;
357
+ let codeSpecId;
358
+ let codeScopeId;
359
+ await b1.locks.acquireLocks({ shared: IModel.dictionaryId });
360
+ withEditTxn(b1, "setup model, category, codeSpec", (txn) => {
361
+ const modelCode = IModelTestUtils.getUniqueModelCode(b1, "DrawingModel");
362
+ const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);
363
+ drawingModelId = modelId;
364
+ let catId = DrawingCategory.queryCategoryIdByName(b1, IModel.dictionaryId, "MyDrawingCategory");
365
+ if (undefined === catId)
366
+ catId = DrawingCategory.insert(txn, IModel.dictionaryId, "MyDrawingCategory", new SubCategoryAppearance());
367
+ drawingCategoryId = catId;
368
+ codeSpecId = b1.codeSpecs.insert(txn, "MyCodeSpec", CodeScopeSpec.Type.Model);
369
+ codeScopeId = drawingModelId;
370
+ });
371
+ await b1.pushChanges({ description: "setup" });
372
+ await b2.pullChanges();
373
+ let elem1Id;
374
+ let elem2Id;
375
+ await b1.locks.acquireLocks({ shared: drawingModelId });
376
+ withEditTxn(b1, "insert two coded elements", (txn) => {
377
+ const elem1Props = {
378
+ classFullName: "BisCore:DrawingGraphic",
379
+ model: drawingModelId,
380
+ category: drawingCategoryId,
381
+ code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" }),
382
+ };
383
+ const elem2Props = {
384
+ classFullName: "BisCore:DrawingGraphic",
385
+ model: drawingModelId,
386
+ category: drawingCategoryId,
387
+ code: new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" }),
388
+ };
389
+ elem1Id = txn.insertElement(elem1Props);
390
+ elem2Id = txn.insertElement(elem2Props);
391
+ });
392
+ await b1.pushChanges({ description: "insert two elements with CODE_A and CODE_B" });
393
+ await b2.pullChanges();
394
+ // --- Single-transaction swap: null elem1, then swap codes ---
395
+ await b1.locks.acquireLocks({ exclusive: [elem2Id, elem1Id] });
396
+ withEditTxn(b1, "single-txn swap", (txn) => {
397
+ // Step 1: null out elem1's code to free the uniqueness slot
398
+ const props = b1.elements.getElementProps(elem1Id);
399
+ props.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "" });
400
+ txn.updateElement(props);
401
+ // Step 2: assign CODE_A to elem2, CODE_B to elem1
402
+ const props1 = b1.elements.getElementProps(elem1Id);
403
+ const props2 = b1.elements.getElementProps(elem2Id);
404
+ props2.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_A" });
405
+ props1.code = new Code({ spec: codeSpecId, scope: codeScopeId, value: "CODE_B" });
406
+ txn.updateElement(props2);
407
+ txn.updateElement(props1);
408
+ });
409
+ // Verify b1 has the swapped state
410
+ b1.clearCaches();
411
+ const b1e1 = b1.elements.getElementProps(elem1Id);
412
+ const b1e2 = b1.elements.getElementProps(elem2Id);
413
+ chai.expect(b1e1.code.value).to.equal("CODE_B");
414
+ chai.expect(b1e2.code.value).to.equal("CODE_A");
415
+ await b1.pushChanges({ description: "single-txn code swap" });
416
+ await b2.pullChanges({ noUpdateLoop: true });
417
+ b2.clearCaches();
418
+ const b2e1 = b2.elements.getElementProps(elem1Id);
419
+ const b2e2 = b2.elements.getElementProps(elem2Id);
420
+ // Old behaviour: swap did NOT land — b2 still sees the original codes
421
+ chai.expect(b2e1.code.value).to.equal("CODE_A");
422
+ chai.expect(b2e2.code.value).to.equal("CODE_B");
423
+ }
424
+ finally {
425
+ b1?.close();
426
+ b2?.close();
427
+ HubMock.shutdown();
428
+ }
429
+ });
430
+ });
431
+ //# sourceMappingURL=CodeSwap.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CodeSwap.test.js","sourceRoot":"","sources":["../../../../src/test/hubaccess/CodeSwap.test.ts"],"names":[],"mappings":"AAAA;;;+FAG+F;AAE/F,OAAO,KAAK,IAAI,MAAM,MAAM,CAAC;AAC7B,OAAO,KAAK,cAAc,MAAM,kBAAkB,CAAC;AAGnD,OAAO,EAAE,IAAI,EAAE,aAAa,EAA2B,MAAM,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AACjH,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,IAAI,CAAC;AACtE,OAAO,EAAe,cAAc,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAClF,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAE5C,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;AAEzB,QAAQ,CAAC,wEAAwE,EAAE;IACjF,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;IAEpB,EAAE,CAAC,wHAAwH,EAAE,KAAK,IAAI,EAAE;QACtI,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAC9D,IAAI,EAA2B,CAAC;QAEhC,IAAI,CAAC;YACH,uBAAuB;YACvB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC;gBAC7C,WAAW,EAAE,OAAO;gBACpB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,UAAU,EAAE,cAAc;gBAC1B,WAAW,EAAE,cAAc;aAC5B,CAAC,CAAC;YAEH,EAAE,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9G,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;YAEhE,2DAA2D;YAC3D,IAAI,cAA0B,CAAC;YAC/B,IAAI,iBAA6B,CAAC;YAClC,IAAI,UAAsB,CAAC;YAC3B,IAAI,WAAuB,CAAC,CAAC,4BAA4B;YAEzD,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;YAC7D,WAAW,CAAC,EAAE,EAAE,iCAAiC,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzD,MAAM,SAAS,GAAG,eAAe,CAAC,kBAAkB,CAAC,EAAG,EAAE,cAAc,CAAC,CAAC;gBAC1E,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,eAAe,CAAC,uCAAuC,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;gBAC5F,cAAc,GAAG,OAAO,CAAC;gBAEzB,IAAI,KAAK,GAAG,eAAe,CAAC,qBAAqB,CAAC,EAAG,EAAE,MAAM,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC;gBACjG,IAAI,SAAS,KAAK,KAAK;oBACrB,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,YAAY,EAAE,mBAAmB,EAAE,IAAI,qBAAqB,EAAE,CAAC,CAAC;gBAC7G,iBAAiB,GAAG,KAAK,CAAC;gBAE1B,UAAU,GAAG,EAAG,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC/E,WAAW,GAAG,cAAc,CAAC;YAC/B,CAAC,CAAC,CAAC;YAEH,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;YAE/C,wFAAwF;YACxF,IAAI,OAAmB,CAAC;YACxB,IAAI,OAAmB,CAAC;YAExB,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,cAAe,EAAE,CAAC,CAAC;YACzD,WAAW,CAAC,EAAE,EAAE,2BAA2B,EAAE,CAAC,GAAG,EAAE,EAAE;gBACnD,MAAM,UAAU,GAA4B;oBAC1C,aAAa,EAAE,wBAAwB;oBACvC,KAAK,EAAE,cAAe;oBACtB,QAAQ,EAAE,iBAAkB;oBAC5B,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;iBAC5E,CAAC;gBACF,MAAM,UAAU,GAA4B;oBAC1C,aAAa,EAAE,wBAAwB;oBACvC,KAAK,EAAE,cAAe;oBACtB,QAAQ,EAAE,iBAAkB;oBAC5B,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;iBAC5E,CAAC;gBACF,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;gBACxC,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;YAEH,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC,CAAC;YAEpF,uBAAuB;YACvB,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC/C,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAW,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAY,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAW,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAY,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAE9C,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC,OAAQ,EAAE,OAAQ,CAAC,EAAE,CAAC,CAAC;YACjE,WAAW,CAAC,EAAE,EAAE,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC1C,MAAM,KAAK,GAAG,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;gBACrD,KAAK,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC7E,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;gBAEzB,MAAM,MAAM,GAAG,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;gBACtD,MAAM,MAAM,GAAG,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;gBAEtD,MAAM,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACpF,MAAM,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAEpF,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC1B,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,WAAW,EAAE,CAAC;YACjB,qBAAqB;YACrB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAE9C,wCAAwC;YACxC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;YAC/C,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAEvB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAE9C,0CAA0C;YAC1C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;YAC/C,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YAEvB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEhD,CAAC;gBAAS,CAAC;YACT,EAAE,EAAE,KAAK,EAAE,CAAC;YACZ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,wGAAwG,EAAE,KAAK,IAAI,EAAE;QACtH,OAAO,CAAC,OAAO,CAAC,cAAc,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAC9D,IAAI,EAA2B,CAAC;QAChC,IAAI,EAA2B,CAAC;QAEhC,IAAI,CAAC;YACH,uBAAuB;YACvB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC;gBAC7C,WAAW,EAAE,OAAO;gBACpB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,UAAU,EAAE,cAAc;gBAC1B,WAAW,EAAE,cAAc;aAC5B,CAAC,CAAC;YAEH,EAAE,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9G,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;YAEhE,EAAE,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9G,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;YAEhE,2DAA2D;YAC3D,IAAI,cAA0B,CAAC;YAC/B,IAAI,iBAA6B,CAAC;YAClC,IAAI,UAAsB,CAAC;YAC3B,IAAI,WAAuB,CAAC,CAAC,4BAA4B;YAEzD,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;YAC7D,WAAW,CAAC,EAAE,EAAE,iCAAiC,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzD,MAAM,SAAS,GAAG,eAAe,CAAC,kBAAkB,CAAC,EAAG,EAAE,cAAc,CAAC,CAAC;gBAC1E,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,eAAe,CAAC,uCAAuC,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;gBAC5F,cAAc,GAAG,OAAO,CAAC;gBAEzB,IAAI,KAAK,GAAG,eAAe,CAAC,qBAAqB,CAAC,EAAG,EAAE,MAAM,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC;gBACjG,IAAI,SAAS,KAAK,KAAK;oBACrB,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,YAAY,EAAE,mBAAmB,EAAE,IAAI,qBAAqB,EAAE,CAAC,CAAC;gBAC7G,iBAAiB,GAAG,KAAK,CAAC;gBAE1B,UAAU,GAAG,EAAG,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC/E,WAAW,GAAG,cAAc,CAAC;YAC/B,CAAC,CAAC,CAAC;YAEH,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;YAC/C,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;YAEvB,wFAAwF;YACxF,IAAI,OAAmB,CAAC;YACxB,IAAI,OAAmB,CAAC;YAExB,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,cAAe,EAAE,CAAC,CAAC;YACzD,WAAW,CAAC,EAAE,EAAE,2BAA2B,EAAE,CAAC,GAAG,EAAE,EAAE;gBACnD,MAAM,UAAU,GAA4B;oBAC1C,aAAa,EAAE,wBAAwB;oBACvC,KAAK,EAAE,cAAe;oBACtB,QAAQ,EAAE,iBAAkB;oBAC5B,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;iBAC5E,CAAC;gBACF,MAAM,UAAU,GAA4B;oBAC1C,aAAa,EAAE,wBAAwB;oBACvC,KAAK,EAAE,cAAe;oBACtB,QAAQ,EAAE,iBAAkB;oBAC5B,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;iBAC5E,CAAC;gBACF,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;gBACxC,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;YAEH,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC,CAAC;YACpF,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;YAEvB,uBAAuB;YACvB,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC/C,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAW,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAY,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAW,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAY,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAE9C,2EAA2E;YAC3E,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC,OAAQ,CAAC,EAAE,CAAC,CAAC;YACvD,WAAW,CAAC,EAAE,EAAE,wBAAwB,EAAE,CAAC,GAAG,EAAE,EAAE;gBAChD,MAAM,KAAK,GAAG,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;gBACrD,KAAK,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC7E,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;YAC3B,CAAC,CAAC,CAAC;YAEH,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAqB,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAE9F,gFAAgF;YAChF,sGAAsG;YACtG,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC,OAAQ,CAAC,EAAE,CAAC,CAAC;YACvD,WAAW,CAAC,EAAE,EAAE,kBAAkB,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC1C,MAAM,MAAM,GAAG,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;gBACtD,MAAM,MAAM,GAAG,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;gBAEtD,MAAM,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACpF,MAAM,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBAEpF,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC1B,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC,CAAC,CAAC;YACH,EAAE,CAAC,WAAW,EAAE,CAAC;YACjB,oBAAoB;YACpB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAE9C,wCAAwC;YACxC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;YAC/C,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAEvB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAqB,EAAE,EAAE,CAAC,CAAC,KAAK,SAAS,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;YAC9F,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAE9C,0CAA0C;YAC1C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;YAC/C,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YAEvB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,uBAAuB;YACvB,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,sCAAsC,EAAE,CAAC,CAAC;YAC9E,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;YAEvB,kCAAkC;YAClC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC;YAEhD,6DAA6D;YAC7D,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YACnD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAElD,CAAC;gBAAS,CAAC;YACT,EAAE,EAAE,KAAK,EAAE,CAAC;YACZ,EAAE,EAAE,KAAK,EAAE,CAAC;YACZ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,qIAAqI,EAAE,KAAK,IAAI,EAAE;QACnJ,OAAO,CAAC,OAAO,CAAC,sBAAsB,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;QACtE,IAAI,EAA2B,CAAC;QAChC,IAAI,EAA2B,CAAC;QAEhC,IAAI,CAAC;YACH,uBAAuB;YACvB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC;gBAC7C,WAAW,EAAE,OAAO;gBACpB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,UAAU,EAAE,sBAAsB;gBAClC,WAAW,EAAE,sBAAsB;aACpC,CAAC,CAAC;YAEH,EAAE,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9G,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;YAEhE,EAAE,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9G,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;YAEhE,2DAA2D;YAC3D,IAAI,cAA0B,CAAC;YAC/B,IAAI,iBAA6B,CAAC;YAClC,IAAI,UAAsB,CAAC;YAC3B,IAAI,WAAuB,CAAC;YAE5B,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;YAC7D,WAAW,CAAC,EAAE,EAAE,iCAAiC,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzD,MAAM,SAAS,GAAG,eAAe,CAAC,kBAAkB,CAAC,EAAG,EAAE,cAAc,CAAC,CAAC;gBAC1E,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,eAAe,CAAC,uCAAuC,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;gBAC5F,cAAc,GAAG,OAAO,CAAC;gBAEzB,IAAI,KAAK,GAAG,eAAe,CAAC,qBAAqB,CAAC,EAAG,EAAE,MAAM,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC;gBACjG,IAAI,SAAS,KAAK,KAAK;oBACrB,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,YAAY,EAAE,mBAAmB,EAAE,IAAI,qBAAqB,EAAE,CAAC,CAAC;gBAC7G,iBAAiB,GAAG,KAAK,CAAC;gBAE1B,UAAU,GAAG,EAAG,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC/E,WAAW,GAAG,cAAc,CAAC;YAC/B,CAAC,CAAC,CAAC;YAEH,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;YAC/C,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;YAEvB,wCAAwC;YACxC,IAAI,OAAmB,CAAC;YAExB,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,cAAe,EAAE,CAAC,CAAC;YACzD,WAAW,CAAC,EAAE,EAAE,4BAA4B,EAAE,CAAC,GAAG,EAAE,EAAE;gBACpD,MAAM,UAAU,GAA4B;oBAC1C,aAAa,EAAE,wBAAwB;oBACvC,KAAK,EAAE,cAAe;oBACtB,QAAQ,EAAE,iBAAkB;oBAC5B,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;iBAC5E,CAAC;gBACF,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;YAEH,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,4BAA4B,EAAE,CAAC,CAAC;YACpE,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;YAEvB,uBAAuB;YACvB,IAAI,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC/C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,UAAW,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,WAAY,CAAC,CAAC;YAClD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAE9C,mEAAmE;YACnE,IAAI,SAAqB,CAAC;YAE1B,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC,OAAQ,CAAC,EAAE,CAAC,CAAC;YACvD,WAAW,CAAC,EAAE,EAAE,oDAAoD,EAAE,CAAC,GAAG,EAAE,EAAE;gBAC5E,GAAG,CAAC,aAAa,CAAC,OAAQ,CAAC,CAAC;gBAE5B,MAAM,YAAY,GAA4B;oBAC5C,aAAa,EAAE,wBAAwB;oBACvC,KAAK,EAAE,cAAe;oBACtB,QAAQ,EAAE,iBAAkB;oBAC5B,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;iBAC5E,CAAC;gBACF,SAAS,GAAG,GAAG,CAAC,aAAa,CAAC,YAAY,CAAC,CAAC;YAC9C,CAAC,CAAC,CAAC;YAEH,EAAE,CAAC,WAAW,EAAE,CAAC;YAEjB,+BAA+B;YAC/B,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YACrE,MAAM,OAAO,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,SAAU,CAAC,CAAC;YACxD,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAEnD,6CAA6C;YAC7C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;YAC/C,EAAE,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC;YAEvB,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YAC3C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAC9C,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,SAAU,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YAEvE,+CAA+C;YAC/C,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC;YAC/C,EAAE,CAAC,IAAI,CAAC,YAAY,EAAE,CAAC;YAEvB,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YACrE,MAAM,UAAU,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,SAAU,CAAC,CAAC;YAC3D,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAEtD,uBAAuB;YACvB,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,oDAAoD,EAAE,CAAC,CAAC;YAC5F,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;YAEvB,kCAAkC;YAClC,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC;YAEhD,+CAA+C;YAC/C,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,CAAC,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC;YACrE,MAAM,SAAS,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,SAAU,CAAC,CAAC;YAC1D,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAEvD,CAAC;gBAAS,CAAC;YACT,EAAE,EAAE,KAAK,EAAE,CAAC;YACZ,EAAE,EAAE,KAAK,EAAE,CAAC;YACZ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,oGAAoG,EAAE,KAAK,IAAI,EAAE;QAClH,OAAO,CAAC,OAAO,CAAC,0BAA0B,EAAE,kBAAkB,CAAC,SAAS,CAAC,CAAC;QAC1E,IAAI,EAA2B,CAAC;QAChC,IAAI,EAA2B,CAAC;QAEhC,IAAI,CAAC;YACH,uBAAuB;YACvB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,eAAe,CAAC;gBAC7C,WAAW,EAAE,OAAO;gBACpB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,UAAU,EAAE,0BAA0B;gBACtC,WAAW,EAAE,0BAA0B;aACxC,CAAC,CAAC;YAEH,EAAE,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9G,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;YAEhE,EAAE,GAAG,MAAM,WAAW,CAAC,wBAAwB,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC;YAC9G,EAAE,CAAC,QAAQ,CAAC,iBAAiB,CAAC,cAAc,CAAC,iBAAiB,CAAC,CAAC;YAEhE,IAAI,cAA0B,CAAC;YAC/B,IAAI,iBAA6B,CAAC;YAClC,IAAI,UAAsB,CAAC;YAC3B,IAAI,WAAuB,CAAC;YAE5B,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,EAAE,CAAC,CAAC;YAC7D,WAAW,CAAC,EAAE,EAAE,iCAAiC,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzD,MAAM,SAAS,GAAG,eAAe,CAAC,kBAAkB,CAAC,EAAG,EAAE,cAAc,CAAC,CAAC;gBAC1E,MAAM,CAAC,EAAE,OAAO,CAAC,GAAG,eAAe,CAAC,uCAAuC,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;gBAC5F,cAAc,GAAG,OAAO,CAAC;gBAEzB,IAAI,KAAK,GAAG,eAAe,CAAC,qBAAqB,CAAC,EAAG,EAAE,MAAM,CAAC,YAAY,EAAE,mBAAmB,CAAC,CAAC;gBACjG,IAAI,SAAS,KAAK,KAAK;oBACrB,KAAK,GAAG,eAAe,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,CAAC,YAAY,EAAE,mBAAmB,EAAE,IAAI,qBAAqB,EAAE,CAAC,CAAC;gBAC7G,iBAAiB,GAAG,KAAK,CAAC;gBAE1B,UAAU,GAAG,EAAG,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,EAAE,YAAY,EAAE,aAAa,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBAC/E,WAAW,GAAG,cAAc,CAAC;YAC/B,CAAC,CAAC,CAAC;YAEH,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,OAAO,EAAE,CAAC,CAAC;YAC/C,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;YAEvB,IAAI,OAAmB,CAAC;YACxB,IAAI,OAAmB,CAAC;YAExB,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,MAAM,EAAE,cAAe,EAAE,CAAC,CAAC;YACzD,WAAW,CAAC,EAAE,EAAE,2BAA2B,EAAE,CAAC,GAAG,EAAE,EAAE;gBACnD,MAAM,UAAU,GAA4B;oBAC1C,aAAa,EAAE,wBAAwB;oBACvC,KAAK,EAAE,cAAe;oBACtB,QAAQ,EAAE,iBAAkB;oBAC5B,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;iBAC5E,CAAC;gBACF,MAAM,UAAU,GAA4B;oBAC1C,aAAa,EAAE,wBAAwB;oBACvC,KAAK,EAAE,cAAe;oBACtB,QAAQ,EAAE,iBAAkB;oBAC5B,IAAI,EAAE,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC;iBAC5E,CAAC;gBACF,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;gBACxC,OAAO,GAAG,GAAG,CAAC,aAAa,CAAC,UAAU,CAAC,CAAC;YAC1C,CAAC,CAAC,CAAC;YAEH,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,4CAA4C,EAAE,CAAC,CAAC;YACpF,MAAM,EAAE,CAAC,WAAW,EAAE,CAAC;YAEvB,+DAA+D;YAC/D,MAAM,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,CAAC,OAAQ,EAAE,OAAQ,CAAC,EAAE,CAAC,CAAC;YACjE,WAAW,CAAC,EAAE,EAAE,iBAAiB,EAAE,CAAC,GAAG,EAAE,EAAE;gBACzC,4DAA4D;gBAC5D,MAAM,KAAK,GAAG,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;gBACrD,KAAK,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,EAAE,EAAE,CAAC,CAAC;gBAC7E,GAAG,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC;gBAEzB,kDAAkD;gBAClD,MAAM,MAAM,GAAG,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;gBACtD,MAAM,MAAM,GAAG,EAAG,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;gBACtD,MAAM,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACpF,MAAM,CAAC,IAAI,GAAG,IAAI,IAAI,CAAC,EAAE,IAAI,EAAE,UAAW,EAAE,KAAK,EAAE,WAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,CAAC,CAAC;gBACpF,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;gBAC1B,GAAG,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC,CAAC,CAAC;YAEH,kCAAkC;YAClC,EAAE,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YACnD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAEhD,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,WAAW,EAAE,sBAAsB,EAAE,CAAC,CAAC;YAO9D,MAAM,EAAE,CAAC,WAAW,CAAC,EAAE,YAAY,EAAE,IAAI,EAAsB,CAAC,CAAC;YAEjE,EAAE,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YACnD,MAAM,IAAI,GAAG,EAAE,CAAC,QAAQ,CAAC,eAAe,CAAC,OAAQ,CAAC,CAAC;YACnD,sEAAsE;YACtE,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YAChD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;QAElD,CAAC;gBAAS,CAAC;YACT,EAAE,EAAE,KAAK,EAAE,CAAC;YACZ,EAAE,EAAE,KAAK,EAAE,CAAC;YACZ,OAAO,CAAC,QAAQ,EAAE,CAAC;QACrB,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["/*---------------------------------------------------------------------------------------------\n* Copyright (c) Bentley Systems, Incorporated. All rights reserved.\n* See LICENSE.md in the project root for license terms and full copyright notice.\n*--------------------------------------------------------------------------------------------*/\n\nimport * as chai from \"chai\";\nimport * as chaiAsPromised from \"chai-as-promised\";\nimport { Suite } from \"mocha\";\nimport { Id64String } from \"@itwin/core-bentley\";\nimport { Code, CodeScopeSpec, GeometricElement2dProps, IModel, SubCategoryAppearance } from \"@itwin/core-common\";\nimport { HubWrappers, IModelTestUtils, KnownTestLocations } from \"..\";\nimport { BriefcaseDb, ChannelControl, DrawingCategory } from \"../../core-backend\";\nimport { HubMock } from \"../../internal/HubMock\";\nimport { withEditTxn } from \"../../EditTxn\";\n\nchai.use(chaiAsPromised);\n\ndescribe(\"Code value management: null, swap, undo/redo, and cross-briefcase pull\", function (this: Suite) {\n this.timeout(60000);\n\n it(\"swap codeValues between two elements sharing the same codeSpec and codeScope, verify undo/redo in a single transaction\", async () => {\n HubMock.startup(\"CodeSwapTest\", KnownTestLocations.outputDir);\n let b1: BriefcaseDb | undefined;\n\n try {\n // --- Setup iModel ---\n const iModelId = await HubMock.createNewIModel({\n accessToken: \"user1\",\n iTwinId: HubMock.iTwinId,\n iModelName: \"CodeSwapTest\",\n description: \"CodeSwapTest\",\n });\n\n b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user1\", iTwinId: HubMock.iTwinId, iModelId });\n b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n // --- Create a drawing model + category and a CodeSpec ---\n let drawingModelId: Id64String;\n let drawingCategoryId: Id64String;\n let codeSpecId: Id64String;\n let codeScopeId: Id64String; // will be the drawing model\n\n await b1.locks.acquireLocks({ shared: IModel.dictionaryId });\n withEditTxn(b1, \"setup model, category, codeSpec\", (txn) => {\n const modelCode = IModelTestUtils.getUniqueModelCode(b1!, \"DrawingModel\");\n const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);\n drawingModelId = modelId;\n\n let catId = DrawingCategory.queryCategoryIdByName(b1!, IModel.dictionaryId, \"MyDrawingCategory\");\n if (undefined === catId)\n catId = DrawingCategory.insert(txn, IModel.dictionaryId, \"MyDrawingCategory\", new SubCategoryAppearance());\n drawingCategoryId = catId;\n\n codeSpecId = b1!.codeSpecs.insert(txn, \"MyCodeSpec\", CodeScopeSpec.Type.Model);\n codeScopeId = drawingModelId;\n });\n\n await b1.pushChanges({ description: \"setup\" });\n\n // --- Insert two elements with same codeSpecId/codeScopeId but different codeValues ---\n let elem1Id: Id64String;\n let elem2Id: Id64String;\n\n await b1.locks.acquireLocks({ shared: drawingModelId! });\n withEditTxn(b1, \"insert two coded elements\", (txn) => {\n const elem1Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n const elem2Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" }),\n };\n elem1Id = txn.insertElement(elem1Props);\n elem2Id = txn.insertElement(elem2Props);\n });\n\n await b1.pushChanges({ description: \"insert two elements with CODE_A and CODE_B\" });\n\n // Verify initial state\n let e1 = b1.elements.getElementProps(elem1Id!);\n let e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.spec).to.equal(codeSpecId!);\n chai.expect(e1.code.scope).to.equal(codeScopeId!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n chai.expect(e2.code.spec).to.equal(codeSpecId!);\n chai.expect(e2.code.scope).to.equal(codeScopeId!);\n chai.expect(e2.code.value).to.equal(\"CODE_B\");\n\n await b1.locks.acquireLocks({ exclusive: [elem2Id!, elem1Id!] });\n withEditTxn(b1, \"swap code values\", (txn) => {\n const props = b1!.elements.getElementProps(elem1Id!);\n props.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"\" });\n txn.updateElement(props);\n\n const props1 = b1!.elements.getElementProps(elem1Id!);\n const props2 = b1!.elements.getElementProps(elem2Id!);\n\n props2.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" });\n props1.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" });\n\n txn.updateElement(props2);\n txn.updateElement(props1);\n });\n b1.clearCaches();\n // Verify after swap2\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_B\");\n chai.expect(e2.code.value).to.equal(\"CODE_A\");\n\n // --- Undo (reverse txn2: the swap) ---\n chai.expect(b1.txns.isUndoPossible).to.be.true;\n b1.txns.reverseTxns(1);\n\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n chai.expect(e2.code.value).to.equal(\"CODE_B\");\n\n // --- Redo (reinstate txn2: the swap) ---\n chai.expect(b1.txns.isRedoPossible).to.be.true;\n b1.txns.reinstateTxn();\n\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_B\");\n chai.expect(e2.code.value).to.equal(\"CODE_A\");\n\n } finally {\n b1?.close();\n HubMock.shutdown();\n }\n });\n it(\"insert two elements with same codeSpecId and codeScopeId, null first code, swap codes, undo/redo, push\", async () => {\n HubMock.startup(\"CodeSwapTest\", KnownTestLocations.outputDir);\n let b1: BriefcaseDb | undefined;\n let b2: BriefcaseDb | undefined;\n\n try {\n // --- Setup iModel ---\n const iModelId = await HubMock.createNewIModel({\n accessToken: \"user1\",\n iTwinId: HubMock.iTwinId,\n iModelName: \"CodeSwapTest\",\n description: \"CodeSwapTest\",\n });\n\n b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user1\", iTwinId: HubMock.iTwinId, iModelId });\n b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user2\", iTwinId: HubMock.iTwinId, iModelId });\n b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n // --- Create a drawing model + category and a CodeSpec ---\n let drawingModelId: Id64String;\n let drawingCategoryId: Id64String;\n let codeSpecId: Id64String;\n let codeScopeId: Id64String; // will be the drawing model\n\n await b1.locks.acquireLocks({ shared: IModel.dictionaryId });\n withEditTxn(b1, \"setup model, category, codeSpec\", (txn) => {\n const modelCode = IModelTestUtils.getUniqueModelCode(b1!, \"DrawingModel\");\n const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);\n drawingModelId = modelId;\n\n let catId = DrawingCategory.queryCategoryIdByName(b1!, IModel.dictionaryId, \"MyDrawingCategory\");\n if (undefined === catId)\n catId = DrawingCategory.insert(txn, IModel.dictionaryId, \"MyDrawingCategory\", new SubCategoryAppearance());\n drawingCategoryId = catId;\n\n codeSpecId = b1!.codeSpecs.insert(txn, \"MyCodeSpec\", CodeScopeSpec.Type.Model);\n codeScopeId = drawingModelId;\n });\n\n await b1.pushChanges({ description: \"setup\" });\n await b2.pullChanges();\n\n // --- Insert two elements with same codeSpecId/codeScopeId but different codeValues ---\n let elem1Id: Id64String;\n let elem2Id: Id64String;\n\n await b1.locks.acquireLocks({ shared: drawingModelId! });\n withEditTxn(b1, \"insert two coded elements\", (txn) => {\n const elem1Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n const elem2Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" }),\n };\n elem1Id = txn.insertElement(elem1Props);\n elem2Id = txn.insertElement(elem2Props);\n });\n\n await b1.pushChanges({ description: \"insert two elements with CODE_A and CODE_B\" });\n await b2.pullChanges();\n\n // Verify initial state\n let e1 = b1.elements.getElementProps(elem1Id!);\n let e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.spec).to.equal(codeSpecId!);\n chai.expect(e1.code.scope).to.equal(codeScopeId!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n chai.expect(e2.code.spec).to.equal(codeSpecId!);\n chai.expect(e2.code.scope).to.equal(codeScopeId!);\n chai.expect(e2.code.value).to.equal(\"CODE_B\");\n\n // --- Step 1 (txn1): Set codeValue of elem1 to empty (null-equivalent) ---\n await b1.locks.acquireLocks({ exclusive: [elem1Id!] });\n withEditTxn(b1, \"set elem1 code to null\", (txn) => {\n const props = b1!.elements.getElementProps(elem1Id!);\n props.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"\" });\n txn.updateElement(props);\n });\n\n e1 = b1.elements.getElementProps(elem1Id!);\n chai.expect(e1.code.value).to.satisfy((v: string | undefined) => v === undefined || v === \"\");\n\n // --- Step 2 (txn2): Swap codeValues — elem1 gets CODE_B, elem2 gets CODE_A ---\n // elem1's intermediate empty code value avoids a uniqueness conflict while assigning CODE_A to elem2.\n await b1.locks.acquireLocks({ exclusive: [elem2Id!] });\n withEditTxn(b1, \"swap code values\", (txn) => {\n const props1 = b1!.elements.getElementProps(elem1Id!);\n const props2 = b1!.elements.getElementProps(elem2Id!);\n\n props2.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" });\n props1.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" });\n\n txn.updateElement(props2);\n txn.updateElement(props1);\n });\n b1.clearCaches();\n // Verify after swap\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_B\");\n chai.expect(e2.code.value).to.equal(\"CODE_A\");\n\n // --- Undo (reverse txn2: the swap) ---\n chai.expect(b1.txns.isUndoPossible).to.be.true;\n b1.txns.reverseTxns(1);\n\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.satisfy((v: string | undefined) => v === undefined || v === \"\");\n chai.expect(e2.code.value).to.equal(\"CODE_B\");\n\n // --- Redo (reinstate txn2: the swap) ---\n chai.expect(b1.txns.isRedoPossible).to.be.true;\n b1.txns.reinstateTxn();\n\n e1 = b1.elements.getElementProps(elem1Id!);\n e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_B\");\n chai.expect(e2.code.value).to.equal(\"CODE_A\");\n // --- Push changes ---\n await b1.pushChanges({ description: \"null elem1 code and swap code values\" });\n await b2.pullChanges();\n\n // No local txns remain after push\n chai.expect(b1.txns.isUndoPossible).to.be.false;\n chai.expect(b1.txns.isRedoPossible).to.be.false;\n\n // Verify b2 sees the final swapped code values after pulling\n const b2e1 = b2.elements.getElementProps(elem1Id!);\n const b2e2 = b2.elements.getElementProps(elem2Id!);\n chai.expect(b2e1.code.value).to.equal(\"CODE_B\");\n chai.expect(b2e2.code.value).to.equal(\"CODE_A\");\n\n } finally {\n b1?.close();\n b2?.close();\n HubMock.shutdown();\n }\n });\n it(\"delete element with existing code and insert new element with same code, verify undo/redo, push, and b2 sees final state after pull\", async () => {\n HubMock.startup(\"CodeDeleteInsertTest\", KnownTestLocations.outputDir);\n let b1: BriefcaseDb | undefined;\n let b2: BriefcaseDb | undefined;\n\n try {\n // --- Setup iModel ---\n const iModelId = await HubMock.createNewIModel({\n accessToken: \"user1\",\n iTwinId: HubMock.iTwinId,\n iModelName: \"CodeDeleteInsertTest\",\n description: \"CodeDeleteInsertTest\",\n });\n\n b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user1\", iTwinId: HubMock.iTwinId, iModelId });\n b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user2\", iTwinId: HubMock.iTwinId, iModelId });\n b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n // --- Create a drawing model + category and a CodeSpec ---\n let drawingModelId: Id64String;\n let drawingCategoryId: Id64String;\n let codeSpecId: Id64String;\n let codeScopeId: Id64String;\n\n await b1.locks.acquireLocks({ shared: IModel.dictionaryId });\n withEditTxn(b1, \"setup model, category, codeSpec\", (txn) => {\n const modelCode = IModelTestUtils.getUniqueModelCode(b1!, \"DrawingModel\");\n const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);\n drawingModelId = modelId;\n\n let catId = DrawingCategory.queryCategoryIdByName(b1!, IModel.dictionaryId, \"MyDrawingCategory\");\n if (undefined === catId)\n catId = DrawingCategory.insert(txn, IModel.dictionaryId, \"MyDrawingCategory\", new SubCategoryAppearance());\n drawingCategoryId = catId;\n\n codeSpecId = b1!.codeSpecs.insert(txn, \"MyCodeSpec\", CodeScopeSpec.Type.Model);\n codeScopeId = drawingModelId;\n });\n\n await b1.pushChanges({ description: \"setup\" });\n await b2.pullChanges();\n\n // --- Insert an element with a code ---\n let elem1Id: Id64String;\n\n await b1.locks.acquireLocks({ shared: drawingModelId! });\n withEditTxn(b1, \"insert element with CODE_A\", (txn) => {\n const elem1Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n elem1Id = txn.insertElement(elem1Props);\n });\n\n await b1.pushChanges({ description: \"insert element with CODE_A\" });\n await b2.pullChanges();\n\n // Verify initial state\n let e1 = b1.elements.getElementProps(elem1Id!);\n chai.expect(e1.code.spec).to.equal(codeSpecId!);\n chai.expect(e1.code.scope).to.equal(codeScopeId!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n\n // --- Delete elem1 and insert a new element with the same code ---\n let newElemId: Id64String;\n\n await b1.locks.acquireLocks({ exclusive: [elem1Id!] });\n withEditTxn(b1, \"delete elem1 and insert new element with same code\", (txn) => {\n txn.deleteElement(elem1Id!);\n\n const newElemProps: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n newElemId = txn.insertElement(newElemProps);\n });\n\n b1.clearCaches();\n\n // Verify after delete + insert\n chai.expect(() => b1!.elements.getElementProps(elem1Id!)).to.throw();\n const newElem = b1.elements.getElementProps(newElemId!);\n chai.expect(newElem.code.value).to.equal(\"CODE_A\");\n\n // --- Undo (reverse the delete + insert) ---\n chai.expect(b1.txns.isUndoPossible).to.be.true;\n b1.txns.reverseTxns(1);\n\n e1 = b1.elements.getElementProps(elem1Id!);\n chai.expect(e1.code.value).to.equal(\"CODE_A\");\n chai.expect(() => b1!.elements.getElementProps(newElemId!)).to.throw();\n\n // --- Redo (reinstate the delete + insert) ---\n chai.expect(b1.txns.isRedoPossible).to.be.true;\n b1.txns.reinstateTxn();\n\n chai.expect(() => b1!.elements.getElementProps(elem1Id!)).to.throw();\n const redoneElem = b1.elements.getElementProps(newElemId!);\n chai.expect(redoneElem.code.value).to.equal(\"CODE_A\");\n\n // --- Push changes ---\n await b1.pushChanges({ description: \"delete elem1 and insert new element with same code\" });\n await b2.pullChanges();\n\n // No local txns remain after push\n chai.expect(b1.txns.isUndoPossible).to.be.false;\n chai.expect(b1.txns.isRedoPossible).to.be.false;\n\n // Verify b2 sees the final state after pulling\n chai.expect(() => b2!.elements.getElementProps(elem1Id!)).to.throw();\n const b2NewElem = b2.elements.getElementProps(newElemId!);\n chai.expect(b2NewElem.code.value).to.equal(\"CODE_A\");\n\n } finally {\n b1?.close();\n b2?.close();\n HubMock.shutdown();\n }\n });\n\n it(\"single-transaction code swap reverts to old behaviour (swap not applied) when noUpdateLoop is true\", async () => {\n HubMock.startup(\"CodeSwapNoUpdateLoopTest\", KnownTestLocations.outputDir);\n let b1: BriefcaseDb | undefined;\n let b2: BriefcaseDb | undefined;\n\n try {\n // --- Setup iModel ---\n const iModelId = await HubMock.createNewIModel({\n accessToken: \"user1\",\n iTwinId: HubMock.iTwinId,\n iModelName: \"CodeSwapNoUpdateLoopTest\",\n description: \"CodeSwapNoUpdateLoopTest\",\n });\n\n b1 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user1\", iTwinId: HubMock.iTwinId, iModelId });\n b1.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n b2 = await HubWrappers.downloadAndOpenBriefcase({ accessToken: \"user2\", iTwinId: HubMock.iTwinId, iModelId });\n b2.channels.addAllowedChannel(ChannelControl.sharedChannelName);\n\n let drawingModelId: Id64String;\n let drawingCategoryId: Id64String;\n let codeSpecId: Id64String;\n let codeScopeId: Id64String;\n\n await b1.locks.acquireLocks({ shared: IModel.dictionaryId });\n withEditTxn(b1, \"setup model, category, codeSpec\", (txn) => {\n const modelCode = IModelTestUtils.getUniqueModelCode(b1!, \"DrawingModel\");\n const [, modelId] = IModelTestUtils.createAndInsertDrawingPartitionAndModel(txn, modelCode);\n drawingModelId = modelId;\n\n let catId = DrawingCategory.queryCategoryIdByName(b1!, IModel.dictionaryId, \"MyDrawingCategory\");\n if (undefined === catId)\n catId = DrawingCategory.insert(txn, IModel.dictionaryId, \"MyDrawingCategory\", new SubCategoryAppearance());\n drawingCategoryId = catId;\n\n codeSpecId = b1!.codeSpecs.insert(txn, \"MyCodeSpec\", CodeScopeSpec.Type.Model);\n codeScopeId = drawingModelId;\n });\n\n await b1.pushChanges({ description: \"setup\" });\n await b2.pullChanges();\n\n let elem1Id: Id64String;\n let elem2Id: Id64String;\n\n await b1.locks.acquireLocks({ shared: drawingModelId! });\n withEditTxn(b1, \"insert two coded elements\", (txn) => {\n const elem1Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" }),\n };\n const elem2Props: GeometricElement2dProps = {\n classFullName: \"BisCore:DrawingGraphic\",\n model: drawingModelId!,\n category: drawingCategoryId!,\n code: new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" }),\n };\n elem1Id = txn.insertElement(elem1Props);\n elem2Id = txn.insertElement(elem2Props);\n });\n\n await b1.pushChanges({ description: \"insert two elements with CODE_A and CODE_B\" });\n await b2.pullChanges();\n\n // --- Single-transaction swap: null elem1, then swap codes ---\n await b1.locks.acquireLocks({ exclusive: [elem2Id!, elem1Id!] });\n withEditTxn(b1, \"single-txn swap\", (txn) => {\n // Step 1: null out elem1's code to free the uniqueness slot\n const props = b1!.elements.getElementProps(elem1Id!);\n props.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"\" });\n txn.updateElement(props);\n\n // Step 2: assign CODE_A to elem2, CODE_B to elem1\n const props1 = b1!.elements.getElementProps(elem1Id!);\n const props2 = b1!.elements.getElementProps(elem2Id!);\n props2.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_A\" });\n props1.code = new Code({ spec: codeSpecId!, scope: codeScopeId!, value: \"CODE_B\" });\n txn.updateElement(props2);\n txn.updateElement(props1);\n });\n\n // Verify b1 has the swapped state\n b1.clearCaches();\n const b1e1 = b1.elements.getElementProps(elem1Id!);\n const b1e2 = b1.elements.getElementProps(elem2Id!);\n chai.expect(b1e1.code.value).to.equal(\"CODE_B\");\n chai.expect(b1e2.code.value).to.equal(\"CODE_A\");\n\n await b1.pushChanges({ description: \"single-txn code swap\" });\n\n // Pull with noUpdateLoop: true — this uses the old (pre-fix) apply path.\n // The unique-index conflict handler skips the intermediate null step,\n // so the swap is silently dropped and b2 retains the original codes.\n // noUpdateLoop is intentionally not in the public PullChangesArgs type.\n type InternalPullArgs = Parameters<BriefcaseDb[\"pullChanges\"]>[0] & { noUpdateLoop?: boolean };\n await b2.pullChanges({ noUpdateLoop: true } as InternalPullArgs);\n\n b2.clearCaches();\n const b2e1 = b2.elements.getElementProps(elem1Id!);\n const b2e2 = b2.elements.getElementProps(elem2Id!);\n // Old behaviour: swap did NOT land — b2 still sees the original codes\n chai.expect(b2e1.code.value).to.equal(\"CODE_A\");\n chai.expect(b2e2.code.value).to.equal(\"CODE_B\");\n\n } finally {\n b1?.close();\n b2?.close();\n HubMock.shutdown();\n }\n });\n});\n"]}
@@ -68,7 +68,7 @@ import { BriefcaseDb } from "../../IModelDb";
68
68
  import { HubMock } from "../../internal/HubMock";
69
69
  import { KnownTestLocations } from "../KnownTestLocations";
70
70
  import { IModelTestUtils } from "../IModelTestUtils";
71
- import { DisableNativeAssertions } from "../TestUtils";
71
+ import { DisableNativeAssertions, TestUtils } from "../TestUtils";
72
72
  import { samplePngTexture } from "../imageData";
73
73
  import { performance } from "perf_hooks";
74
74
  import { _cache, _hubAccess, _instanceKeyCache } from "../../internal/Symbols";
@@ -106,6 +106,11 @@ describe("iModel", () => {
106
106
  let originalEnv;
107
107
  before(async () => {
108
108
  originalEnv = { ...process.env };
109
+ // Several tests in this suite (ECEF location, geolocation, and coordinate reprojection) rely on
110
+ // GCS data loaded from cloud workspaces, so restart the backend with GCS workspaces enabled
111
+ // (the test harness disables them by default to avoid network calls).
112
+ await TestUtils.shutdownBackend();
113
+ await TestUtils.startBackend({ loadGcsWorkspaces: true });
109
114
  IModelTestUtils.registerTestBimSchema();
110
115
  imodel1 = await generateTestSnapshot("test.bim", "test.bim");
111
116
  imodel2 = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("IModel", "CompatibilityTestSeed.bim"), IModelTestUtils.resolveAssetFile("CompatibilityTestSeed.bim"));
@@ -113,13 +118,16 @@ describe("iModel", () => {
113
118
  imodel4 = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("IModel", "GetSetAutoHandledArrayProperties.bim"), IModelTestUtils.resolveAssetFile("GetSetAutoHandledArrayProperties.bim"));
114
119
  imodel5 = IModelTestUtils.createSnapshotFromSeed(IModelTestUtils.prepareOutputFile("IModel", "mirukuru.ibim"), IModelTestUtils.resolveAssetFile("mirukuru.ibim"));
115
120
  });
116
- after(() => {
121
+ after(async () => {
117
122
  process.env = originalEnv;
118
123
  imodel1.close();
119
124
  imodel2.close();
120
125
  imodel3.close();
121
126
  imodel4.close();
122
127
  imodel5.close();
128
+ // Restore the default test backend so subsequent test suites aren't left with GCS enabled.
129
+ await TestUtils.shutdownBackend();
130
+ await TestUtils.startBackend();
123
131
  });
124
132
  afterEach(() => {
125
133
  sinon.restore();