import { MfaService } from './../Services/Mfa/MfaService' import { KeyRecoveryService } from './../Services/KeyRecovery/KeyRecoveryService' import { MigrationService } from './../Services/Migration/MigrationService' import { LegacyApiService } from './../Services/Api/ApiService' import { FeaturesService } from '@Lib/Services/Features/FeaturesService' import { PreferencesService } from './../Services/Preferences/PreferencesService' import { ProtectionService } from './../Services/Protection/ProtectionService' import { SessionManager } from './../Services/Session/SessionManager' import { HttpService, HttpServiceInterface, UserRegistrationResponseBody } from '@standardnotes/api' import { ApplicationIdentifier, compareVersions, ProtocolVersion, KeyParamsOrigination } from '@standardnotes/common' import { DeinitCallback, SessionEvent, SyncEvent, ApplicationStage, FeaturesEvent, SyncMode, SyncSource, ApplicationStageChangedEventPayload, StorageValueModes, ChallengeObserver, ImportDataResult, ImportData, StoragePersistencePolicies, HomeServerServiceInterface, DeviceInterface, SubscriptionManagerInterface, FeaturesClientInterface, ItemManagerInterface, SyncServiceInterface, UserServiceInterface, MutatorClientInterface, StatusServiceInterface, AlertService, StorageServiceInterface, ChallengeServiceInterface, AsymmetricMessageServiceInterface, VaultServiceInterface, ContactServiceInterface, SharedVaultServiceInterface, PreferenceServiceInterface, InternalEventBusInterface, ApplicationEvent, ApplicationEventCallback, ChallengeValidation, ComponentManagerInterface, ChallengeValue, StorageKey, ChallengeReason, DeinitMode, DeinitSource, AppGroupManagedApplication, ApplicationInterface, EncryptionService, EncryptionServiceEvent, Challenge, ErrorAlertStrings, SessionsClientInterface, ProtectionsClientInterface, UserService, ProtocolUpgradeStrings, CredentialsChangeFunctionResponse, SessionStrings, AccountEvent, PayloadManagerInterface, HistoryServiceInterface, InternalEventPublishStrategy, EncryptionProviderInterface, VaultUserServiceInterface, VaultInviteServiceInterface, VaultLockServiceInterface, ApplicationConstructorOptions, FullyResolvedApplicationOptions, ApplicationOptionsDefaults, ChangeAndSaveItem, ProtectionEvent, GetHost, SetHost, MfaServiceInterface, GenerateUuid, CreateDecryptedBackupFile, CreateEncryptedBackupFile, WebSocketsService, } from '@standardnotes/services' import { SNNote, PrefKey, PrefValue, BackupFile, EncryptedItemInterface, Environment, Platform, } from '@standardnotes/models' import { HttpResponse, SessionListResponse, SignInResponse, ClientDisplayableError, SessionListEntry, } from '@standardnotes/responses' import { SyncService, SettingsService, ActionsService, ChallengeResponse, ListedClientInterface, DiskStorageService, } from '../Services' import { nonSecureRandomIdentifier, assertUnreachable, removeFromArray, isNullOrUndefined, sleep, useBoolean, LoggerInterface, canBlockDeinit, } from '@standardnotes/utils' import { UuidString, ApplicationEventPayload } from '../Types' import { applicationEventForSyncEvent } from '@Lib/Application/Event' import { BackupServiceInterface, FilesClientInterface } from '@standardnotes/files' import { ComputePrivateUsername } from '@standardnotes/encryption' import { SNLog } from '../Log' import { SignInWithRecoveryCodes } from '@Lib/Domain/UseCase/SignInWithRecoveryCodes/SignInWithRecoveryCodes' import { UseCaseContainerInterface } from '@Lib/Domain/UseCase/UseCaseContainerInterface' import { GetRecoveryCodes } from '@Lib/Domain/UseCase/GetRecoveryCodes/GetRecoveryCodes' import { AddAuthenticator } from '@Lib/Domain/UseCase/AddAuthenticator/AddAuthenticator' import { ListAuthenticators } from '@Lib/Domain/UseCase/ListAuthenticators/ListAuthenticators' import { DeleteAuthenticator } from '@Lib/Domain/UseCase/DeleteAuthenticator/DeleteAuthenticator' import { ListRevisions } from '@Lib/Domain/UseCase/ListRevisions/ListRevisions' import { GetRevision } from '@Lib/Domain/UseCase/GetRevision/GetRevision' import { DeleteRevision } from '@Lib/Domain/UseCase/DeleteRevision/DeleteRevision' import { GetAuthenticatorAuthenticationResponse } from '@Lib/Domain/UseCase/GetAuthenticatorAuthenticationResponse/GetAuthenticatorAuthenticationResponse' import { GetAuthenticatorAuthenticationOptions } from '@Lib/Domain/UseCase/GetAuthenticatorAuthenticationOptions/GetAuthenticatorAuthenticationOptions' import { Dependencies } from './Dependencies/Dependencies' import { TYPES } from './Dependencies/Types' import { RegisterApplicationServicesEvents } from './Dependencies/DependencyEvents' import { Result } from '@standardnotes/domain-core' type LaunchCallback = { receiveChallenge: (challenge: Challenge) => void } type ApplicationObserver = { singleEvent?: ApplicationEvent callback: ApplicationEventCallback } type ObserverRemover = () => void export class SNApplication implements ApplicationInterface, AppGroupManagedApplication, UseCaseContainerInterface { onDeinit!: DeinitCallback /** * A runtime based identifier for each dynamic instantiation of the application instance. * This differs from the persistent application.identifier which persists in storage * across instantiations. */ public readonly ephemeralIdentifier = nonSecureRandomIdentifier() private eventHandlers: ApplicationObserver[] = [] private serviceObservers: ObserverRemover[] = [] private managedSubscribers: ObserverRemover[] = [] /** True if the result of deviceInterface.openDatabase yields a new database being created */ private createdNewDatabase = false /** True if the application has started (but not necessarily launched) */ private started = false /** True if the application has launched */ private launched = false /** Whether the application has been destroyed via .deinit() */ public dealloced = false private revokingSession = false private handledFullSyncStage = false public readonly environment: Environment public readonly platform: Platform public readonly identifier: ApplicationIdentifier public readonly options: FullyResolvedApplicationOptions private dependencies: Dependencies constructor(options: ApplicationConstructorOptions) { const allOptions: FullyResolvedApplicationOptions = { ...ApplicationOptionsDefaults, ...options, } if (!SNLog.onLog) { throw Error('SNLog.onLog must be set.') } if (!SNLog.onError) { throw Error('SNLog.onError must be set.') } const requiredOptions: (keyof FullyResolvedApplicationOptions)[] = [ 'deviceInterface', 'environment', 'platform', 'crypto', 'alertService', 'identifier', 'defaultHost', 'appVersion', ] for (const optionName of requiredOptions) { if (!allOptions[optionName]) { throw Error(`${optionName} must be supplied when creating an application.`) } } this.environment = options.environment this.platform = options.platform this.identifier = options.identifier this.options = Object.freeze(allOptions) this.dependencies = new Dependencies(this.options) const logger = this.dependencies.get(TYPES.Logger) logger.setLevel('error') this.registerServiceObservers() RegisterApplicationServicesEvents(this.dependencies, this.events) } private registerServiceObservers() { const encryptionService = this.dependencies.get(TYPES.EncryptionService) this.serviceObservers.push( encryptionService.addEventObserver(async (event) => { if (event === EncryptionServiceEvent.RootKeyStatusChanged) { await this.notifyEvent(ApplicationEvent.KeyStatusChanged) } }), ) this.dependencies.get(TYPES.DiskStorageService).provideEncryptionProvider(encryptionService) const apiService = this.dependencies.get(TYPES.LegacyApiService) this.dependencies .get(TYPES.HttpService) .setCallbacks(apiService.processMetaObject.bind(apiService), apiService.setSession.bind(apiService)) this.serviceObservers.push( this.dependencies.get(TYPES.SessionManager).addEventObserver(async (event) => { switch (event) { case SessionEvent.Restored: { void (async () => { await this.sync.sync({ sourceDescription: 'Session restored pre key creation' }) if (encryptionService.needsNewRootKeyBasedItemsKey()) { void encryptionService.createNewDefaultItemsKey().then(() => { void this.sync.sync({ sourceDescription: 'Session restored post key creation' }) }) } })() break } case SessionEvent.Revoked: { await this.handleRevokedSession() break } case SessionEvent.UserKeyPairChanged: break default: { assertUnreachable(event) } } }), ) const syncEventCallback = async (eventName: SyncEvent) => { const appEvent = applicationEventForSyncEvent(eventName) if (appEvent) { await encryptionService.onSyncEvent(eventName) await this.notifyEvent(appEvent) if (appEvent === ApplicationEvent.CompletedFullSync) { if (!this.handledFullSyncStage) { this.handledFullSyncStage = true await this.handleStage(ApplicationStage.FullSyncCompleted_13) } } } } const syncService = this.dependencies.get(TYPES.SyncService) const uninstall = syncService.addEventObserver(syncEventCallback) this.serviceObservers.push(uninstall) const protectionService = this.dependencies.get(TYPES.ProtectionService) this.serviceObservers.push( protectionService.addEventObserver((event) => { if (event === ProtectionEvent.UnprotectedSessionBegan) { void this.notifyEvent(ApplicationEvent.UnprotectedSessionBegan) } else if (event === ProtectionEvent.UnprotectedSessionExpired) { void this.notifyEvent(ApplicationEvent.UnprotectedSessionExpired) } }), ) const userService = this.dependencies.get(TYPES.UserService) this.serviceObservers.push( userService.addEventObserver(async (event, data) => { switch (event) { case AccountEvent.SignedInOrRegistered: { void this.notifyEvent(ApplicationEvent.SignedIn) break } case AccountEvent.SignedOut: { await this.notifyEvent(ApplicationEvent.SignedOut) await this.prepareForDeinit() this.deinit(this.getDeinitMode(), data?.payload.source || DeinitSource.SignOut) break } default: { assertUnreachable(event) } } }), ) const preferencesService = this.dependencies.get(TYPES.PreferencesService) this.serviceObservers.push( preferencesService.addEventObserver(() => { void this.notifyEvent(ApplicationEvent.PreferencesChanged) }), ) const featuresService = this.dependencies.get(TYPES.FeaturesService) this.serviceObservers.push( featuresService.addEventObserver((event) => { switch (event) { case FeaturesEvent.UserRolesChanged: { void this.notifyEvent(ApplicationEvent.UserRolesChanged) break } case FeaturesEvent.FeaturesAvailabilityChanged: { void this.notifyEvent(ApplicationEvent.FeaturesAvailabilityChanged) break } case FeaturesEvent.DidPurchaseSubscription: { void this.notifyEvent(ApplicationEvent.DidPurchaseSubscription) break } default: { assertUnreachable(event) } } }), ) } public computePrivateUsername(username: string): Promise { return ComputePrivateUsername(this.options.crypto, username) } /** * The first thing consumers should call when starting their app. * This function will load all services in their correct order. */ async prepareForLaunch(callback: LaunchCallback): Promise { if (this.launched) { throw new Error('Attempting to prelaunch already launched application') } await this.options.crypto.initialize() this.setLaunchCallback(callback) const databaseResult = await this.device.openDatabase(this.identifier).catch((error) => { void this.notifyEvent(ApplicationEvent.LocalDatabaseReadError, error) return undefined }) this.createdNewDatabase = useBoolean(databaseResult?.isNewDatabase, false) await this.migrations.initialize() await this.notifyEvent(ApplicationEvent.MigrationsLoaded) await this.handleStage(ApplicationStage.PreparingForLaunch_0) await this.storage.initializeFromDisk() await this.notifyEvent(ApplicationEvent.StorageReady) await this.encryption.initialize() await this.handleStage(ApplicationStage.ReadyForLaunch_05) this.started = true await this.notifyEvent(ApplicationEvent.Started) } private setLaunchCallback(callback: LaunchCallback) { this.challenges.sendChallenge = callback.receiveChallenge } /** * Handles device authentication, unlocks application, and * issues a callback if a device activation requires user input * (i.e local passcode or fingerprint). * @param awaitDatabaseLoad * Option to await database load before marking the app as ready. */ public async launch(awaitDatabaseLoad = false): Promise { if (this.launched) { throw new Error('Attempting to launch already launched application') } this.launched = false const launchChallenge = this.getLaunchChallenge() if (launchChallenge) { const response = await this.challenges.promptForChallengeResponse(launchChallenge) if (!response) { throw Error('Launch challenge was cancelled.') } await this.handleLaunchChallengeResponse(response) } if (this.storage.isStorageWrapped()) { try { await this.storage.decryptStorage() } catch (_error) { void this.alerts.alert(ErrorAlertStrings.StorageDecryptErrorBody, ErrorAlertStrings.StorageDecryptErrorTitle) } } await this.handleStage(ApplicationStage.StorageDecrypted_09) const host = this.legacyApi.loadHost() this.http.setHost(host) this.sockets.loadWebSocketUrl() this.settings.initializeFromDisk() this.launched = true await this.notifyEvent(ApplicationEvent.Launched) await this.handleStage(ApplicationStage.Launched_10) await this.handleStage(ApplicationStage.LoadingDatabase_11) if (this.createdNewDatabase) { await this.sync.onNewDatabaseCreated() } /** * We don't want to await this, as we want to begin allowing the app to function * before local data has been loaded fully. */ const loadPromise = this.sync .loadDatabasePayloads() .then(async () => { if (this.dealloced) { throw 'Application has been destroyed.' } await this.handleStage(ApplicationStage.LoadedDatabase_12) this.sync.beginAutoSyncTimer() await this.sync.sync({ mode: SyncMode.DownloadFirst, source: SyncSource.External, sourceDescription: 'Application Launch', }) this.vaultUsers.invalidateVaultUsersCache().catch(console.error) }) .catch((error) => { void this.notifyEvent(ApplicationEvent.LocalDatabaseReadError, error) throw error }) if (awaitDatabaseLoad) { await loadPromise } } public onStart(): void { // optional override } public onLaunch(): void { // optional override } public getLaunchChallenge(): Challenge | undefined { return this.protections.createLaunchChallenge() } private async handleLaunchChallengeResponse(response: ChallengeResponse) { if (response.challenge.hasPromptForValidationType(ChallengeValidation.LocalPasscode)) { let wrappingKey = response.artifacts?.wrappingKey if (!wrappingKey) { const value = response.getValueForType(ChallengeValidation.LocalPasscode) wrappingKey = await this.encryption.computeWrappingKey(value.value as string) } await this.encryption.unwrapRootKey(wrappingKey) } } private async handleStage(stage: ApplicationStage) { await this.events.publishSync( { type: ApplicationEvent.ApplicationStageChanged, payload: { stage } as ApplicationStageChangedEventPayload, }, InternalEventPublishStrategy.SEQUENCE, ) } /** * @param singleEvent Whether to only listen for a particular event. */ public addEventObserver(callback: ApplicationEventCallback, singleEvent?: ApplicationEvent): () => void { const observer = { callback, singleEvent } this.eventHandlers.push(observer) return () => { removeFromArray(this.eventHandlers, observer) } } public addSingleEventObserver(event: ApplicationEvent, callback: ApplicationEventCallback): () => void { const filteredCallback = async (firedEvent: ApplicationEvent) => { if (firedEvent === event) { void callback(event) } } return this.addEventObserver(filteredCallback, event) } private async notifyEvent(event: ApplicationEvent, data?: ApplicationEventPayload) { if (event === ApplicationEvent.Started) { this.onStart() } else if (event === ApplicationEvent.Launched) { this.onLaunch() } for (const observer of this.eventHandlers.slice()) { if ((observer.singleEvent && observer.singleEvent === event) || !observer.singleEvent) { await observer.callback(event, data || {}) } } this.events.publish({ type: event, payload: data, }) void this.migrations.handleApplicationEvent(event) } public getSessions(): Promise> { return this.sessions.getSessionsList() } public async revokeSession(sessionId: UuidString): Promise | undefined> { if (await this.protections.authorizeSessionRevoking()) { return this.sessions.revokeSession(sessionId) } return undefined } /** * Revokes all sessions except the current one. */ public async revokeAllOtherSessions(): Promise { return this.sessions.revokeAllOtherSessions() } public userCanManageSessions(): boolean { const userVersion = this.getUserVersion() if (isNullOrUndefined(userVersion)) { return false } return compareVersions(userVersion, ProtocolVersion.V004) >= 0 } public async setCustomHost(host: string, websocketUrl?: string): Promise { await this.setHost.execute(host) this.sockets.setWebSocketUrl(websocketUrl) } public getUserPasswordCreationDate(): Date | undefined { return this.encryption.getPasswordCreatedDate() } public getProtocolEncryptionDisplayName(): Promise { return this.encryption.getEncryptionDisplayName() } public getUserVersion(): ProtocolVersion | undefined { return this.encryption.getUserVersion() } /** * Returns true if there is an upgrade available for the account or passcode */ public protocolUpgradeAvailable(): Promise { return this.encryption.upgradeAvailable() } /** * Returns true if there is an encryption source available */ public isEncryptionAvailable(): boolean { return this.hasAccount() || this.hasPasscode() } public async upgradeProtocolVersion(): Promise<{ success?: true canceled?: true error?: { message: string } }> { const result = await this.user.performProtocolUpgrade() if (result.success) { if (this.hasAccount()) { void this.alerts.alert(ProtocolUpgradeStrings.SuccessAccount) } else { void this.alerts.alert(ProtocolUpgradeStrings.SuccessPasscodeOnly) } } else if (result.error) { void this.alerts.alert(ProtocolUpgradeStrings.Fail) } return result } public hasAccount(): boolean { return this.encryption.hasAccount() } /** * @returns true if the user has a source of protection available, such as a * passcode, password, or biometrics. */ public hasProtectionSources(): boolean { return this.protections.hasProtectionSources() } /** * When a user specifies a non-zero remember duration on a protection * challenge, a session will be started during which protections are disabled. */ public getProtectionSessionExpiryDate(): Date { return this.protections.getSessionExpiryDate() } public clearProtectionSession(): Promise { return this.protections.clearSession() } public async authorizeProtectedActionForNotes(notes: SNNote[], challengeReason: ChallengeReason): Promise { return await this.protections.authorizeProtectedActionForItems(notes, challengeReason) } /** * @returns whether note access has been granted or not */ public authorizeNoteAccess(note: SNNote): Promise { return this.protections.authorizeItemAccess(note) } public authorizeAutolockIntervalChange(): Promise { return this.protections.authorizeAutolockIntervalChange() } public isEphemeralSession(): boolean { return this.storage.isEphemeralSession() } public setValue(key: string, value: unknown, mode?: StorageValueModes): void { return this.storage.setValue(key, value, mode) } public getValue(key: string, mode?: StorageValueModes): T { return this.storage.getValue(key, mode) } public async removeValue(key: string, mode?: StorageValueModes): Promise { return this.storage.removeValue(key, mode) } public getPreference(key: K): PrefValue[K] | undefined public getPreference(key: K, defaultValue: PrefValue[K]): PrefValue[K] public getPreference(key: K, defaultValue?: PrefValue[K]): PrefValue[K] | undefined { return this.preferences.getValue(key, defaultValue) } public async setPreference(key: K, value: PrefValue[K]): Promise { return this.preferences.setValue(key, value) } /** * Gives services a chance to complete any sensitive operations before yielding * @param maxWait The maximum number of milliseconds to wait for services * to finish tasks. 0 means no limit. */ private async prepareForDeinit(maxWait = 0): Promise { const deps = this.dependencies.getAll().filter(canBlockDeinit) const promise = Promise.all(deps.map((service) => service.blockDeinit())) if (maxWait === 0) { await promise } else { /** Await up to maxWait. If not resolved by then, return. */ await Promise.race([promise, sleep(maxWait, false, 'Preparing for deinit...')]) } } public addChallengeObserver(challenge: Challenge, observer: ChallengeObserver): () => void { return this.challenges.addChallengeObserver(challenge, observer) } public submitValuesForChallenge(challenge: Challenge, values: ChallengeValue[]): Promise { return this.challenges.submitValuesForChallenge(challenge, values) } public cancelChallenge(challenge: Challenge): void { this.challenges.cancelChallenge(challenge) } public setOnDeinit(onDeinit: DeinitCallback): void { this.onDeinit = onDeinit } /** * Destroys the application instance. */ public deinit(mode: DeinitMode, source: DeinitSource): void { this.dealloced = true for (const uninstallObserver of this.serviceObservers) { uninstallObserver() } for (const uninstallSubscriber of this.managedSubscribers) { uninstallSubscriber() } this.options.crypto.deinit() ;(this.options as unknown) = undefined this.createdNewDatabase = false this.serviceObservers.length = 0 this.managedSubscribers.length = 0 this.started = false this.dependencies.deinit() this.onDeinit?.(this, mode, source) ;(this.onDeinit as unknown) = undefined } /** * @param mergeLocal Whether to merge existing offline data into account. If false, * any pre-existing data will be fully deleted upon success. */ public async register( email: string, password: string, ephemeral = false, mergeLocal = true, ): Promise { return this.user.register(email, password, ephemeral, mergeLocal) } /** * @param mergeLocal Whether to merge existing offline data into account. * If false, any pre-existing data will be fully deleted upon success. */ public async signIn( email: string, password: string, strict = false, ephemeral = false, mergeLocal = true, awaitSync = false, ): Promise> { return this.user.signIn(email, password, strict, ephemeral, mergeLocal, awaitSync) } public async changeEmail( newEmail: string, currentPassword: string, passcode?: string, origination = KeyParamsOrigination.EmailChange, ): Promise { return this.user.changeCredentials({ currentPassword, newEmail, passcode, origination, validateNewPasswordStrength: false, }) } public async changePassword( currentPassword: string, newPassword: string, passcode?: string, origination = KeyParamsOrigination.PasswordChange, validateNewPasswordStrength = true, ): Promise { return this.user.changeCredentials({ currentPassword, newPassword, passcode, origination, validateNewPasswordStrength, }) } public async importData(data: BackupFile, awaitSync = false): Promise> { const usecase = this.dependencies.get(TYPES.ImportData) return usecase.execute(data, awaitSync) } private async handleRevokedSession(): Promise { /** * Because multiple API requests can come back at the same time * indicating revoked session we only want to do this once. */ if (this.revokingSession) { return } this.revokingSession = true /** Keep a reference to the soon-to-be-cleared alertService */ const alertService = this.alerts await this.user.signOut(true) void alertService.alert(SessionStrings.CurrentSessionRevoked) } public async validateAccountPassword(password: string): Promise { const { valid } = await this.encryption.validateAccountPassword(password) return valid } public isStarted(): boolean { return this.started } public isLaunched(): boolean { return this.launched } public hasPasscode(): boolean { return this.encryption.hasPasscode() } public async lock(): Promise { /** * Because locking is a critical operation, we want to try to do it safely, * but only up to a certain limit. */ const MaximumWaitTime = 500 await this.prepareForDeinit(MaximumWaitTime) return this.deinit(this.getDeinitMode(), DeinitSource.Lock) } isNativeMobileWeb() { return this.environment === Environment.Mobile } getDeinitMode(): DeinitMode { const value = this.getValue(StorageKey.DeinitMode) if (value === 'hard') { return DeinitMode.Hard } return DeinitMode.Soft } public addPasscode(passcode: string): Promise { return this.user.addPasscode(passcode) } /** * @returns whether the passcode was successfuly removed */ public async removePasscode(): Promise { return this.user.removePasscode() } public async changePasscode( newPasscode: string, origination = KeyParamsOrigination.PasscodeChange, ): Promise { return this.user.changePasscode(newPasscode, origination) } public enableEphemeralPersistencePolicy(): Promise { return this.storage.setPersistencePolicy(StoragePersistencePolicies.Ephemeral) } public hasPendingMigrations(): Promise { return this.migrations.hasPendingMigrations() } public presentKeyRecoveryWizard(): void { const service = this.dependencies.get(TYPES.KeyRecoveryService) return service.presentKeyRecoveryWizard() } public canAttemptDecryptionOfItem(item: EncryptedItemInterface): ClientDisplayableError | true { const service = this.dependencies.get(TYPES.KeyRecoveryService) return service.canAttemptDecryptionOfItem(item) } public async isMfaActivated(): Promise { return this.mfa.isMfaActivated() } public async generateMfaSecret(): Promise { return this.mfa.generateMfaSecret() } public async getOtpToken(secret: string): Promise { return this.mfa.getOtpToken(secret) } public async enableMfa(secret: string, otpToken: string): Promise { return this.mfa.enableMfa(secret, otpToken) } public async disableMfa(): Promise { if (await this.protections.authorizeMfaDisable()) { return this.mfa.disableMfa() } } async isUsingHomeServer(): Promise { const homeServerService = this.dependencies.get(TYPES.HomeServerService) if (!homeServerService) { return false } return this.getHost.execute().getValue() === (await homeServerService.getHomeServerUrl()) } get device(): DeviceInterface { return this.dependencies.get(TYPES.DeviceInterface) } get subscriptions(): SubscriptionManagerInterface { return this.dependencies.get(TYPES.SubscriptionManager) } get signInWithRecoveryCodes(): SignInWithRecoveryCodes { return this.dependencies.get(TYPES.SignInWithRecoveryCodes) } get getRecoveryCodes(): GetRecoveryCodes { return this.dependencies.get(TYPES.GetRecoveryCodes) } get addAuthenticator(): AddAuthenticator { return this.dependencies.get(TYPES.AddAuthenticator) } get listAuthenticators(): ListAuthenticators { return this.dependencies.get(TYPES.ListAuthenticators) } get deleteAuthenticator(): DeleteAuthenticator { return this.dependencies.get(TYPES.DeleteAuthenticator) } get getAuthenticatorAuthenticationOptions(): GetAuthenticatorAuthenticationOptions { return this.dependencies.get(TYPES.GetAuthenticatorAuthenticationOptions) } get getAuthenticatorAuthenticationResponse(): GetAuthenticatorAuthenticationResponse { return this.dependencies.get(TYPES.GetAuthenticatorAuthenticationResponse) } get listRevisions(): ListRevisions { return this.dependencies.get(TYPES.ListRevisions) } get getRevision(): GetRevision { return this.dependencies.get(TYPES.GetRevision) } get deleteRevision(): DeleteRevision { return this.dependencies.get(TYPES.DeleteRevision) } public get files(): FilesClientInterface { return this.dependencies.get(TYPES.FileService) } public get features(): FeaturesClientInterface { return this.dependencies.get(TYPES.FeaturesService) } public get items(): ItemManagerInterface { return this.dependencies.get(TYPES.ItemManager) } public get payloads(): PayloadManagerInterface { return this.dependencies.get(TYPES.PayloadManager) } public get protections(): ProtectionsClientInterface { return this.dependencies.get(TYPES.ProtectionService) } public get sync(): SyncServiceInterface { return this.dependencies.get(TYPES.SyncService) } public get user(): UserServiceInterface { return this.dependencies.get(TYPES.UserService) } public get settings(): SettingsService { return this.dependencies.get(TYPES.SettingsService) } public get mutator(): MutatorClientInterface { return this.dependencies.get(TYPES.MutatorService) } public get sessions(): SessionsClientInterface { return this.dependencies.get(TYPES.SessionManager) } public get status(): StatusServiceInterface { return this.dependencies.get(TYPES.StatusService) } public get fileBackups(): BackupServiceInterface | undefined { return this.dependencies.get(TYPES.FilesBackupService) } public get componentManager(): ComponentManagerInterface { return this.dependencies.get(TYPES.ComponentManager) } public get listed(): ListedClientInterface { return this.dependencies.get(TYPES.ListedService) } public get alerts(): AlertService { return this.dependencies.get(TYPES.AlertService) } public get storage(): StorageServiceInterface { return this.dependencies.get(TYPES.DiskStorageService) } public get actions(): ActionsService { return this.dependencies.get(TYPES.ActionsService) } public get challenges(): ChallengeServiceInterface { return this.dependencies.get(TYPES.ChallengeService) } public get asymmetric(): AsymmetricMessageServiceInterface { return this.dependencies.get(TYPES.AsymmetricMessageService) } get homeServer(): HomeServerServiceInterface | undefined { return this.dependencies.get(TYPES.HomeServerService) } public get preferences(): PreferenceServiceInterface { return this.dependencies.get(TYPES.PreferencesService) } public get history(): HistoryServiceInterface { return this.dependencies.get(TYPES.HistoryManager) } public get encryption(): EncryptionProviderInterface { return this.dependencies.get(TYPES.EncryptionService) } public get events(): InternalEventBusInterface { return this.dependencies.get(TYPES.InternalEventBus) } public get vaults(): VaultServiceInterface { return this.dependencies.get(TYPES.VaultService) } public get vaultLocks(): VaultLockServiceInterface { return this.dependencies.get(TYPES.VaultLockService) } public get vaultUsers(): VaultUserServiceInterface { return this.dependencies.get(TYPES.VaultUserService) } public get vaultInvites(): VaultInviteServiceInterface { return this.dependencies.get(TYPES.VaultInviteService) } public get contacts(): ContactServiceInterface { return this.dependencies.get(TYPES.ContactService) } public get sharedVaults(): SharedVaultServiceInterface { return this.dependencies.get(TYPES.SharedVaultService) } public get changeAndSaveItem(): ChangeAndSaveItem { return this.dependencies.get(TYPES.ChangeAndSaveItem) } public get getHost(): GetHost { return this.dependencies.get(TYPES.GetHost) } public get setHost(): SetHost { return this.dependencies.get(TYPES.SetHost) } public get legacyApi(): LegacyApiService { return this.dependencies.get(TYPES.LegacyApiService) } public get mfa(): MfaServiceInterface { return this.dependencies.get(TYPES.MfaService) } public get generateUuid(): GenerateUuid { return this.dependencies.get(TYPES.GenerateUuid) } public get createDecryptedBackupFile(): CreateDecryptedBackupFile { return this.dependencies.get(TYPES.CreateDecryptedBackupFile) } public get createEncryptedBackupFile(): CreateEncryptedBackupFile { return this.dependencies.get(TYPES.CreateEncryptedBackupFile) } private get migrations(): MigrationService { return this.dependencies.get(TYPES.MigrationService) } private get http(): HttpServiceInterface { return this.dependencies.get(TYPES.HttpService) } private get sockets(): WebSocketsService { return this.dependencies.get(TYPES.WebSocketsService) } }