@itwin/core-backend 5.10.0-dev.18 → 5.10.0-dev.20

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 (95) hide show
  1. package/lib/cjs/BriefcaseManager.d.ts +8 -1
  2. package/lib/cjs/BriefcaseManager.d.ts.map +1 -1
  3. package/lib/cjs/BriefcaseManager.js.map +1 -1
  4. package/lib/cjs/ChangesetReader.d.ts +2 -2
  5. package/lib/cjs/ChangesetReader.js +2 -2
  6. package/lib/cjs/ChangesetReader.js.map +1 -1
  7. package/lib/cjs/ClassRegistry.d.ts +3 -3
  8. package/lib/cjs/ClassRegistry.js +3 -3
  9. package/lib/cjs/ClassRegistry.js.map +1 -1
  10. package/lib/cjs/Element.d.ts +6 -3
  11. package/lib/cjs/Element.d.ts.map +1 -1
  12. package/lib/cjs/Element.js +6 -3
  13. package/lib/cjs/Element.js.map +1 -1
  14. package/lib/cjs/Entity.d.ts +13 -5
  15. package/lib/cjs/Entity.d.ts.map +1 -1
  16. package/lib/cjs/Entity.js +13 -5
  17. package/lib/cjs/Entity.js.map +1 -1
  18. package/lib/cjs/IModelDb.d.ts +79 -17
  19. package/lib/cjs/IModelDb.d.ts.map +1 -1
  20. package/lib/cjs/IModelDb.js +181 -22
  21. package/lib/cjs/IModelDb.js.map +1 -1
  22. package/lib/cjs/Relationship.d.ts +3 -1
  23. package/lib/cjs/Relationship.d.ts.map +1 -1
  24. package/lib/cjs/Relationship.js +3 -1
  25. package/lib/cjs/Relationship.js.map +1 -1
  26. package/lib/cjs/internal/ElementLRUCache.d.ts.map +1 -1
  27. package/lib/cjs/internal/ElementLRUCache.js +23 -4
  28. package/lib/cjs/internal/ElementLRUCache.js.map +1 -1
  29. package/lib/cjs/internal/workspace/SettingsSchemasImpl.d.ts.map +1 -1
  30. package/lib/cjs/internal/workspace/SettingsSchemasImpl.js +54 -9
  31. package/lib/cjs/internal/workspace/SettingsSchemasImpl.js.map +1 -1
  32. package/lib/cjs/workspace/SettingsSchemas.d.ts +15 -1
  33. package/lib/cjs/workspace/SettingsSchemas.d.ts.map +1 -1
  34. package/lib/cjs/workspace/SettingsSchemas.js.map +1 -1
  35. package/lib/esm/BriefcaseManager.d.ts +8 -1
  36. package/lib/esm/BriefcaseManager.d.ts.map +1 -1
  37. package/lib/esm/BriefcaseManager.js.map +1 -1
  38. package/lib/esm/ChangesetReader.d.ts +2 -2
  39. package/lib/esm/ChangesetReader.js +2 -2
  40. package/lib/esm/ChangesetReader.js.map +1 -1
  41. package/lib/esm/ClassRegistry.d.ts +3 -3
  42. package/lib/esm/ClassRegistry.js +3 -3
  43. package/lib/esm/ClassRegistry.js.map +1 -1
  44. package/lib/esm/Element.d.ts +6 -3
  45. package/lib/esm/Element.d.ts.map +1 -1
  46. package/lib/esm/Element.js +6 -3
  47. package/lib/esm/Element.js.map +1 -1
  48. package/lib/esm/Entity.d.ts +13 -5
  49. package/lib/esm/Entity.d.ts.map +1 -1
  50. package/lib/esm/Entity.js +13 -5
  51. package/lib/esm/Entity.js.map +1 -1
  52. package/lib/esm/IModelDb.d.ts +79 -17
  53. package/lib/esm/IModelDb.d.ts.map +1 -1
  54. package/lib/esm/IModelDb.js +182 -23
  55. package/lib/esm/IModelDb.js.map +1 -1
  56. package/lib/esm/Relationship.d.ts +3 -1
  57. package/lib/esm/Relationship.d.ts.map +1 -1
  58. package/lib/esm/Relationship.js +3 -1
  59. package/lib/esm/Relationship.js.map +1 -1
  60. package/lib/esm/internal/ElementLRUCache.d.ts.map +1 -1
  61. package/lib/esm/internal/ElementLRUCache.js +23 -4
  62. package/lib/esm/internal/ElementLRUCache.js.map +1 -1
  63. package/lib/esm/internal/workspace/SettingsSchemasImpl.d.ts.map +1 -1
  64. package/lib/esm/internal/workspace/SettingsSchemasImpl.js +54 -9
  65. package/lib/esm/internal/workspace/SettingsSchemasImpl.js.map +1 -1
  66. package/lib/esm/test/ElementLRUCache.test.js +60 -0
  67. package/lib/esm/test/ElementLRUCache.test.js.map +1 -1
  68. package/lib/esm/test/imodel/IModel.test.js +31 -0
  69. package/lib/esm/test/imodel/IModel.test.js.map +1 -1
  70. package/lib/esm/test/schema/ClassRegistry.test.js +3 -0
  71. package/lib/esm/test/schema/ClassRegistry.test.js.map +1 -1
  72. package/lib/esm/test/schema/IModelSchemaContext.test.js +2 -0
  73. package/lib/esm/test/schema/IModelSchemaContext.test.js.map +1 -1
  74. package/lib/esm/test/schema/SchemaViewHidden.test.d.ts +2 -0
  75. package/lib/esm/test/schema/SchemaViewHidden.test.d.ts.map +1 -0
  76. package/lib/esm/test/schema/SchemaViewHidden.test.js +275 -0
  77. package/lib/esm/test/schema/SchemaViewHidden.test.js.map +1 -0
  78. package/lib/esm/test/schema/SchemaViewKoQ.test.d.ts +2 -0
  79. package/lib/esm/test/schema/SchemaViewKoQ.test.d.ts.map +1 -0
  80. package/lib/esm/test/schema/SchemaViewKoQ.test.js +175 -0
  81. package/lib/esm/test/schema/SchemaViewKoQ.test.js.map +1 -0
  82. package/lib/esm/test/schema/SchemaViewLifecycle.test.d.ts +2 -0
  83. package/lib/esm/test/schema/SchemaViewLifecycle.test.d.ts.map +1 -0
  84. package/lib/esm/test/schema/SchemaViewLifecycle.test.js +141 -0
  85. package/lib/esm/test/schema/SchemaViewLifecycle.test.js.map +1 -0
  86. package/lib/esm/test/schema/SchemaViewValidation.test.d.ts +2 -0
  87. package/lib/esm/test/schema/SchemaViewValidation.test.d.ts.map +1 -0
  88. package/lib/esm/test/schema/SchemaViewValidation.test.js +475 -0
  89. package/lib/esm/test/schema/SchemaViewValidation.test.js.map +1 -0
  90. package/lib/esm/test/standalone/SettingsSchemas.test.js +382 -0
  91. package/lib/esm/test/standalone/SettingsSchemas.test.js.map +1 -1
  92. package/lib/esm/workspace/SettingsSchemas.d.ts +15 -1
  93. package/lib/esm/workspace/SettingsSchemas.d.ts.map +1 -1
  94. package/lib/esm/workspace/SettingsSchemas.js.map +1 -1
  95. package/package.json +14 -14
@@ -165,6 +165,7 @@ class IModelDb extends core_common_1.IModel {
165
165
  _jsClassMap;
166
166
  _schemaMap;
167
167
  _schemaContext;
168
+ _schemasPromise;
168
169
  /** @deprecated in 5.0.0 - will not be removed until after 2026-06-13. Use [[fonts]]. */
169
170
  _fontMap; // eslint-disable-line @typescript-eslint/no-deprecated
170
171
  _fonts = (0, IModelDbFontsImpl_1.createIModelDbFonts)(this);
@@ -797,6 +798,11 @@ class IModelDb extends core_common_1.IModel {
797
798
  this._jsClassMap = undefined;
798
799
  this._schemaMap = undefined;
799
800
  this._schemaContext = undefined;
801
+ if (this._schemasPromise) {
802
+ const old = this._schemasPromise;
803
+ this._schemasPromise = undefined;
804
+ old.then((view) => view.markOutdated()).catch(() => { });
805
+ }
800
806
  this[Symbols_1._nativeDb].clearECDbCache();
801
807
  }
802
808
  this.elements[Symbols_1._cache].clear();
@@ -1237,7 +1243,7 @@ class IModelDb extends core_common_1.IModel {
1237
1243
  }
1238
1244
  /** The registry of entity metadata for this iModel.
1239
1245
  * @internal
1240
- * @deprecated in 5.0 - will not be removed until after 2026-06-13. Please use `schemaContext` from the `iModel` instead.
1246
+ * @deprecated in 5.0 - will not be removed until after 2026-06-13. Use `getSchemaView()` from the `iModel` instead.
1241
1247
  *
1242
1248
  * @example
1243
1249
  * ```typescript
@@ -1245,7 +1251,8 @@ class IModelDb extends core_common_1.IModel {
1245
1251
  * const classMetaData: EntityMetaData | undefined = iModel.classMetaDataRegistry.find("SchemaName:ClassName");
1246
1252
  *
1247
1253
  * // Replacement:
1248
- * const metaData: EntityClass | undefined = imodel.schemaContext.getSchemaItemSync("SchemaName.ClassName", EntityClass);
1254
+ * const view = await imodel.getSchemaView();
1255
+ * const cls = view.findClass("SchemaName:ClassName");
1249
1256
  * ```
1250
1257
  */
1251
1258
  // eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -1272,7 +1279,11 @@ class IModelDb extends core_common_1.IModel {
1272
1279
  return this._schemaMap;
1273
1280
  }
1274
1281
  /**
1275
- * Gets the context that allows accessing the metadata (ecschema-metadata package) of this iModel
1282
+ * Gets the context that allows accessing the metadata (`@itwin/ecschema-metadata` package) of this iModel.
1283
+ *
1284
+ * For runtime read-only access - class/property iteration, IS-A checks, navigating relationships, KOQ lookups -
1285
+ * prefer [[getSchemaView]]. `schemaContext` remains the right choice when you need schema authoring
1286
+ * (via `@itwin/ecschema-editing`), custom-attribute deserialization, or the full ecschema-metadata object graph.
1276
1287
  * @public @preview
1277
1288
  */
1278
1289
  get schemaContext() {
@@ -1286,6 +1297,55 @@ class IModelDb extends core_common_1.IModel {
1286
1297
  }
1287
1298
  return this._schemaContext;
1288
1299
  }
1300
+ /** Get the schema view for this iModel. The view is built lazily on
1301
+ * first call by fetching compact binary schema data via `PRAGMA schema_view` through
1302
+ * the ConcurrentQuery thread pool. Subsequent calls return the cached view. Multiple
1303
+ * concurrent callers share a single in-flight build.
1304
+ *
1305
+ * The returned `SchemaView` is a lightweight, read-only, synchronous API for
1306
+ * navigating schema metadata - classes, properties, relationships, enumerations, etc.
1307
+ * It is the recommended default for runtime read-only metadata access and is significantly
1308
+ * faster and lower-memory than [[schemaContext]]. Use [[schemaContext]] for schema authoring,
1309
+ * custom-attribute deserialization, or anywhere you need the full ecschema-metadata object graph.
1310
+ * @beta
1311
+ */
1312
+ async getSchemaView() {
1313
+ if (this._schemasPromise) {
1314
+ const ctx = await this._schemasPromise;
1315
+ if (!ctx.isOutdated)
1316
+ return ctx;
1317
+ }
1318
+ // Capture the in-flight promise locally so the rejection handler only clears
1319
+ // `_schemasPromise` if it still points at this build. A concurrent invalidation +
1320
+ // re-fetch could otherwise replace the field before our hydrate fails, and a naive
1321
+ // `_schemasPromise = undefined` would clobber that newer reference.
1322
+ const inflight = this._hydrateSchemas();
1323
+ this._schemasPromise = inflight;
1324
+ inflight.catch(() => {
1325
+ if (this._schemasPromise === inflight)
1326
+ this._schemasPromise = undefined;
1327
+ });
1328
+ return inflight;
1329
+ }
1330
+ async _hydrateSchemas() {
1331
+ // PRAGMA returns exactly one row with format, formatVersion, data (binary), schemaToken.
1332
+ // Important: only call reader.next() once - do NOT use `for await` on PRAGMA results.
1333
+ // ConcurrentQuery wraps regular ECSQL in LIMIT/OFFSET for pagination but skips this for
1334
+ // PRAGMAs. If the serialized result exceeds the memory threshold, the response is marked
1335
+ // "Partial", and a `for await` loop would re-issue the same PRAGMA forever since PRAGMAs
1336
+ // don't support OFFSET-based pagination.
1337
+ // This implementation uses the non-pinned version of the pragma other than frontend - because backend
1338
+ // is always strictly coupled with the native code.
1339
+ const reader = this.createQueryReader("PRAGMA schema_view");
1340
+ const result = await reader.next();
1341
+ if (result.done)
1342
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "PRAGMA schema_view returned no rows");
1343
+ const data = result.value.data;
1344
+ const token = result.value.schemaToken;
1345
+ if (data === undefined || data === null)
1346
+ throw new core_common_1.IModelError(core_bentley_1.DbResult.BE_SQLITE_ERROR, "PRAGMA schema_view returned null data column");
1347
+ return ecschema_metadata_1.SchemaView.fromBinary(data, token ?? "");
1348
+ }
1289
1349
  /** Get the linkTableRelationships for this IModel */
1290
1350
  get relationships() {
1291
1351
  return this._relationships || (this._relationships = new Relationship_1.Relationships(this));
@@ -1360,7 +1420,7 @@ class IModelDb extends core_common_1.IModel {
1360
1420
  }
1361
1421
  /** Get metadata for a class. This method will load the metadata from the iModel into the cache as a side-effect, if necessary.
1362
1422
  * @throws [[IModelError]] if the metadata cannot be found nor loaded.
1363
- * @deprecated in 5.0 - will not be removed until after 2026-06-13. Please use `getSchemaItem` from `SchemaContext` class instead.
1423
+ * @deprecated in 5.0 - will not be removed until after 2026-06-13. Use `getSchemaView()` on the iModel and call `view.findClass(...)` instead.
1364
1424
  *
1365
1425
  * @example
1366
1426
  * * ```typescript
@@ -1368,7 +1428,8 @@ class IModelDb extends core_common_1.IModel {
1368
1428
  * const metaData: EntityMetaData = imodel.getMetaData("SchemaName:ClassName");
1369
1429
  *
1370
1430
  * // Replacement:
1371
- * const metaData: EntityClass | undefined = imodel.schemaContext.getSchemaItemSync("SchemaName", "ClassName", EntityClass);
1431
+ * const view = await imodel.getSchemaView();
1432
+ * const cls = view.findClass("SchemaName:ClassName");
1372
1433
  * ```
1373
1434
  */
1374
1435
  // eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -1386,7 +1447,7 @@ class IModelDb extends core_common_1.IModel {
1386
1447
  return metadata;
1387
1448
  }
1388
1449
  /** Identical to [[getMetaData]], except it returns `undefined` instead of throwing an error if the metadata cannot be found nor loaded.
1389
- * @deprecated in 5.0 - will not be removed until after 2026-06-13. Please use `getSchemaItem` from `SchemaContext` class instead.
1450
+ * @deprecated in 5.0 - will not be removed until after 2026-06-13. Use `getSchemaView()` on the iModel and call `view.findClass(...)` instead.
1390
1451
  *
1391
1452
  * @example
1392
1453
  * * ```typescript
@@ -1394,7 +1455,8 @@ class IModelDb extends core_common_1.IModel {
1394
1455
  * const metaData: EntityMetaData | undefined = imodel.tryGetMetaData("SchemaName:ClassName");
1395
1456
  *
1396
1457
  * // Replacement:
1397
- * const metaData: EntityClass | undefined = imodel.schemaContext.getSchemaItemSync("SchemaName.ClassName", EntityClass);
1458
+ * const view = await imodel.getSchemaView();
1459
+ * const cls = view.findClass("SchemaName:ClassName");
1398
1460
  * ```
1399
1461
  */
1400
1462
  // eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -1414,7 +1476,7 @@ class IModelDb extends core_common_1.IModel {
1414
1476
  * @param func The callback to be invoked on each property
1415
1477
  * @param includeCustom If true (default), include custom-handled properties in the iteration. Otherwise, skip custom-handled properties.
1416
1478
  * @note Custom-handled properties are core properties that have behavior enforced by C++ handlers.
1417
- * @deprecated in 5.0 - will not be removed until after 2026-06-13. Please use `forEachProperty` instead.
1479
+ * @deprecated in 5.0 - will not be removed until after 2026-06-13. Use `getSchemaView()` on the iModel and iterate `view.findClass(classFullName)?.getProperties()` instead.
1418
1480
  *
1419
1481
  * @example
1420
1482
  * ```typescript
@@ -1424,9 +1486,10 @@ class IModelDb extends core_common_1.IModel {
1424
1486
  * }, false);
1425
1487
  *
1426
1488
  * // Replacement:
1427
- * await IModelDb.forEachProperty(imodel, "TestDomain.TestDomainClass", true, (propName: string, property: Property) => {
1428
- * console.log(`Property name: ${propName}, Property type: ${property.propertyType}`);
1429
- * }, false);
1489
+ * const view = await imodel.getSchemaView();
1490
+ * for (const property of view.findClass("BisCore:Element")?.getProperties() ?? []) {
1491
+ * console.log(`Property name: ${property.name}, Kind: ${property.kind}`);
1492
+ * }
1430
1493
  * ```
1431
1494
  */
1432
1495
  // eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -1440,7 +1503,7 @@ class IModelDb extends core_common_1.IModel {
1440
1503
  * @param func The callback to be invoked on each property
1441
1504
  * @param includeCustom If true (default), include custom-handled properties in the iteration. Otherwise, skip custom-handled properties.
1442
1505
  * @note Custom-handled properties are core properties that have behavior enforced by C++ handlers.
1443
- * @deprecated in 5.0 - will not be removed until after 2026-06-13. Use `forEachProperty` from `SchemaContext` class instead.
1506
+ * @deprecated in 5.0 - will not be removed until after 2026-06-13. Use `getSchemaView()` on the iModel and iterate `view.findClass(classFullName)?.getProperties()` instead.
1444
1507
  *
1445
1508
  * @example
1446
1509
  * ```typescript
@@ -1450,9 +1513,10 @@ class IModelDb extends core_common_1.IModel {
1450
1513
  * });
1451
1514
  *
1452
1515
  * // Replacement:
1453
- * imodel.schemaContext.forEachProperty("BisCore:Element", true, (propName: string, property: Property) => {
1454
- * console.log(`Property name: ${propName}, Property type: ${property.propertyType}`);
1455
- * });
1516
+ * const view = await imodel.getSchemaView();
1517
+ * for (const property of view.findClass("BisCore:Element")?.getProperties() ?? []) {
1518
+ * console.log(`Property name: ${property.name}, Kind: ${property.kind}`);
1519
+ * }
1456
1520
  * ```
1457
1521
  */
1458
1522
  // eslint-disable-next-line @typescript-eslint/no-deprecated
@@ -3339,7 +3403,63 @@ class BriefcaseDb extends IModelDb {
3339
3403
  async getAllChangesetHealthData() {
3340
3404
  return this[Symbols_1._nativeDb].getAllChangesetHealthData();
3341
3405
  }
3342
- /** Revert timeline changes and then push resulting changeset */
3406
+ /**
3407
+ * Whether file-based transactions are enabled for this briefcase.
3408
+ *
3409
+ * When enabled, transaction data is stored in separate temporary `.txn` files rather than in the
3410
+ * briefcase's internal transaction table. This avoids SQLite blob size limits and reduces memory
3411
+ * pressure for very large changesets, at the cost of additional disk I/O.
3412
+ * @see [[enableFileBasedTxns]] to enable, [[disableFileBasedTxns]] to disable.
3413
+ * @internal
3414
+ */
3415
+ get isFileBasedTxnsEnabled() {
3416
+ return this[Symbols_1._nativeDb].queryLocalValue("fileBasedTxns") === "1";
3417
+ }
3418
+ /**
3419
+ * Enable file-based transactions for this briefcase.
3420
+ * @throws IModelError with [[ChangeSetStatus.HasUncommittedChanges]] if there are unsaved changes.
3421
+ * @throws IModelError with [[ChangeSetStatus.HasLocalChanges]] if there are pending transactions.
3422
+ * @internal
3423
+ */
3424
+ enableFileBasedTxns() {
3425
+ this._setFileBasedTxnsSetting(true);
3426
+ }
3427
+ /**
3428
+ * Disable file-based transactions for this briefcase, reverting to the default storage mode
3429
+ * (transactions stored within the briefcase's internal transaction table).
3430
+ * @throws IModelError with [[ChangeSetStatus.HasUncommittedChanges]] if there are unsaved changes.
3431
+ * @throws IModelError with [[ChangeSetStatus.HasLocalChanges]] if there are pending transactions.
3432
+ * @internal
3433
+ */
3434
+ disableFileBasedTxns() {
3435
+ this._setFileBasedTxnsSetting(false);
3436
+ }
3437
+ _setFileBasedTxnsSetting(enabled) {
3438
+ if (this.isFileBasedTxnsEnabled === enabled)
3439
+ return;
3440
+ const nativeDb = this[Symbols_1._nativeDb];
3441
+ if (nativeDb.hasUnsavedChanges())
3442
+ throw new core_common_1.IModelError(core_bentley_1.ChangeSetStatus.HasUncommittedChanges, "Cannot change file-based transactions setting while there are unsaved changes");
3443
+ if (nativeDb.hasPendingTxns())
3444
+ throw new core_common_1.IModelError(core_bentley_1.ChangeSetStatus.HasLocalChanges, "Cannot change file-based transactions setting while there are pending transactions");
3445
+ if (enabled)
3446
+ nativeDb.saveLocalValue("fileBasedTxns", "1");
3447
+ else
3448
+ nativeDb.deleteLocalValue("fileBasedTxns");
3449
+ }
3450
+ /**
3451
+ * Revert timeline changes and push the resulting changeset.
3452
+ *
3453
+ * Pulls the latest changes, acquires the schema lock, reverts the inclusive range of
3454
+ * changesets `[toIndex..current]`, and pushes the revert as a new changeset. On failure,
3455
+ * follow the behavior specified by `arg.inCaseOfFailure`, which may discard local changes,
3456
+ * retain local changes, or delete the briefcase.
3457
+ *
3458
+ * @param arg - Arguments specifying the target changeset index, push options, access token, and failure handling behavior.
3459
+ * @throws IModelError with [[ChangeSetStatus.ApplyError]] if `toIndex` is not specified.
3460
+ * @throws IModelError with [[ChangeSetStatus.HasUncommittedChanges]] if there are unsaved changes.
3461
+ * @throws IModelError with [[ChangeSetStatus.HasLocalChanges]] if there are pending transactions.
3462
+ */
3343
3463
  async revertAndPushChanges(arg) {
3344
3464
  const nativeDb = this[Symbols_1._nativeDb];
3345
3465
  if (arg.toIndex === undefined) {
@@ -3371,11 +3491,14 @@ class BriefcaseDb extends IModelDb {
3371
3491
  if (nativeDb.schemaSyncEnabled()) {
3372
3492
  arg.skipSchemaChanges = true;
3373
3493
  }
3494
+ // The native side enables file-based txns during revert. Restore the original setting afterward.
3495
+ const wasFileBasedTxnsEnabled = this.isFileBasedTxnsEnabled;
3496
+ const preRevertIndex = this.changeset.index;
3374
3497
  try {
3375
3498
  await BriefcaseManager_1.BriefcaseManager.revertTimelineChanges(this, arg);
3376
- this[Symbols_1._nativeDb].saveChanges("Revert changes");
3499
+ nativeDb.saveChanges("Revert changes");
3377
3500
  if (!arg.description) {
3378
- arg.description = `Reverted changes from ${this.changeset.index} to ${arg.toIndex}${arg.skipSchemaChanges ? " (schema changes skipped)" : ""}`;
3501
+ arg.description = `Reverted changes from ${preRevertIndex} to ${arg.toIndex}${arg.skipSchemaChanges ? " (schema changes skipped)" : ""}`;
3379
3502
  }
3380
3503
  const pushArgs = {
3381
3504
  description: arg.description,
@@ -3390,13 +3513,49 @@ class BriefcaseDb extends IModelDb {
3390
3513
  this.clearCaches();
3391
3514
  }
3392
3515
  catch (err) {
3393
- if (!arg.retainLocks) {
3394
- await this.locks.releaseAllLocks();
3395
- throw err;
3516
+ const failureAction = arg.inCaseOfFailure ?? "revert";
3517
+ try {
3518
+ switch (failureAction) {
3519
+ case "revert":
3520
+ // Restore the briefcase to its pre-revert state: save any unsaved changes into txns,
3521
+ // reverse all txns, then delete them.
3522
+ nativeDb.saveChanges();
3523
+ if (nativeDb.hasPendingTxns())
3524
+ nativeDb.reverseAll();
3525
+ nativeDb.deleteAllTxns();
3526
+ break;
3527
+ case "delete":
3528
+ // Clear local changes first so lock release can succeed.
3529
+ nativeDb.abandonChanges();
3530
+ nativeDb.deleteAllTxns();
3531
+ if (!arg.retainLocks)
3532
+ await this.locks.releaseAllLocks();
3533
+ const filePath = this.pathName;
3534
+ this.close();
3535
+ await BriefcaseManager_1.BriefcaseManager.deleteBriefcaseFiles(filePath, arg.accessToken);
3536
+ break;
3537
+ case "retain":
3538
+ // Keep local changes as-is for caller inspection/recovery.
3539
+ nativeDb.saveChanges();
3540
+ break;
3541
+ }
3542
+ }
3543
+ catch (cleanupErr) {
3544
+ core_bentley_1.Logger.logError(loggerCategory, `Failed to clean up after revert error (action=${failureAction}): ${String(cleanupErr)}`);
3545
+ }
3546
+ if (!arg.retainLocks && this.isOpen) {
3547
+ try {
3548
+ await this.locks.releaseAllLocks();
3549
+ }
3550
+ catch (lockErr) {
3551
+ core_bentley_1.Logger.logError(loggerCategory, `Failed to release locks after revert failure (action=${failureAction}): ${String(lockErr)}`);
3552
+ }
3396
3553
  }
3554
+ throw err;
3397
3555
  }
3398
3556
  finally {
3399
- this[Symbols_1._nativeDb].abandonChanges();
3557
+ if (this.isOpen && !wasFileBasedTxnsEnabled && !nativeDb.hasPendingTxns() && !nativeDb.hasUnsavedChanges())
3558
+ this.disableFileBasedTxns();
3400
3559
  }
3401
3560
  }
3402
3561
  /**