refactor(web): dependency management (#2386)

This commit is contained in:
Mo
2023-08-05 12:48:39 -05:00
committed by GitHub
parent b07da5b663
commit d8d4052a52
274 changed files with 4065 additions and 3873 deletions

View File

@@ -10,6 +10,8 @@ export interface LegacyApiServiceInterface
extends AbstractService<ApiServiceEvent, ApiServiceEventData>,
FilesApiInterface {
isThirdPartyHostUsed(): boolean
setHost(host: string): Promise<void>
getHost(): string
downloadOfflineFeaturesFromRepo(
repo: SNFeatureRepo,
@@ -24,4 +26,6 @@ export interface LegacyApiServiceInterface
limit: number,
sharedVaultUuids?: string[],
): HttpRequest
getNewSubscriptionToken(): Promise<string | undefined>
}

View File

@@ -1,24 +1,27 @@
import { VaultUserServiceInterface, VaultInviteServiceInterface } from '@standardnotes/services'
import {
VaultUserServiceInterface,
VaultInviteServiceInterface,
StorageServiceInterface,
SyncServiceInterface,
FullyResolvedApplicationOptions,
ProtectionsClientInterface,
ChangeAndSaveItem,
GetHost,
SetHost,
LegacyApiServiceInterface,
StatusServiceInterface,
MfaServiceInterface,
} from '@standardnotes/services'
import { VaultLockServiceInterface } from './../VaultLock/VaultLockServiceInterface'
import { HistoryServiceInterface } from './../History/HistoryServiceInterface'
import { InternalEventBusInterface } from './../Internal/InternalEventBusInterface'
import { PreferenceServiceInterface } from './../Preferences/PreferenceServiceInterface'
import { AsymmetricMessageServiceInterface } from './../AsymmetricMessage/AsymmetricMessageServiceInterface'
import { SyncOptions } from './../Sync/SyncOptions'
import { ImportDataReturnType } from './../Mutator/ImportDataUseCase'
import { ChallengeServiceInterface } from './../Challenge/ChallengeServiceInterface'
import { VaultServiceInterface } from '../Vault/VaultServiceInterface'
import { ApplicationIdentifier } from '@standardnotes/common'
import {
BackupFile,
DecryptedItemInterface,
DecryptedItemMutator,
ItemStream,
PayloadEmitSource,
Platform,
PrefKey,
PrefValue,
} from '@standardnotes/models'
import { BackupFile, Environment, Platform, PrefKey, PrefValue } from '@standardnotes/models'
import { BackupServiceInterface, FilesClientInterface } from '@standardnotes/files'
import { AlertService } from '../Alert/AlertService'
@@ -37,7 +40,6 @@ import { DeinitSource } from './DeinitSource'
import { UserServiceInterface } from '../User/UserServiceInterface'
import { SessionsClientInterface } from '../Session/SessionsClientInterface'
import { HomeServerServiceInterface } from '../HomeServer/HomeServerServiceInterface'
import { User } from '@standardnotes/responses'
import { EncryptionProviderInterface } from '../Encryption/EncryptionProviderInterface'
export interface ApplicationInterface {
@@ -53,49 +55,24 @@ export interface ApplicationInterface {
createDecryptedBackupFile(): Promise<BackupFile | undefined>
hasPasscode(): boolean
lock(): Promise<void>
softLockBiometrics(): void
setValue(key: string, value: unknown, mode?: StorageValueModes): void
getValue<T>(key: string, mode?: StorageValueModes): T
removeValue(key: string, mode?: StorageValueModes): Promise<void>
isLocked(): Promise<boolean>
getPreference<K extends PrefKey>(key: K): PrefValue[K] | undefined
getPreference<K extends PrefKey>(key: K, defaultValue: PrefValue[K]): PrefValue[K]
getPreference<K extends PrefKey>(key: K, defaultValue?: PrefValue[K]): PrefValue[K] | undefined
setPreference<K extends PrefKey>(key: K, value: PrefValue[K]): Promise<void>
streamItems<I extends DecryptedItemInterface = DecryptedItemInterface>(
contentType: string | string[],
stream: ItemStream<I>,
): () => void
getUser(): User | undefined
hasAccount(): boolean
setCustomHost(host: string): Promise<void>
isThirdPartyHostUsed(): boolean
isUsingHomeServer(): Promise<boolean>
getNewSubscriptionToken(): Promise<string | undefined>
importData(data: BackupFile, awaitSync?: boolean): Promise<ImportDataReturnType>
/**
* Mutates a pre-existing item, marks it as dirty, and syncs it
*/
changeAndSaveItem<M extends DecryptedItemMutator = DecryptedItemMutator>(
itemToLookupUuidFor: DecryptedItemInterface,
mutate: (mutator: M) => void,
updateTimestamps?: boolean,
emitSource?: PayloadEmitSource,
syncOptions?: SyncOptions,
): Promise<DecryptedItemInterface | undefined>
/**
* Mutates pre-existing items, marks them as dirty, and syncs
*/
changeAndSaveItems<M extends DecryptedItemMutator = DecryptedItemMutator>(
itemsToLookupUuidsFor: DecryptedItemInterface[],
mutate: (mutator: M) => void,
updateTimestamps?: boolean,
emitSource?: PayloadEmitSource,
syncOptions?: SyncOptions,
): Promise<void>
get changeAndSaveItem(): ChangeAndSaveItem
get getHost(): GetHost
get setHost(): SetHost
get alerts(): AlertService
get asymmetric(): AsymmetricMessageServiceInterface
@@ -109,16 +86,24 @@ export interface ApplicationInterface {
get history(): HistoryServiceInterface
get homeServer(): HomeServerServiceInterface | undefined
get items(): ItemManagerInterface
get legacyApi(): LegacyApiServiceInterface
get mfa(): MfaServiceInterface
get mutator(): MutatorClientInterface
get preferences(): PreferenceServiceInterface
get protections(): ProtectionsClientInterface
get sessions(): SessionsClientInterface
get status(): StatusServiceInterface
get storage(): StorageServiceInterface
get subscriptions(): SubscriptionManagerInterface
get sync(): SyncServiceInterface
get user(): UserServiceInterface
get vaults(): VaultServiceInterface
get vaultLocks(): VaultLockServiceInterface
get vaultUsers(): VaultUserServiceInterface
get vaultInvites(): VaultInviteServiceInterface
get vaultLocks(): VaultLockServiceInterface
get vaults(): VaultServiceInterface
get vaultUsers(): VaultUserServiceInterface
readonly options: FullyResolvedApplicationOptions
readonly environment: Environment
readonly identifier: ApplicationIdentifier
readonly platform: Platform
device: DeviceInterface

View File

@@ -0,0 +1,16 @@
import { ApplicationOptionsWhichHaveDefaults } from './Defaults'
import {
ApplicationDisplayOptions,
ApplicationOptionalConfiguratioOptions,
ApplicationSyncOptions,
} from './OptionalOptions'
import { RequiredApplicationOptions } from './RequiredOptions'
export type ApplicationConstructorOptions = RequiredApplicationOptions &
Partial<ApplicationSyncOptions & ApplicationDisplayOptions & ApplicationOptionalConfiguratioOptions>
export type FullyResolvedApplicationOptions = RequiredApplicationOptions &
ApplicationSyncOptions &
ApplicationDisplayOptions &
ApplicationOptionalConfiguratioOptions &
ApplicationOptionsWhichHaveDefaults

View File

@@ -0,0 +1,15 @@
import { ApplicationDisplayOptions, ApplicationSyncOptions } from './OptionalOptions'
export interface ApplicationOptionsWhichHaveDefaults {
loadBatchSize: ApplicationSyncOptions['loadBatchSize']
sleepBetweenBatches: ApplicationSyncOptions['sleepBetweenBatches']
allowNoteSelectionStatePersistence: ApplicationDisplayOptions['allowNoteSelectionStatePersistence']
allowMultipleSelection: ApplicationDisplayOptions['allowMultipleSelection']
}
export const ApplicationOptionsDefaults: ApplicationOptionsWhichHaveDefaults = {
loadBatchSize: 700,
sleepBetweenBatches: 10,
allowMultipleSelection: true,
allowNoteSelectionStatePersistence: true,
}

View File

@@ -0,0 +1,40 @@
export interface ApplicationSyncOptions {
/**
* The size of the item batch to decrypt and render upon application load.
*/
loadBatchSize: number
sleepBetweenBatches: number
}
export interface ApplicationDisplayOptions {
allowNoteSelectionStatePersistence: boolean
allowMultipleSelection: boolean
}
export interface ApplicationOptionalConfiguratioOptions {
/**
* URL for WebSocket providing permissions and roles information.
*/
webSocketUrl?: string
/**
* 3rd party library function for prompting U2F authenticator device registration
*
* @param registrationOptions - Registration options generated by the server
* @returns authenticator device response
*/
u2fAuthenticatorRegistrationPromptFunction?: (
registrationOptions: Record<string, unknown>,
) => Promise<Record<string, unknown>>
/**
* 3rd party library function for prompting U2F authenticator device authentication
*
* @param registrationOptions - Registration options generated by the server
* @returns authenticator device response
*/
u2fAuthenticatorVerificationPromptFunction?: (
authenticationOptions: Record<string, unknown>,
) => Promise<Record<string, unknown>>
}

View File

@@ -0,0 +1,43 @@
import { Environment, Platform } from '@standardnotes/models'
import { ApplicationIdentifier } from '@standardnotes/common'
import { AlertService, DeviceInterface } from '@standardnotes/services'
import { PureCryptoInterface } from '@standardnotes/sncrypto-common'
export interface RequiredApplicationOptions {
/**
* The Environment that identifies your application.
*/
environment: Environment
/**
* The Platform that identifies your application.
*/
platform: Platform
/**
* The device interface that provides platform specific
* utilities that are used to read/write raw values from/to the database or value storage.
*/
deviceInterface: DeviceInterface
/**
* The platform-dependent implementation of SNPureCrypto to use.
* Web uses SNWebCrypto, mobile uses SNReactNativeCrypto.
*/
crypto: PureCryptoInterface
/**
* The platform-dependent implementation of alert service.
*/
alertService: AlertService
/**
* A unique persistent identifier to namespace storage and other
* persistent properties. For an ephemeral runtime identifier, use ephemeralIdentifier.
*/
identifier: ApplicationIdentifier
/**
* Default host to use in ApiService.
*/
defaultHost: string
/**
* Version of client application.
*/
appVersion: string
}

View File

@@ -14,8 +14,6 @@ export interface ChallengeServiceInterface extends AbstractService {
submitValuesForChallenge(challenge: ChallengeInterface, values: ChallengeValue[]): Promise<void>
cancelChallenge(challenge: ChallengeInterface): void
isPasscodeLocked(): Promise<boolean>
/**
* Resolves when the challenge has been completed.
* For non-validated challenges, will resolve when the first value is submitted.

View File

@@ -4,4 +4,7 @@ export interface DesktopManagerInterface {
syncComponentsInstallation(components: ComponentInterface[]): void
registerUpdateObserver(callback: (component: ComponentInterface) => void): () => void
getExtServerHost(): string
saveDesktopBackup(): Promise<void>
searchText(text?: string): void
redoSearch(): void
}

View File

@@ -24,6 +24,8 @@ import {
export interface EncryptionProviderInterface {
initialize(): Promise<void>
isPasscodeLocked(): Promise<boolean>
encryptSplitSingle(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface>
encryptSplit(split: KeyedEncryptionSplit): Promise<EncryptedPayloadInterface[]>
decryptSplitSingle<

View File

@@ -191,6 +191,7 @@ export class EncryptionService
return ProtocolVersionLatest
}
/** Unlike SessionManager.isSignedIn, hasAccount can be read before the application is unlocked and is based on the key state */
public hasAccount() {
return this.rootKeyManager.hasAccount()
}
@@ -625,7 +626,7 @@ export class EncryptionService
/**
* @returns True if the root key has not yet been unwrapped (passcode locked).
*/
public async isPasscodeLocked() {
public async isPasscodeLocked(): Promise<boolean> {
return (await this.rootKeyManager.hasRootKeyWrapper()) && this.rootKeyManager.getRootKey() == undefined
}

View File

@@ -55,7 +55,5 @@ export enum ApplicationEvent {
UnprotectedSessionExpired = 'Application:UnprotectedSessionExpired',
/** Called when the app first launches and after first sync request made after sign in */
CompletedInitialSync = 'Application:CompletedInitialSync',
BiometricsSoftLockEngaged = 'Application:BiometricsSoftLockEngaged',
BiometricsSoftLockDisengaged = 'Application:BiometricsSoftLockDisengaged',
DidPurchaseSubscription = 'Application:DidPurchaseSubscription',
}

View File

@@ -22,6 +22,7 @@ import {
NotesAndFilesDisplayControllerOptions,
ThemeInterface,
ComponentInterface,
ItemStream,
} from '@standardnotes/models'
import { AbstractService } from '../Service/AbstractService'
@@ -57,6 +58,11 @@ export interface ItemManagerInterface extends AbstractService {
callback: ItemManagerChangeObserverCallback<I>,
): () => void
streamItems<I extends DecryptedItemInterface = DecryptedItemInterface>(
contentType: string | string[],
stream: ItemStream<I>,
): () => void
get items(): DecryptedItemInterface[]
getItems<T extends DecryptedItemInterface>(contentType: string | string[]): T[]

View File

@@ -0,0 +1,7 @@
export interface MfaServiceInterface {
isMfaActivated(): Promise<boolean>
generateMfaSecret(): Promise<string>
getOtpToken(secret: string): Promise<string>
enableMfa(secret: string, otpToken: string): Promise<void>
disableMfa(): Promise<void>
}

View File

@@ -6,9 +6,9 @@ export enum PreferencesServiceEvent {
}
export interface PreferenceServiceInterface extends AbstractService<PreferencesServiceEvent> {
getValue<K extends PrefKey>(key: K, defaultValue: PrefValue[K] | undefined): PrefValue[K] | undefined
getValue<K extends PrefKey>(key: K, defaultValue: PrefValue[K]): PrefValue[K]
getValue<K extends PrefKey>(key: K, defaultValue?: PrefValue[K]): PrefValue[K] | undefined
getValue<K extends PrefKey>(key: K, defaultValue: PrefValue[K] | undefined): PrefValue[K] | undefined
setValue<K extends PrefKey>(key: K, value: PrefValue[K]): Promise<void>
/** Set value without triggering sync or event notifications */

View File

@@ -1,9 +1,14 @@
import { ApplicationServiceInterface } from './../Service/ApplicationServiceInterface'
import { DecryptedItem, DecryptedItemInterface, FileItem, SNNote } from '@standardnotes/models'
import { ChallengeInterface, ChallengeReason } from '../Challenge'
import { MobileUnlockTiming } from './MobileUnlockTiming'
import { TimingDisplayOption } from './TimingDisplayOption'
import { ProtectionEvent } from './ProtectionEvent'
export interface ProtectionsClientInterface extends ApplicationServiceInterface<ProtectionEvent, unknown> {
isLocked(): Promise<boolean>
softLockBiometrics(): void
export interface ProtectionsClientInterface {
createLaunchChallenge(): ChallengeInterface | undefined
authorizeProtectedActionForItems<T extends DecryptedItem>(files: T[], challengeReason: ChallengeReason): Promise<T[]>
authorizeItemAccess(item: DecryptedItem): Promise<boolean>

View File

@@ -0,0 +1,6 @@
export enum ProtectionEvent {
UnprotectedSessionBegan = 'Protection:UnprotectedSessionBegan',
UnprotectedSessionExpired = 'Protection:UnprotectedSessionExpired',
BiometricsSoftLockEngaged = 'Protection:BiometricsSoftLockEngaged',
BiometricsSoftLockDisengaged = 'Protection:BiometricsSoftLockDisengaged',
}

View File

@@ -20,6 +20,7 @@ export interface SessionsClientInterface {
getUser(): User | undefined
isSignedIn(): boolean
isSignedOut(): boolean
get userUuid(): string
getSureUser(): User
isSignedIntoFirstPartyServer(): boolean

View File

@@ -0,0 +1,33 @@
import { SyncOptions } from './../Sync/SyncOptions'
import { MutatorClientInterface } from './../Mutator/MutatorClientInterface'
import { SyncServiceInterface } from './../Sync/SyncServiceInterface'
import { ItemManagerInterface } from '../Item/ItemManagerInterface'
import { DecryptedItemInterface, DecryptedItemMutator, MutationType, PayloadEmitSource } from '@standardnotes/models'
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
export class ChangeAndSaveItem implements UseCaseInterface<DecryptedItemInterface | undefined> {
constructor(
private readonly items: ItemManagerInterface,
private mutator: MutatorClientInterface,
private sync: SyncServiceInterface,
) {}
async execute<M extends DecryptedItemMutator = DecryptedItemMutator>(
itemToLookupUuidFor: DecryptedItemInterface,
mutate: (mutator: M) => void,
updateTimestamps = true,
emitSource?: PayloadEmitSource,
syncOptions?: SyncOptions,
): Promise<Result<DecryptedItemInterface | undefined>> {
await this.mutator.changeItems(
[itemToLookupUuidFor],
mutate,
updateTimestamps ? MutationType.UpdateUserTimestamps : MutationType.NoUpdateUserTimestamps,
emitSource,
)
await this.sync.sync(syncOptions)
return Result.ok(this.items.findItem(itemToLookupUuidFor.uuid))
}
}

View File

@@ -0,0 +1,10 @@
import { LegacyApiServiceInterface } from './../Api/LegacyApiServiceInterface'
import { Result, SyncUseCaseInterface } from '@standardnotes/domain-core'
export class GetHost implements SyncUseCaseInterface<string> {
constructor(private legacyApi: LegacyApiServiceInterface) {}
execute(): Result<string> {
return Result.ok(this.legacyApi.getHost())
}
}

View File

@@ -0,0 +1,18 @@
import { HttpServiceInterface } from '@standardnotes/api'
import { LegacyApiServiceInterface } from '../Api/LegacyApiServiceInterface'
import { Result, UseCaseInterface } from '@standardnotes/domain-core'
export class SetHost implements UseCaseInterface<void> {
constructor(
private http: HttpServiceInterface,
private legacyApi: LegacyApiServiceInterface,
) {}
async execute(host: string): Promise<Result<string>> {
this.http.setHost(host)
await this.legacyApi.setHost(host)
return Result.ok()
}
}

View File

@@ -10,6 +10,10 @@ export * from './Application/ApplicationStage'
export * from './Application/DeinitCallback'
export * from './Application/DeinitMode'
export * from './Application/DeinitSource'
export * from './Application/Options/ApplicationOptions'
export * from './Application/Options/Defaults'
export * from './Application/Options/OptionalOptions'
export * from './Application/Options/RequiredOptions'
export * from './AsymmetricMessage/AsymmetricMessageService'
export * from './AsymmetricMessage/AsymmetricMessageServiceInterface'
export * from './AsymmetricMessage/UseCase/GetInboundMessages'
@@ -117,12 +121,14 @@ export * from './Item/StaticItemCounter'
export * from './ItemsEncryption/ItemsEncryption'
export * from './ItemsEncryption/ItemsEncryption'
export * from './KeySystem/KeySystemKeyManager'
export * from './Mfa/MfaServiceInterface'
export * from './Mutator/ImportDataUseCase'
export * from './Mutator/MutatorClientInterface'
export * from './Payloads/PayloadManagerInterface'
export * from './Preferences/PreferenceServiceInterface'
export * from './Protection/MobileUnlockTiming'
export * from './Protection/ProtectionClientInterface'
export * from './Protection/ProtectionEvent'
export * from './Protection/TimingDisplayOption'
export * from './Revision/RevisionClientInterface'
export * from './Revision/RevisionManager'
@@ -170,20 +176,23 @@ export * from './Sync/SyncOptions'
export * from './Sync/SyncQueueStrategy'
export * from './Sync/SyncServiceInterface'
export * from './Sync/SyncSource'
export * from './UseCase/ChangeAndSaveItem'
export * from './UseCase/DiscardItemsLocally'
export * from './UseCase/GetHost'
export * from './UseCase/SetHost'
export * from './User/AccountEvent'
export * from './User/AccountEventData'
export * from './User/CredentialsChangeFunctionResponse'
export * from './User/SignedInOrRegisteredEventPayload'
export * from './User/SignedOutEventPayload'
export * from './User/UserServiceInterface'
export * from './User/UserServiceInterface'
export * from './User/UserService'
export * from './User/UserServiceInterface'
export * from './User/UserServiceInterface'
export * from './UserEvent/NotificationService'
export * from './UserEvent/NotificationServiceEvent'
export * from './Vault/UseCase/ChangeVaultStorageMode'
export * from './Vault/UseCase/ChangeVaultKeyOptions'
export * from './Vault/UseCase/ChangeVaultKeyOptionsDTO'
export * from './Vault/UseCase/ChangeVaultStorageMode'
export * from './Vault/UseCase/CreateVault'
export * from './Vault/UseCase/DeleteVault'
export * from './Vault/UseCase/GetVault'