@itwin/core-backend
Version:
iTwin.js backend components
545 lines • 26.2 kB
TypeScript
/** @packageDocumentation
* @module iModels
*/
import { BeEvent, DbConflictResolution, DbResult, Id64String, IModelStatus } from "@itwin/core-bentley";
import { ChangesetIndexAndId, EntityIdAndClassIdIterable, ModelGeometryChangesProps, ModelIdAndGeometryGuid } from "@itwin/core-common";
import { BriefcaseDb, SaveChangesArgs, StandaloneDb } from "./IModelDb";
import { RelationshipProps } from "./Relationship";
import { DbRebaseChangesetConflictArgs, RebaseChangesetConflictArgs } from "./internal/ChangesetConflictArgs";
/** A string that identifies a Txn.
* @public @preview
*/
export type TxnIdString = string;
/** An error generated during dependency validation.
* @see [[TxnManager.validationErrors]].
* @public @preview
*/
export interface ValidationError {
/** If true, txn is aborted. */
fatal: boolean;
/** The type of error. */
errorType: string;
/** Optional description of what went wrong. */
message?: string;
}
/** Describes a set of [[Element]]s or [[Model]]s that changed as part of a transaction.
* @see [[TxnManager.onElementsChanged]] and [[TxnManager.onModelsChanged]].
* @public @preview
*/
export interface TxnChangedEntities {
/** The entities that were inserted by the transaction. */
readonly inserts: EntityIdAndClassIdIterable;
/** The entities that were deleted by the transaction. */
readonly deletes: EntityIdAndClassIdIterable;
/** The entities that were modified by the transaction, including any [[Element]]s for which one of their [[ElementAspect]]s was changed. */
readonly updates: EntityIdAndClassIdIterable;
}
/** Arguments supplied to [[TxnManager.queryLocalChanges]].
* @beta
*/
export interface QueryLocalChangesArgs {
/** If supplied and non-empty, restricts the results to include only EC instances belonging to the specified classes or subclasses thereof. */
readonly includedClasses?: string[];
/** If `true`, include changes that have not yet been saved. */
readonly includeUnsavedChanges?: boolean;
}
/** Represents a change (insertion, deletion, or modification) to a single EC instance made in a local [[BriefcaseDb]].
* @see [[TxnManager.queryLocalChanges]] to iterate all of the changed instances.
* @beta
*/
export interface ChangeInstanceKey {
/** ECInstanceId of the instance. */
id: Id64String;
/** Fully-qualified class name of the instance. */
classFullName: string;
/** The type of change. */
changeType: "inserted" | "updated" | "deleted";
}
/** Strictly for tests. @internal */
export declare function setMaxEntitiesPerEvent(max: number): number;
/**
* @alpha
* Transaction types
*/
export type TxnType = "Data" | "ECSchema" | "Ddl";
/**
* @alpha
* Transaction modes
*/
export type TxnMode = "direct" | "indirect";
/**
* @alpha
* Represents the properties of a transaction within the transaction manager.
*
* @property id - The unique identifier for the transaction.
* @property sessionId - The identifier of the session to which the transaction belongs.
* @property nextId - (Optional) The identifier of the next transaction in the sequence.
* @property prevId - (Optional) The identifier of the previous transaction in the sequence.
* @property props - The arguments or properties associated with the save changes operation.
* @property type - The type of transaction, which can be "Data", "ECSchema", or "Ddl".
* @property reversed - Indicates whether the transaction has been reversed.
* @property grouped - Indicates whether the transaction is grouped with others.
* @property timestamp - The timestamp when the transaction was created.
*/
export interface TxnProps {
id: TxnIdString;
sessionId: number;
nextId?: TxnIdString;
prevId?: TxnIdString;
props: SaveChangesArgs;
type: TxnType;
reversed: boolean;
grouped: boolean;
timestamp: string;
}
/**
* Manages the process of merging and rebasing local changes (transactions) in a [[BriefcaseDb]] or [[StandaloneDb]].
*
* The `RebaseManager` coordinates the rebase of local transactions when pulling and merging changes from other sources,
* such as remote repositories or other users. It provides mechanisms to handle transaction conflicts, register custom conflict
* handlers, and manage the rebase workflow. This includes resuming rebases, invoking user-defined handlers for conflict resolution,
* and tracking the current merge/rebase state.
*
* Key responsibilities:
* - Orchestrates the rebase of local transactions after a pull/merge operation.
* - Allows registration and removal of custom conflict handlers to resolve changeset conflicts during rebase.
* - Provides methods to check the current merge/rebase state.
* - Raises events before and after each transaction is rebased.
* - Ensures changes are saved or aborted appropriately based on the outcome of the rebase process.
*
* @alpha
*/
export declare class RebaseManager {
private _iModel;
private _conflictHandlers?;
private _customHandler?;
private _aborting;
constructor(_iModel: BriefcaseDb | StandaloneDb);
/**
* Resumes the rebase process for the current iModel, applying any pending local changes
* on top of the latest pulled changes from the remote source.
*
* This method performs the following steps:
* 1. Begins the rebase process using the native database.
* 2. Iterates through each transaction that needs to be rebased:
* - Retrieves transaction properties.
* - Raises events before and after rebasing each transaction.
* - Optionally reinstates local changes based on the rebase handler.
* - Optionally recomputes transaction data using the rebase handler.
* - Updates the transaction in the native database.
* 3. Ends the rebase process and saves changes if the database is not read-only.
* 4. Drops any restore point associated with the pull-merge operation.
*
* If an error occurs during the process, the rebase is aborted and the error is rethrown.
*
* @throws {Error} If a transaction cannot be found or if any step in the rebase process fails.
*/
resume(): Promise<void>;
/**
* Determines whether the current transaction can be aborted.
*
* This method checks if a transaction is currently in progress and if a specific restore point,
* identified by `BriefcaseManager.PULL_MERGE_RESTORE_POINT_NAME`, exists in the briefcase manager.
*
* @returns {boolean} Returns `true` if a transaction is in progress and the required restore point exists; otherwise, returns `false`.
*/
canAbort(): boolean;
/**
* Aborts the current transaction by restoring the iModel to a predefined restore point. This method will
* automatically discard any unsaved changes before performing the restore.
*
* If a restore point is available (as determined by `canAbort()`), this method restores the iModel
* to the state saved at the restore point named by `BriefcaseManager.PULL_MERGE_RESTORE_POINT_NAME`.
* If no restore point is available, an error is thrown.
*
* @returns A promise that resolves when the restore operation is complete.
* @throws {Error} If there is no restore point to abort to.
*/
abort(): Promise<void>;
/**
* Sets the handler to be invoked for rebase operations.
*
* @param handler - The {@link RebaseHandler} to handle rebase events.
*/
setCustomHandler(handler: RebaseHandler): void;
/**
* Determines whether a transaction is currently in progress.
*
* @returns {boolean} Returns `true` if there is an active transaction stage, otherwise `false`.
*/
inProgress(): boolean;
/**
* Indicates whether the current transaction manager is in the process of aborting a transaction.
*
* @returns `true` if the transaction manager is currently aborting; otherwise, `false`.
*/
get isAborting(): boolean;
/**
* Indicates whether the current transaction manager is in the "Rebasing" stage.
*
* This property checks the internal native database's merge stage to determine if a rebase operation is in progress.
*
* @returns `true` if the transaction manager is currently rebasing; otherwise, `false`.
*/
get isRebasing(): boolean;
/**
* Indicates whether the current iModel is in the process of merging changes from a pull operation.
*
* @returns `true` if the iModel is currently merging changes; otherwise, `false`.
*/
get isMerging(): boolean;
/**
* Attempts to resolve a changeset conflict by invoking registered conflict handlers in sequence.
*
* Iterates through the linked list of conflict handlers, passing the provided conflict arguments to each handler.
* If a handler returns a defined resolution, logs the resolution and returns it immediately.
* If no handler resolves the conflict, returns `undefined`.
*
* @param args - The arguments describing the changeset conflict to resolve.
* @returns The conflict resolution provided by a handler, or `undefined` if no handler resolves the conflict.
*/
onConflict(args: RebaseChangesetConflictArgs): DbConflictResolution | undefined;
/**
* Registers a new conflict handler for rebase changeset conflicts.
*
* @param args - An object containing:
* - `id`: A unique identifier for the conflict handler.
* - `handler`: A function that handles rebase changeset conflicts and returns a `DbConflictResolution` or `undefined`.
* @throws IModelError if a conflict handler with the same `id` already exists.
*
* @remarks
* Conflict handlers are used during changeset rebase operations to resolve conflicts.
* Each handler must have a unique `id`. Attempting to register a handler with a duplicate `id` will result in an error.
*/
addConflictHandler(args: {
id: string;
handler: (args: RebaseChangesetConflictArgs) => DbConflictResolution | undefined;
}): void;
/**
* Removes a conflict handler from the internal linked list by its identifier.
*
* @param id - The unique identifier of the conflict handler to remove.
*
* If the handler with the specified `id` exists in the list, it will be removed.
* If no handler with the given `id` is found, the method does nothing.
*/
removeConflictHandler(id: string): void;
}
/** Manages local changes to a [[BriefcaseDb]] or [[StandaloneDb]] via [Txns]($docs/learning/InteractiveEditing.md)
* @public @preview
*/
export declare class TxnManager {
private _iModel;
/** @internal */
private _isDisposed;
/** @internal */
private _withIndirectChangeRefCounter;
/** @internal */
get isDisposed(): boolean;
/** @internal */
readonly rebaser: RebaseManager;
/** @internal */
constructor(_iModel: BriefcaseDb | StandaloneDb);
/** Array of errors from dependency propagation */
readonly validationErrors: ValidationError[];
private get _nativeDb();
private _getElementClass;
private _getRelationshipClass;
/** If a -watch file exists for this iModel, update its timestamp so watching processes can be
* notified that we've modified the briefcase.
* @internal Used by IModelDb on push/pull.
*/
touchWatchFile(): void;
/** @internal */
protected _onBeforeOutputsHandled(elClassName: string, elId: Id64String): void;
/** @internal */
protected _onAllInputsHandled(elClassName: string, elId: Id64String): void;
/** @internal */
protected _onRootChanged(props: RelationshipProps): void;
/** @internal */
protected _onDeletedDependency(props: RelationshipProps): void;
/** @internal */
protected _onBeginValidate(): void;
/** called from native code after validation of a Txn, either from saveChanges or apply changeset.
* @internal
*/
protected _onEndValidate(): void;
/** @internal */
protected _onGeometryChanged(modelProps: ModelGeometryChangesProps[]): void;
/** @internal */
protected _onGeometryGuidsChanged(changes: ModelIdAndGeometryGuid[]): void;
/** @internal */
protected _onCommit(): void;
/** @internal */
protected _onCommitted(): void;
/** @internal */
protected _onReplayExternalTxns(): void;
/** @internal */
protected _onReplayedExternalTxns(): void;
/** @internal */
protected _onChangesApplied(): void;
/** @internal */
protected _onBeforeUndoRedo(isUndo: boolean): void;
/** @internal */
protected _onAfterUndoRedo(isUndo: boolean): void;
/** @internal */
_onChangesPushed(changeset: ChangesetIndexAndId): void;
/** @internal */
_onChangesPulled(changeset: ChangesetIndexAndId): void;
private _onRebaseLocalTxnConflict;
/**
* @alpha
* Retrieves the txn properties for a given txn ID.
*
* @param id - The unique identifier of the transaction.
* @returns The properties of the transaction if found; otherwise, `undefined`.
*/
getTxnProps(id: TxnIdString): TxnProps | undefined;
/**
* @alpha
* Iterates over all transactions in the sequence, yielding each transaction's properties.
*
* @yields {TxnProps} The properties of each transaction in the sequence.
*/
queryTxns(): Generator<TxnProps>;
/**
* @alpha
* Retrieves the properties of the last saved txn via `IModelDb.saveChanges()`, if available.
*
* @returns The properties of the last saved txn, or `undefined` if none exist.
*/
getLastSavedTxnProps(): TxnProps | undefined;
/** Dependency handlers may call method this to report a validation error.
* @param error The error. If error.fatal === true, the transaction will cancel rather than commit.
*/
reportError(error: ValidationError): void;
/** Determine whether any fatal validation errors have occurred during dependency propagation. */
get hasFatalError(): boolean;
/** @internal */
readonly onEndValidation: BeEvent<() => void>;
/** Called after validation completes from [[IModelDb.saveChanges]].
* The argument to the event holds the list of elements that were inserted, updated, and deleted.
* @note If there are many changed elements in a single Txn, the notifications are sent in batches so this event *may be called multiple times* per Txn.
*/
readonly onElementsChanged: BeEvent<(changes: TxnChangedEntities) => void>;
/** Called after validation completes from [[IModelDb.saveChanges]].
* The argument to the event holds the list of models that were inserted, updated, and deleted.
* @note If there are many changed models in a single Txn, the notifications are sent in batches so this event *may be called multiple times* per Txn.
*/
readonly onModelsChanged: BeEvent<(changes: TxnChangedEntities) => void>;
/** Event raised after the geometry within one or more [[GeometricModel]]s is modified by applying a changeset or validation of a transaction.
* A model's geometry can change as a result of:
* - Insertion or deletion of a geometric element within the model; or
* - Modification of an existing element's geometric properties; or
* - An explicit request to flag it as changed via [[IModelDb.Models.updateModel]].
*/
readonly onModelGeometryChanged: BeEvent<(changes: ReadonlyArray<ModelIdAndGeometryGuid>) => void>;
readonly onGeometryChanged: BeEvent<(models: ModelGeometryChangesProps[]) => void>;
/** Event raised before a commit operation is performed. Initiated by a call to [[IModelDb.saveChanges]], unless there are no changes to save. */
readonly onCommit: BeEvent<() => void>;
/** Event raised after a commit operation has been performed. Initiated by a call to [[IModelDb.saveChanges]], even if there were no changes to save. */
readonly onCommitted: BeEvent<() => void>;
/** Event raised after a ChangeSet has been applied to this briefcase */
readonly onChangesApplied: BeEvent<() => void>;
/** Event raised before an undo/redo operation is performed. */
readonly onBeforeUndoRedo: BeEvent<(isUndo: boolean) => void>;
/** Event raised after an undo/redo operation has been performed.
* @param _action The action that was performed.
*/
readonly onAfterUndoRedo: BeEvent<(isUndo: boolean) => void>;
/** Event raised for a read-only briefcase that was opened with the `watchForChanges` flag enabled when changes made by another connection are applied to the briefcase.
* @see [[onReplayedExternalTxns]] for the event raised after all such changes have been applied.
*/
readonly onReplayExternalTxns: BeEvent<() => void>;
/** Event raised for a read-only briefcase that was opened with the `watchForChanges` flag enabled when changes made by another connection are applied to the briefcase.
* @see [[onReplayExternalTxns]] for the event raised before the changes are applied.
*/
readonly onReplayedExternalTxns: BeEvent<() => void>;
/**
* @alpha
* Event raised when a rebase transaction begins.
*/
readonly onRebaseTxnBegin: BeEvent<(txn: TxnProps) => void>;
/**
* @alpha
* Event raised when a rebase transaction ends.
*/
readonly onRebaseTxnEnd: BeEvent<(txn: TxnProps) => void>;
/**
* @alpha
* Event raised when a rebase begins.
*/
readonly onRebaseBegin: BeEvent<(txns: TxnIdString[]) => void>;
/**
* @alpha
* Event raised when a rebase ends.
*/
readonly onRebaseEnd: BeEvent<() => void>;
/** Event raised after changes are pulled from iModelHub.
* @see [[BriefcaseDb.pullChanges]].
*/
readonly onChangesPulled: BeEvent<(parentChangeset: ChangesetIndexAndId) => void>;
/** Event raised after changes are pushed to iModelHub.
* @see [[BriefcaseDb.pushChanges]].
*/
readonly onChangesPushed: BeEvent<(parentChangeset: ChangesetIndexAndId) => void>;
/**
* if handler is set and it does not return undefined then default handler will not be called
* @internal
* */
appCustomConflictHandler?: (args: DbRebaseChangesetConflictArgs) => DbConflictResolution | undefined;
/**
* Restart the current TxnManager session. This causes all Txns in the current session to no longer be undoable (as if the file was closed
* and reopened.)
* @note This can be quite disconcerting to the user expecting to be able to undo previously made changes. It should only be used
* under extreme circumstances where damage to the file or session could happen if the currently committed are reversed. Use sparingly and with care.
* Probably a good idea to alert the user it happened.
*/
restartSession(): void;
/** Determine whether current txn is propagating indirect changes or not. */
get isIndirectChanges(): boolean;
/** Determine if there are currently any reversible (undoable) changes from this editing session. */
get isUndoPossible(): boolean;
/** Determine if there are currently any reinstatable (redoable) changes */
get isRedoPossible(): boolean;
/** Get the description of the operation that would be reversed by calling reverseTxns(1).
* This is useful for showing the operation that would be undone, for example in a menu.
*/
getUndoString(): string;
/** Get a description of the operation that would be reinstated by calling reinstateTxn.
* This is useful for showing the operation that would be redone, in a pull-down menu for example.
*/
getRedoString(): string;
/** Begin a new multi-Txn operation. This can be used to cause a series of Txns that would normally
* be considered separate actions for undo to be grouped into a single undoable operation. This means that when reverseTxns(1) is called,
* the entire group of changes are undone together. Multi-Txn operations can be nested and until the outermost operation is closed
* all changes constitute a single operation.
* @note This method must always be paired with a call to endMultiTxnAction.
*/
beginMultiTxnOperation(): DbResult;
/** End a multi-Txn operation */
endMultiTxnOperation(): DbResult;
/** Return the depth of the multi-Txn stack. Generally for diagnostic use only. */
getMultiTxnOperationDepth(): number;
/** Reverse (undo) the most recent operation(s) to this IModelDb.
* @param numOperations the number of operations to reverse. If this is greater than 1, the entire set of operations will
* be reinstated together when/if ReinstateTxn is called.
* @note If there are any outstanding uncommitted changes, they are reversed.
* @note The term "operation" is used rather than Txn, since multiple Txns can be grouped together via [[beginMultiTxnOperation]]. So,
* even if numOperations is 1, multiple Txns may be reversed if they were grouped together when they were made.
* @note If numOperations is too large only the operations are reversible are reversed.
*/
reverseTxns(numOperations: number): IModelStatus;
/** Reverse the most recent operation. */
reverseSingleTxn(): IModelStatus;
/** Reverse all changes back to the beginning of the session. */
reverseAll(): IModelStatus;
/** Reverse all changes back to a previously saved TxnId.
* @param txnId a TxnId obtained from a previous call to GetCurrentTxnId.
* @returns Success if the transactions were reversed, error status otherwise.
* @see [[getCurrentTxnId]] [[cancelTo]]
*/
reverseTo(txnId: TxnIdString): IModelStatus;
/** Reverse and then cancel (make non-reinstatable) all changes back to a previous TxnId.
* @param txnId a TxnId obtained from a previous call to [[getCurrentTxnId]]
* @returns Success if the transactions were reversed and cleared, error status otherwise.
*/
cancelTo(txnId: TxnIdString): IModelStatus;
/** Reinstate the most recently reversed transaction. Since at any time multiple transactions can be reversed, it
* may take multiple calls to this method to reinstate all reversed operations.
* @returns Success if a reversed transaction was reinstated, error status otherwise.
* @note If there are any outstanding uncommitted changes, they are canceled before the Txn is reinstated.
*/
reinstateTxn(): IModelStatus;
/** Get the Id of the first transaction, if any.
*/
queryFirstTxnId(): TxnIdString;
/** Get the successor of the specified TxnId */
queryNextTxnId(txnId: TxnIdString): TxnIdString;
/** Get the predecessor of the specified TxnId */
queryPreviousTxnId(txnId: TxnIdString): TxnIdString;
/** Get the Id of the current (tip) transaction. */
getCurrentTxnId(): TxnIdString;
/**
* @alpha
* Get the Id of the current session.
*/
getCurrentSessionId(): number;
/** Get the description that was supplied when the specified transaction was saved. */
getTxnDescription(txnId: TxnIdString): string;
/** Test if a TxnId is valid */
isTxnIdValid(txnId: TxnIdString): boolean;
/** Query if there are any pending Txns in this IModelDb that are waiting to be pushed.
* @see [[IModelDb.pushChanges]]
*/
get hasPendingTxns(): boolean;
/**
* Query if there are any changes in memory that have yet to be saved to the IModelDb.
* @see [[IModelDb.saveChanges]]
*/
get hasUnsavedChanges(): boolean;
/**
* @alpha
* Query if there are any pending schema changes in this IModelDb.
*/
get hasPendingSchemaChanges(): boolean;
/**
* Query if there are changes in memory that have not been saved to the iModelDb or if there are Txns that are waiting to be pushed.
* @see [[IModelDb.saveChanges]]
* @see [[IModelDb.pushChanges]]
*/
get hasLocalChanges(): boolean;
/** Destroy the record of all local changes that have yet to be saved and/or pushed.
* This permanently eradicates your changes - use with caution!
* Typically, callers will want to subsequently use [[LockControl.releaseAllLocks]].
* After calling this function, [[hasLocalChanges]], [[hasPendingTxns]], and [[hasUnsavedChanges]] will all be `false`.
*/
deleteAllTxns(): void;
/** Obtain a list of the EC instances that have been changed locally by the [[BriefcaseDb]] associated with this `TxnManager` and have not yet been pushed to the iModel.
* @beta
*/
queryLocalChanges(args?: QueryLocalChangesArgs): Iterable<ChangeInstanceKey>;
/** Query the number of bytes of memory currently allocated by SQLite to keep track of
* changes to the iModel, for debugging/diagnostic purposes, as reported by [sqlite3session_memory_used](https://www.sqlite.org/session/sqlite3session_memory_used.html).
*/
getChangeTrackingMemoryUsed(): number;
/**
* @alpha
* Get the current transaction mode.
* @returns The current transaction mode, either "direct" or "indirect".
*/
getMode(): TxnMode;
/**
* @alpha
* Execute a series of changes in an indirect transaction.
* @param callback The function containing the changes to make.
*/
withIndirectTxnMode(callback: () => void): void;
/**
* @alpha
* Execute a series of changes in an indirect transaction.
* @param callback The function containing the changes to make.
*/
withIndirectTxnModeAsync(callback: () => Promise<void>): Promise<void>;
}
/**
* Interface for handling rebase operations on transactions.
* @alpha
*/
export interface RebaseHandler {
/**
* Determine whether a transaction should be reinstated during a rebase operation.
* @param txn The transaction to check.
*
* @alpha
*/
shouldReinstate(txn: TxnProps): boolean;
/**
* Recompute the changes for a given transaction.
* @param txn The transaction to recompute.
*
* @alpha
*/
recompute(txn: TxnProps): Promise<void>;
}
//# sourceMappingURL=TxnManager.d.ts.map