From 65b77880f296f197b92b228fc635be8eab667562 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 19 Dec 2024 16:23:08 -0800 Subject: [PATCH 01/10] create service to load sdk on application init --- .../browser/src/background/main.background.ts | 12 +- .../sdk/browser-sdk-client-factory.ts | 69 --------- .../services/sdk/browser-sdk-load.service.ts | 134 ++++++++++++++++++ .../src/popup/services/init.service.ts | 3 + .../src/popup/services/services.module.ts | 13 +- apps/web/src/app/core/core.module.ts | 8 ++ apps/web/src/app/core/init.service.ts | 3 + .../app/platform/web-sdk-client-factory.ts | 31 ---- .../src/app/platform/web-sdk-load.service.ts | 31 ++++ .../abstractions/sdk/sdk-load.service.ts | 3 + .../services/sdk/noop-sdk-load.service.ts | 8 ++ 11 files changed, 211 insertions(+), 104 deletions(-) create mode 100644 apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts create mode 100644 apps/web/src/app/platform/web-sdk-load.service.ts create mode 100644 libs/common/src/platform/abstractions/sdk/sdk-load.service.ts create mode 100644 libs/common/src/platform/services/sdk/noop-sdk-load.service.ts diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 98f3867e5ff..44878f19069 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -92,6 +92,7 @@ import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platfor import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { @@ -261,6 +262,7 @@ import { BackgroundPlatformUtilsService } from "../platform/services/platform-ut import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service"; import { PopupViewCacheBackgroundService } from "../platform/services/popup-view-cache-background.service"; import { BrowserSdkClientFactory } from "../platform/services/sdk/browser-sdk-client-factory"; +import { BrowserSdkLoadService } from "../platform/services/sdk/browser-sdk-load.service"; import { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service"; import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service"; import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider"; @@ -380,6 +382,7 @@ export default class MainBackground { themeStateService: DefaultThemeStateService; autoSubmitLoginBackground: AutoSubmitLoginBackground; sdkService: SdkService; + sdkLoadService: SdkLoadService; cipherAuthorizationService: CipherAuthorizationService; inlineMenuFieldQualificationService: InlineMenuFieldQualificationService; @@ -731,8 +734,14 @@ export default class MainBackground { ); const sdkClientFactory = flagEnabled("sdk") - ? new BrowserSdkClientFactory(this.logService) + ? new BrowserSdkClientFactory() : new NoopSdkClientFactory(); + this.sdkLoadService = new BrowserSdkLoadService( + this.platformUtilsService, + this.apiService, + this.environmentService, + this.logService, + ); this.sdkService = new DefaultSdkService( sdkClientFactory, this.environmentService, @@ -1267,6 +1276,7 @@ export default class MainBackground { async bootstrap() { this.containerService.attachToGlobal(self); + await this.sdkLoadService.load(); // Only the "true" background should run migrations await this.stateService.init({ runMigrations: true }); diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts index 0499f34a4ae..60078f095a8 100644 --- a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts +++ b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts @@ -1,86 +1,17 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore -import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; import type { BitwardenClient } from "@bitwarden/sdk-internal"; -import { BrowserApi } from "../../browser/browser-api"; - -// https://stackoverflow.com/a/47880734 -const supported = (() => { - try { - if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") { - const module = new WebAssembly.Module( - Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00), - ); - if (module instanceof WebAssembly.Module) { - return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; - } - } - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (e) { - // ignore - } - return false; -})(); - -// Due to using webpack as bundler, sync imports will return an async module. Since we do support -// top level awaits, we define a promise we can await in the `load` function. -let loadingPromise: Promise | undefined; - -// Manifest v3 does not support dynamic imports in the service worker. -if (BrowserApi.isManifestVersion(3)) { - if (supported) { - // eslint-disable-next-line no-console - console.debug("WebAssembly is supported in this environment"); - loadingPromise = import("./wasm"); - } else { - // eslint-disable-next-line no-console - console.debug("WebAssembly is not supported in this environment"); - loadingPromise = import("./fallback"); - } -} - -// Manifest v2 expects dynamic imports to prevent timing issues. -async function load() { - if (BrowserApi.isManifestVersion(3)) { - // Ensure we have loaded the module - await loadingPromise; - return; - } - - if (supported) { - // eslint-disable-next-line no-console - console.debug("WebAssembly is supported in this environment"); - await import("./wasm"); - } else { - // eslint-disable-next-line no-console - console.debug("WebAssembly is not supported in this environment"); - await import("./fallback"); - } -} - /** * SDK client factory with a js fallback for when WASM is not supported. * * Works both in popup and service worker. */ export class BrowserSdkClientFactory implements SdkClientFactory { - constructor(private logService: LogService) {} - async createSdkClient( ...args: ConstructorParameters ): Promise { - const startTime = performance.now(); - await load(); - - const endTime = performance.now(); - const instance = (globalThis as any).init_sdk(...args); - this.logService.info("WASM SDK loaded in", Math.round(endTime - startTime), "ms"); - return instance; } } diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts b/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts new file mode 100644 index 00000000000..e21ed263932 --- /dev/null +++ b/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts @@ -0,0 +1,134 @@ +import { firstValueFrom } from "rxjs"; + +import { ApiService } from "@bitwarden/common/abstractions/api.service"; +import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; +import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; + +import { BrowserApi } from "../../browser/browser-api"; + +// https://stackoverflow.com/a/47880734 +const supported = (() => { + try { + if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") { + const module = new WebAssembly.Module( + Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00), + ); + if (module instanceof WebAssembly.Module) { + return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; + } + } + } catch (e) { + // ignore + } + return false; +})(); + +// Due to using webpack as bundler, sync imports will return an async module. Since we do support +// top level awaits, we define a promise we can await in the `load` function. +let loadingPromise: Promise | undefined; + +// Manifest v3 does not support dynamic imports in the service worker. +if (BrowserApi.isManifestVersion(3)) { + if (supported) { + // eslint-disable-next-line no-console + console.debug("WebAssembly is supported in this environment"); + loadingPromise = import("./wasm"); + } else { + // eslint-disable-next-line no-console + console.debug("WebAssembly is not supported in this environment"); + loadingPromise = import("./fallback"); + } +} + +// Manifest v2 expects dynamic imports to prevent timing issues. +async function load() { + if (BrowserApi.isManifestVersion(3)) { + // Ensure we have loaded the module + await loadingPromise; + return; + } + + if (supported) { + // eslint-disable-next-line no-console + console.debug("WebAssembly is supported in this environment"); + await import("./wasm"); + } else { + // eslint-disable-next-line no-console + console.debug("WebAssembly is not supported in this environment"); + await import("./fallback"); + } +} + +const loadWithTimeout = async () => { + return new Promise((resolve, reject) => { + const timer = setTimeout(() => { + reject(new Error("Operation timed out after 10 second")); + }, 10000); + + load() + .then(() => { + clearTimeout(timer); + resolve(); + }) + .catch((error) => { + clearTimeout(timer); + reject(error); + }); + }); +}; + +export class BrowserSdkLoadService implements SdkLoadService { + constructor( + readonly platformUtilsService: PlatformUtilsService, + readonly apiService: ApiService, + readonly environmentService: EnvironmentService, + readonly logService: LogService, + ) {} + + async load(): Promise { + const startTime = performance.now(); + + try { + await loadWithTimeout(); + } catch (error) { + throw new Error(`Failed to load: ${(error as Error).message}`); + } + const endTime = performance.now(); + const elapsed = Math.round(endTime - startTime); + const message = `WASM SDK loaded in ${elapsed}ms`; + + this.logService.info(message); + + // If it takes 3 seconds or more to load, we want to capture it. + if (elapsed >= 3000) { + await this.logFailureToInitialize(message); + } + } + + private async logFailureToInitialize(message: string): Promise { + // Only log on cloud instances + if ( + this.platformUtilsService.isDev() || + !(await firstValueFrom(this.environmentService.environment$)).isCloud + ) { + return; + } + + return this.apiService.send( + "POST", + "/wasm-debug", + { + category: "sdk", + error: message, + }, + false, + false, + undefined, + (headers) => { + headers.append("SDK-Version", "1.0.0"); + }, + ); + } +} diff --git a/apps/browser/src/popup/services/init.service.ts b/apps/browser/src/popup/services/init.service.ts index 24661438495..069ebf4020d 100644 --- a/apps/browser/src/popup/services/init.service.ts +++ b/apps/browser/src/popup/services/init.service.ts @@ -6,6 +6,7 @@ import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { BrowserApi } from "../../platform/browser/browser-api"; @@ -22,11 +23,13 @@ export class InitService { private twoFactorService: TwoFactorService, private logService: LogServiceAbstraction, private themingService: AbstractThemingService, + private sdkLoadService: SdkLoadService, @Inject(DOCUMENT) private document: Document, ) {} init() { return async () => { + await this.sdkLoadService.load(); await this.stateService.init({ runMigrations: false }); // Browser background is responsible for migrations await this.i18nService.init(); this.twoFactorService.init(); diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 24d82ab8b67..9fb3c8bbfa8 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -70,6 +70,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { StateService } from "@bitwarden/common/platform/abstractions/state.service"; import { AbstractStorageService, @@ -145,6 +146,7 @@ import { BrowserScriptInjectorService } from "../../platform/services/browser-sc import I18nService from "../../platform/services/i18n.service"; import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service"; import { BrowserSdkClientFactory } from "../../platform/services/sdk/browser-sdk-client-factory"; +import { BrowserSdkLoadService } from "../../platform/services/sdk/browser-sdk-load.service"; import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service"; import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider"; import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service"; @@ -565,11 +567,16 @@ const safeProviders: SafeProvider[] = [ useClass: ForegroundLockService, deps: [MessageSender, MessageListener], }), + safeProvider({ + provide: SdkLoadService, + useClass: BrowserSdkLoadService, + deps: [PlatformUtilsService, ApiService, EnvironmentService, LogService], + }), safeProvider({ provide: SdkClientFactory, - useFactory: (logService: LogService) => - flagEnabled("sdk") ? new BrowserSdkClientFactory(logService) : new NoopSdkClientFactory(), - deps: [LogService], + useFactory: () => + flagEnabled("sdk") ? new BrowserSdkClientFactory() : new NoopSdkClientFactory(), + deps: [], }), safeProvider({ provide: LoginEmailService, diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 8f21dfa2c8b..9c24aa62e97 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -66,6 +66,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service" import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; import { ThemeType } from "@bitwarden/common/platform/enums"; import { AppIdService as DefaultAppIdService } from "@bitwarden/common/platform/services/app-id.service"; @@ -74,6 +75,7 @@ import { MemoryStorageService } from "@bitwarden/common/platform/services/memory import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; +import { NoopSdkLoadService } from "@bitwarden/common/platform/services/sdk/noop-sdk-load.service"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; /* eslint-disable import/no-restricted-paths -- Implementation for memory storage */ import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state"; @@ -111,6 +113,7 @@ import { WebBiometricsService } from "../key-management/web-biometric.service"; import { WebEnvironmentService } from "../platform/web-environment.service"; import { WebMigrationRunner } from "../platform/web-migration-runner"; import { WebSdkClientFactory } from "../platform/web-sdk-client-factory"; +import { WebSdkLoadService } from "../platform/web-sdk-load.service"; import { WebStorageServiceProvider } from "../platform/web-storage-service.provider"; import { EventService } from "./event.service"; @@ -288,6 +291,11 @@ const safeProviders: SafeProvider[] = [ useClass: DefaultCollectionAdminService, deps: [ApiService, KeyServiceAbstraction, EncryptService, CollectionService], }), + safeProvider({ + provide: SdkLoadService, + useClass: flagEnabled("sdk") ? WebSdkLoadService : NoopSdkLoadService, + deps: [], + }), safeProvider({ provide: SdkClientFactory, useClass: flagEnabled("sdk") ? WebSdkClientFactory : NoopSdkClientFactory, diff --git a/apps/web/src/app/core/init.service.ts b/apps/web/src/app/core/init.service.ts index b3e6d691f75..0d6063b4ac3 100644 --- a/apps/web/src/app/core/init.service.ts +++ b/apps/web/src/app/core/init.service.ts @@ -10,6 +10,7 @@ import { AccountService } from "@bitwarden/common/auth/abstractions/account.serv import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; @@ -35,11 +36,13 @@ export class InitService { private userAutoUnlockKeyService: UserAutoUnlockKeyService, private accountService: AccountService, private versionService: VersionService, + private sdkLoadService: SdkLoadService, @Inject(DOCUMENT) private document: Document, ) {} init() { return async () => { + await this.sdkLoadService.load(); await this.stateService.init(); const activeAccount = await firstValueFrom(this.accountService.activeAccount$); diff --git a/apps/web/src/app/platform/web-sdk-client-factory.ts b/apps/web/src/app/platform/web-sdk-client-factory.ts index 0dd43ecbb92..917da45cfa6 100644 --- a/apps/web/src/app/platform/web-sdk-client-factory.ts +++ b/apps/web/src/app/platform/web-sdk-client-factory.ts @@ -8,37 +8,6 @@ export class WebSdkClientFactory implements SdkClientFactory { async createSdkClient( ...args: ConstructorParameters ): Promise { - const module = await load(); - - (sdk as any).init(module); - return Promise.resolve(new sdk.BitwardenClient(...args)); } } - -// https://stackoverflow.com/a/47880734 -const supported = (() => { - try { - if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") { - const module = new WebAssembly.Module( - Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00), - ); - if (module instanceof WebAssembly.Module) { - return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; - } - } - // FIXME: Remove when updating file. Eslint update - // eslint-disable-next-line @typescript-eslint/no-unused-vars - } catch (e) { - // ignore - } - return false; -})(); - -async function load() { - if (supported) { - return await import("@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"); - } else { - return await import("@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm.js"); - } -} diff --git a/apps/web/src/app/platform/web-sdk-load.service.ts b/apps/web/src/app/platform/web-sdk-load.service.ts new file mode 100644 index 00000000000..f1f98736f2f --- /dev/null +++ b/apps/web/src/app/platform/web-sdk-load.service.ts @@ -0,0 +1,31 @@ +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; +import * as sdk from "@bitwarden/sdk-internal"; + +// https://stackoverflow.com/a/47880734 +const supported = (() => { + try { + if (typeof WebAssembly === "object" && typeof WebAssembly.instantiate === "function") { + const module = new WebAssembly.Module( + Uint8Array.of(0x0, 0x61, 0x73, 0x6d, 0x01, 0x00, 0x00, 0x00), + ); + if (module instanceof WebAssembly.Module) { + return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; + } + } + } catch (e) { + // ignore + } + return false; +})(); + +export class WebSdkLoadService implements SdkLoadService { + async load(): Promise { + let module: any; + if (supported) { + module = await import("@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"); + } else { + module = await import("@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm.js"); + } + (sdk as any).init(module); + } +} diff --git a/libs/common/src/platform/abstractions/sdk/sdk-load.service.ts b/libs/common/src/platform/abstractions/sdk/sdk-load.service.ts new file mode 100644 index 00000000000..16482e797b2 --- /dev/null +++ b/libs/common/src/platform/abstractions/sdk/sdk-load.service.ts @@ -0,0 +1,3 @@ +export abstract class SdkLoadService { + abstract load(): Promise; +} diff --git a/libs/common/src/platform/services/sdk/noop-sdk-load.service.ts b/libs/common/src/platform/services/sdk/noop-sdk-load.service.ts new file mode 100644 index 00000000000..fd6d4a8e4f0 --- /dev/null +++ b/libs/common/src/platform/services/sdk/noop-sdk-load.service.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line no-restricted-imports +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; + +export class NoopSdkLoadService extends SdkLoadService { + async load() { + return; + } +} From 01135c9c6dab9ad08bbe7fc76fda18e18df653d1 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Mon, 23 Dec 2024 19:14:52 -0800 Subject: [PATCH 02/10] Eagerly load CLI SDK --- .../src/service-container/service-container.ts | 5 +++++ .../abstractions/sdk/sdk-client-factory.ts | 4 ++++ .../services/sdk/default-sdk-client-factory.ts | 12 ++++++------ .../services/sdk/default-sdk-load.service.ts | 15 +++++++++++++++ 4 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 libs/common/src/platform/services/sdk/default-sdk-load.service.ts diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index f57db9909d6..230f09354bf 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -65,6 +65,7 @@ import { RegionConfig, } from "@bitwarden/common/platform/abstractions/environment.service"; import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service"; import { KeySuffixOptions, LogLevelType } from "@bitwarden/common/platform/enums"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; @@ -88,6 +89,7 @@ import { MemoryStorageService } from "@bitwarden/common/platform/services/memory import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; +import { DefaultSdkLoadService } from "@bitwarden/common/platform/services/sdk/default-sdk-load.service"; import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { StateService } from "@bitwarden/common/platform/services/state.service"; @@ -266,6 +268,7 @@ export class ServiceContainer { kdfConfigService: KdfConfigService; taskSchedulerService: TaskSchedulerService; sdkService: SdkService; + sdkLoadService: SdkLoadService; cipherAuthorizationService: CipherAuthorizationService; constructor() { @@ -566,6 +569,7 @@ export class ServiceContainer { const sdkClientFactory = flagEnabled("sdk") ? new DefaultSdkClientFactory() : new NoopSdkClientFactory(); + this.sdkLoadService = new DefaultSdkLoadService(); this.sdkService = new DefaultSdkService( sdkClientFactory, this.environmentService, @@ -854,6 +858,7 @@ export class ServiceContainer { return; } + await this.sdkLoadService.load(); await this.storageService.init(); await this.stateService.init(); this.containerService.attachToGlobal(global); diff --git a/libs/common/src/platform/abstractions/sdk/sdk-client-factory.ts b/libs/common/src/platform/abstractions/sdk/sdk-client-factory.ts index d684561dacd..6a1b7b67b42 100644 --- a/libs/common/src/platform/abstractions/sdk/sdk-client-factory.ts +++ b/libs/common/src/platform/abstractions/sdk/sdk-client-factory.ts @@ -4,6 +4,10 @@ import type { BitwardenClient } from "@bitwarden/sdk-internal"; * Factory for creating SDK clients. */ export abstract class SdkClientFactory { + /** + * Creates a new BitwardenClient. Assumes the SDK is already loaded. + * @param args Bitwarden client constructor parameters + */ abstract createSdkClient( ...args: ConstructorParameters ): Promise; diff --git a/libs/common/src/platform/services/sdk/default-sdk-client-factory.ts b/libs/common/src/platform/services/sdk/default-sdk-client-factory.ts index 8e99af2efed..fc55cc83ac8 100644 --- a/libs/common/src/platform/services/sdk/default-sdk-client-factory.ts +++ b/libs/common/src/platform/services/sdk/default-sdk-client-factory.ts @@ -1,19 +1,19 @@ import * as sdk from "@bitwarden/sdk-internal"; -import * as module from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"; import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory"; /** - * Directly imports the Bitwarden SDK and initializes it. - * - * **Warning**: This requires WASM support and will fail if the environment does not support it. + * Default SDK client factory. */ export class DefaultSdkClientFactory implements SdkClientFactory { + /** + * Initializes a Bitwarden client. Assumes the SDK is already loaded. + * @param args Bitwarden client constructor parameters + * @returns A BitwardenClient + */ async createSdkClient( ...args: ConstructorParameters ): Promise { - (sdk as any).init(module); - return Promise.resolve(new sdk.BitwardenClient(...args)); } } diff --git a/libs/common/src/platform/services/sdk/default-sdk-load.service.ts b/libs/common/src/platform/services/sdk/default-sdk-load.service.ts new file mode 100644 index 00000000000..957937fcb73 --- /dev/null +++ b/libs/common/src/platform/services/sdk/default-sdk-load.service.ts @@ -0,0 +1,15 @@ +// eslint-disable-next-line no-restricted-imports -- TODO MDG: fix this import restriction error +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; +import * as sdk from "@bitwarden/sdk-internal"; +import * as module from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"; + +/** + * Directly imports the Bitwarden SDK and initializes it. + * + * **Warning**: This requires WASM support and will fail if the environment does not support it. + */ +export class DefaultSdkLoadService implements SdkLoadService { + async load(): Promise { + (sdk as any).init(module); + } +} From a45e7879d6d74637f8e7ca28d44e47aaa70ca222 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Wed, 8 Jan 2025 12:16:37 -0800 Subject: [PATCH 03/10] Remove wasm logging to api --- .../browser/src/background/main.background.ts | 7 +- .../services/sdk/browser-sdk-load.service.ts | 73 +------------------ .../src/popup/services/services.module.ts | 2 +- 3 files changed, 6 insertions(+), 76 deletions(-) diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 44878f19069..2364ead97e8 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -736,12 +736,7 @@ export default class MainBackground { const sdkClientFactory = flagEnabled("sdk") ? new BrowserSdkClientFactory() : new NoopSdkClientFactory(); - this.sdkLoadService = new BrowserSdkLoadService( - this.platformUtilsService, - this.apiService, - this.environmentService, - this.logService, - ); + this.sdkLoadService = new BrowserSdkLoadService(this.logService); this.sdkService = new DefaultSdkService( sdkClientFactory, this.environmentService, diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts b/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts index e21ed263932..e272fdecbdb 100644 --- a/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts +++ b/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts @@ -1,9 +1,4 @@ -import { firstValueFrom } from "rxjs"; - -import { ApiService } from "@bitwarden/common/abstractions/api.service"; -import { EnvironmentService } from "@bitwarden/common/platform/abstractions/environment.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service"; -import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { BrowserApi } from "../../browser/browser-api"; @@ -43,7 +38,7 @@ if (BrowserApi.isManifestVersion(3)) { } // Manifest v2 expects dynamic imports to prevent timing issues. -async function load() { +async function importModule() { if (BrowserApi.isManifestVersion(3)) { // Ensure we have loaded the module await loadingPromise; @@ -61,74 +56,14 @@ async function load() { } } -const loadWithTimeout = async () => { - return new Promise((resolve, reject) => { - const timer = setTimeout(() => { - reject(new Error("Operation timed out after 10 second")); - }, 10000); - - load() - .then(() => { - clearTimeout(timer); - resolve(); - }) - .catch((error) => { - clearTimeout(timer); - reject(error); - }); - }); -}; - export class BrowserSdkLoadService implements SdkLoadService { - constructor( - readonly platformUtilsService: PlatformUtilsService, - readonly apiService: ApiService, - readonly environmentService: EnvironmentService, - readonly logService: LogService, - ) {} + constructor(readonly logService: LogService) {} async load(): Promise { const startTime = performance.now(); - - try { - await loadWithTimeout(); - } catch (error) { - throw new Error(`Failed to load: ${(error as Error).message}`); - } + await importModule(); const endTime = performance.now(); - const elapsed = Math.round(endTime - startTime); - const message = `WASM SDK loaded in ${elapsed}ms`; - - this.logService.info(message); - - // If it takes 3 seconds or more to load, we want to capture it. - if (elapsed >= 3000) { - await this.logFailureToInitialize(message); - } - } - - private async logFailureToInitialize(message: string): Promise { - // Only log on cloud instances - if ( - this.platformUtilsService.isDev() || - !(await firstValueFrom(this.environmentService.environment$)).isCloud - ) { - return; - } - return this.apiService.send( - "POST", - "/wasm-debug", - { - category: "sdk", - error: message, - }, - false, - false, - undefined, - (headers) => { - headers.append("SDK-Version", "1.0.0"); - }, - ); + this.logService.info(`WASM SDK loaded in ${Math.round(endTime - startTime)}ms`); } } diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index 9fb3c8bbfa8..c597b635c84 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -570,7 +570,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: SdkLoadService, useClass: BrowserSdkLoadService, - deps: [PlatformUtilsService, ApiService, EnvironmentService, LogService], + deps: [LogService], }), safeProvider({ provide: SdkClientFactory, From 7bd0a78e5f81895773758dbd67dd0f6c85f57fec Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Wed, 8 Jan 2025 14:28:24 -0800 Subject: [PATCH 04/10] Fix imports --- .../src/platform/services/sdk/default-sdk-load.service.ts | 4 ++-- .../common/src/platform/services/sdk/noop-sdk-load.service.ts | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/libs/common/src/platform/services/sdk/default-sdk-load.service.ts b/libs/common/src/platform/services/sdk/default-sdk-load.service.ts index 957937fcb73..a4eca27676f 100644 --- a/libs/common/src/platform/services/sdk/default-sdk-load.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk-load.service.ts @@ -1,8 +1,8 @@ -// eslint-disable-next-line no-restricted-imports -- TODO MDG: fix this import restriction error -import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import * as sdk from "@bitwarden/sdk-internal"; import * as module from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"; +import { SdkLoadService } from "../../abstractions/sdk/sdk-load.service"; + /** * Directly imports the Bitwarden SDK and initializes it. * diff --git a/libs/common/src/platform/services/sdk/noop-sdk-load.service.ts b/libs/common/src/platform/services/sdk/noop-sdk-load.service.ts index fd6d4a8e4f0..60dac4f21f1 100644 --- a/libs/common/src/platform/services/sdk/noop-sdk-load.service.ts +++ b/libs/common/src/platform/services/sdk/noop-sdk-load.service.ts @@ -1,5 +1,4 @@ -// eslint-disable-next-line no-restricted-imports -import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; +import { SdkLoadService } from "../../abstractions/sdk/sdk-load.service"; export class NoopSdkLoadService extends SdkLoadService { async load() { From 9ac590350882cf11007072cb450cb5112dd8ff0e Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 9 Jan 2025 10:03:11 -0800 Subject: [PATCH 05/10] Eagerly load Desktop renderer SDK Note: If the main process ever requires an SDK, we'll need to load it there, too. In that event, it's probably a good idea to move to IPC for all SDK functions to avoid loading the SDK for every window. --- .../browser/run-inside-angular.operator.ts | 52 +++++++++++++++++++ apps/desktop/src/app/services/init.service.ts | 3 ++ .../src/app/services/services.module.ts | 8 +++ 3 files changed, 63 insertions(+) diff --git a/apps/browser/src/platform/browser/run-inside-angular.operator.ts b/apps/browser/src/platform/browser/run-inside-angular.operator.ts index 4e9b52b009c..bf7055c1a7f 100644 --- a/apps/browser/src/platform/browser/run-inside-angular.operator.ts +++ b/apps/browser/src/platform/browser/run-inside-angular.operator.ts @@ -19,3 +19,55 @@ export function runInsideAngular(ngZone: NgZone): MonoTypeOperatorFunction return () => subscription.unsubscribe(); }); } + +// function now() { +// const userId = "userId" as UserId; +// const otherService: { someSetting: Observable } = null; +// const sdkService: DefaultSdkService = null; + +// combineLatest([otherService.someSetting, sdkService.userClient$(userId)]).subscribe( +// ([someSetting, sdkClient]) => { +// using client = sdkClient.take(); +// if (someSetting) { +// client.value.echo("some setting"); +// } else { +// client.value.echo("no setting"); +// } +// }, +// ); +// } + +// function callback() { +// const userId = "userId" as UserId; +// const otherService: { someSetting: Observable } = null; +// const sdkService: DefaultSdkService = null; + +// someSetting.pipe( +// switchMap((someSettings) => +// sdkService.userClientWithCallback$(userId, (client) => { +// // take done in userClient With Callback while calling this callback +// if (someSettings) { +// client.echo("some setting"); +// } else { +// client.echo("no setting"); +// } +// }), +// ), +// ); +// } + +// function operator() { +// const userId = "userId" as UserId; +// const otherService: { someSetting: Observable } = null; +// const sdkService: DefaultSdkService = null; + +// someSetting.pipe( +// sdkService.withUserClient, // this transforms the stream into [value, client] tuple while ensuring the client it taken +// ).subscribe(([someSetting, client]) => { +// if (someSetting) { +// client.echo("some setting"); +// } else { +// client.echo("no setting"); +// } +// }); +// } diff --git a/apps/desktop/src/app/services/init.service.ts b/apps/desktop/src/app/services/init.service.ts index de80f95593a..72c2821bf33 100644 --- a/apps/desktop/src/app/services/init.service.ts +++ b/apps/desktop/src/app/services/init.service.ts @@ -11,6 +11,7 @@ import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/comm import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; @@ -47,11 +48,13 @@ export class InitService { private versionService: VersionService, private sshAgentService: SshAgentService, private autofillService: DesktopAutofillService, + private sdkLoadService: SdkLoadService, @Inject(DOCUMENT) private document: Document, ) {} init() { return async () => { + await this.sdkLoadService.load(); await this.sshAgentService.init(); this.nativeMessagingService.init(); await this.stateService.init({ runMigrations: false }); // Desktop will run them in main process diff --git a/apps/desktop/src/app/services/services.module.ts b/apps/desktop/src/app/services/services.module.ts index 8b890032443..576d36d0104 100644 --- a/apps/desktop/src/app/services/services.module.ts +++ b/apps/desktop/src/app/services/services.module.ts @@ -67,6 +67,7 @@ import { import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.service"; @@ -77,7 +78,9 @@ import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling"; import { Fido2AuthenticatorService } from "@bitwarden/common/platform/services/fido2/fido2-authenticator.service"; import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; +import { DefaultSdkLoadService } from "@bitwarden/common/platform/services/sdk/default-sdk-load.service"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; +import { NoopSdkLoadService } from "@bitwarden/common/platform/services/sdk/noop-sdk-load.service"; import { SystemService } from "@bitwarden/common/platform/services/system.service"; import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state"; // eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage @@ -393,6 +396,11 @@ const safeProviders: SafeProvider[] = [ useClass: flagEnabled("sdk") ? DefaultSdkClientFactory : NoopSdkClientFactory, deps: [], }), + safeProvider({ + provide: SdkLoadService, + useClass: flagEnabled("sdk") ? DefaultSdkLoadService : NoopSdkLoadService, + deps: [], + }), safeProvider({ provide: LoginEmailService, useClass: LoginEmailService, From 6a254a28cf1f4871fe267d72a8a7c663e81df59a Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 9 Jan 2025 11:27:43 -0800 Subject: [PATCH 06/10] init wasm module from sdk load service --- .../sdk/browser-sdk-client-factory.ts | 3 ++- .../services/sdk/browser-sdk-load.service.ts | 24 +++++++++++-------- .../src/platform/services/sdk/fallback.ts | 6 ++--- .../browser/src/platform/services/sdk/wasm.ts | 6 ++--- 4 files changed, 22 insertions(+), 17 deletions(-) diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts index 60078f095a8..27b651d1886 100644 --- a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts +++ b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts @@ -1,4 +1,5 @@ import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; +import * as sdk from "@bitwarden/sdk-internal"; import type { BitwardenClient } from "@bitwarden/sdk-internal"; /** @@ -10,7 +11,7 @@ export class BrowserSdkClientFactory implements SdkClientFactory { async createSdkClient( ...args: ConstructorParameters ): Promise { - const instance = (globalThis as any).init_sdk(...args); + const instance = new sdk.BitwardenClient(...args); return instance; } diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts b/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts index e272fdecbdb..573dd28f138 100644 --- a/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts +++ b/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts @@ -3,6 +3,10 @@ import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk- import { BrowserApi } from "../../browser/browser-api"; +export type GlobalWithWasmInit = typeof globalThis & { + initSdk: () => void; +}; + // https://stackoverflow.com/a/47880734 const supported = (() => { try { @@ -28,32 +32,32 @@ let loadingPromise: Promise | undefined; if (BrowserApi.isManifestVersion(3)) { if (supported) { // eslint-disable-next-line no-console - console.debug("WebAssembly is supported in this environment"); + console.info("WebAssembly is supported in this environment"); loadingPromise = import("./wasm"); } else { // eslint-disable-next-line no-console - console.debug("WebAssembly is not supported in this environment"); + console.info("WebAssembly is not supported in this environment"); loadingPromise = import("./fallback"); } } // Manifest v2 expects dynamic imports to prevent timing issues. -async function importModule() { +async function importModule(): Promise { if (BrowserApi.isManifestVersion(3)) { // Ensure we have loaded the module await loadingPromise; - return; - } - - if (supported) { + } else if (supported) { // eslint-disable-next-line no-console - console.debug("WebAssembly is supported in this environment"); + console.info("WebAssembly is supported in this environment"); await import("./wasm"); } else { // eslint-disable-next-line no-console - console.debug("WebAssembly is not supported in this environment"); + console.info("WebAssembly is not supported in this environment"); await import("./fallback"); } + + // the wasm and fallback imports mutate globalThis to add the initSdk function + return (globalThis as GlobalWithWasmInit).initSdk; } export class BrowserSdkLoadService implements SdkLoadService { @@ -61,7 +65,7 @@ export class BrowserSdkLoadService implements SdkLoadService { async load(): Promise { const startTime = performance.now(); - await importModule(); + await importModule().then((initSdk) => initSdk()); const endTime = performance.now(); this.logService.info(`WASM SDK loaded in ${Math.round(endTime - startTime)}ms`); diff --git a/apps/browser/src/platform/services/sdk/fallback.ts b/apps/browser/src/platform/services/sdk/fallback.ts index 82d292fc9ee..234a3ded5fc 100644 --- a/apps/browser/src/platform/services/sdk/fallback.ts +++ b/apps/browser/src/platform/services/sdk/fallback.ts @@ -1,8 +1,8 @@ import * as sdk from "@bitwarden/sdk-internal"; import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm.js"; -(globalThis as any).init_sdk = (...args: ConstructorParameters) => { - (sdk as any).init(wasm); +import { GlobalWithWasmInit } from "src/platform/services/sdk/browser-sdk-load.service"; - return new sdk.BitwardenClient(...args); +(globalThis as GlobalWithWasmInit).initSdk = () => { + (sdk as any).init(wasm); }; diff --git a/apps/browser/src/platform/services/sdk/wasm.ts b/apps/browser/src/platform/services/sdk/wasm.ts index 1977a171e23..0d794ce1b87 100644 --- a/apps/browser/src/platform/services/sdk/wasm.ts +++ b/apps/browser/src/platform/services/sdk/wasm.ts @@ -1,8 +1,8 @@ import * as sdk from "@bitwarden/sdk-internal"; import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"; -(globalThis as any).init_sdk = (...args: ConstructorParameters) => { - (sdk as any).init(wasm); +import { GlobalWithWasmInit } from "src/platform/services/sdk/browser-sdk-load.service"; - return new sdk.BitwardenClient(...args); +(globalThis as GlobalWithWasmInit).initSdk = () => { + (sdk as any).init(wasm); }; From 9b8b64a8f2bb51db6b1fe228cc4a451d21d2952a Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Thu, 9 Jan 2025 11:35:18 -0800 Subject: [PATCH 07/10] Use default client factory --- apps/browser/src/background/main.background.ts | 4 ++-- .../services/sdk/browser-sdk-client-factory.ts | 18 ------------------ .../src/popup/services/services.module.ts | 4 ++-- apps/web/src/app/core/core.module.ts | 4 ++-- .../src/app/platform/web-sdk-client-factory.ts | 13 ------------- 5 files changed, 6 insertions(+), 37 deletions(-) delete mode 100644 apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts delete mode 100644 apps/web/src/app/platform/web-sdk-client-factory.ts diff --git a/apps/browser/src/background/main.background.ts b/apps/browser/src/background/main.background.ts index 2364ead97e8..f2ec1be5b5e 100644 --- a/apps/browser/src/background/main.background.ts +++ b/apps/browser/src/background/main.background.ts @@ -126,6 +126,7 @@ import { FileUploadService } from "@bitwarden/common/platform/services/file-uplo import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; +import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { StateService } from "@bitwarden/common/platform/services/state.service"; @@ -261,7 +262,6 @@ import { LocalBackedSessionStorageService } from "../platform/services/local-bac import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service"; import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-platform-utils.service"; import { PopupViewCacheBackgroundService } from "../platform/services/popup-view-cache-background.service"; -import { BrowserSdkClientFactory } from "../platform/services/sdk/browser-sdk-client-factory"; import { BrowserSdkLoadService } from "../platform/services/sdk/browser-sdk-load.service"; import { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service"; import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service"; @@ -734,7 +734,7 @@ export default class MainBackground { ); const sdkClientFactory = flagEnabled("sdk") - ? new BrowserSdkClientFactory() + ? new DefaultSdkClientFactory() : new NoopSdkClientFactory(); this.sdkLoadService = new BrowserSdkLoadService(this.logService); this.sdkService = new DefaultSdkService( diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts b/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts deleted file mode 100644 index 27b651d1886..00000000000 --- a/apps/browser/src/platform/services/sdk/browser-sdk-client-factory.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; -import * as sdk from "@bitwarden/sdk-internal"; -import type { BitwardenClient } from "@bitwarden/sdk-internal"; - -/** - * SDK client factory with a js fallback for when WASM is not supported. - * - * Works both in popup and service worker. - */ -export class BrowserSdkClientFactory implements SdkClientFactory { - async createSdkClient( - ...args: ConstructorParameters - ): Promise { - const instance = new sdk.BitwardenClient(...args); - - return instance; - } -} diff --git a/apps/browser/src/popup/services/services.module.ts b/apps/browser/src/popup/services/services.module.ts index c597b635c84..6906193fc17 100644 --- a/apps/browser/src/popup/services/services.module.ts +++ b/apps/browser/src/popup/services/services.module.ts @@ -83,6 +83,7 @@ import { flagEnabled } from "@bitwarden/common/platform/misc/flags"; import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service"; +import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; @@ -145,7 +146,6 @@ import BrowserMemoryStorageService from "../../platform/services/browser-memory- import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service"; import I18nService from "../../platform/services/i18n.service"; import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.service"; -import { BrowserSdkClientFactory } from "../../platform/services/sdk/browser-sdk-client-factory"; import { BrowserSdkLoadService } from "../../platform/services/sdk/browser-sdk-load.service"; import { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service"; import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider"; @@ -575,7 +575,7 @@ const safeProviders: SafeProvider[] = [ safeProvider({ provide: SdkClientFactory, useFactory: () => - flagEnabled("sdk") ? new BrowserSdkClientFactory() : new NoopSdkClientFactory(), + flagEnabled("sdk") ? new DefaultSdkClientFactory() : new NoopSdkClientFactory(), deps: [], }), safeProvider({ diff --git a/apps/web/src/app/core/core.module.ts b/apps/web/src/app/core/core.module.ts index 9c24aa62e97..1581d4ad8cd 100644 --- a/apps/web/src/app/core/core.module.ts +++ b/apps/web/src/app/core/core.module.ts @@ -74,6 +74,7 @@ import { MemoryStorageService } from "@bitwarden/common/platform/services/memory // eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; +import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { NoopSdkLoadService } from "@bitwarden/common/platform/services/sdk/noop-sdk-load.service"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; @@ -112,7 +113,6 @@ import { WebProcessReloadService } from "../key-management/services/web-process- import { WebBiometricsService } from "../key-management/web-biometric.service"; import { WebEnvironmentService } from "../platform/web-environment.service"; import { WebMigrationRunner } from "../platform/web-migration-runner"; -import { WebSdkClientFactory } from "../platform/web-sdk-client-factory"; import { WebSdkLoadService } from "../platform/web-sdk-load.service"; import { WebStorageServiceProvider } from "../platform/web-storage-service.provider"; @@ -298,7 +298,7 @@ const safeProviders: SafeProvider[] = [ }), safeProvider({ provide: SdkClientFactory, - useClass: flagEnabled("sdk") ? WebSdkClientFactory : NoopSdkClientFactory, + useClass: flagEnabled("sdk") ? DefaultSdkClientFactory : NoopSdkClientFactory, deps: [], }), safeProvider({ diff --git a/apps/web/src/app/platform/web-sdk-client-factory.ts b/apps/web/src/app/platform/web-sdk-client-factory.ts deleted file mode 100644 index 917da45cfa6..00000000000 --- a/apps/web/src/app/platform/web-sdk-client-factory.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; -import * as sdk from "@bitwarden/sdk-internal"; - -/** - * SDK client factory with a js fallback for when WASM is not supported. - */ -export class WebSdkClientFactory implements SdkClientFactory { - async createSdkClient( - ...args: ConstructorParameters - ): Promise { - return Promise.resolve(new sdk.BitwardenClient(...args)); - } -} From 5cbe0660e039ea8b598c12160feb3c71abd1e6b4 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Mon, 13 Jan 2025 08:38:04 -0800 Subject: [PATCH 08/10] Fix type imports --- apps/browser/src/platform/services/sdk/fallback.ts | 2 +- apps/browser/src/platform/services/sdk/wasm.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/platform/services/sdk/fallback.ts b/apps/browser/src/platform/services/sdk/fallback.ts index 234a3ded5fc..cee3598feda 100644 --- a/apps/browser/src/platform/services/sdk/fallback.ts +++ b/apps/browser/src/platform/services/sdk/fallback.ts @@ -1,7 +1,7 @@ import * as sdk from "@bitwarden/sdk-internal"; import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm.js"; -import { GlobalWithWasmInit } from "src/platform/services/sdk/browser-sdk-load.service"; +import { GlobalWithWasmInit } from "./browser-sdk-load.service"; (globalThis as GlobalWithWasmInit).initSdk = () => { (sdk as any).init(wasm); diff --git a/apps/browser/src/platform/services/sdk/wasm.ts b/apps/browser/src/platform/services/sdk/wasm.ts index 0d794ce1b87..de2eeffd294 100644 --- a/apps/browser/src/platform/services/sdk/wasm.ts +++ b/apps/browser/src/platform/services/sdk/wasm.ts @@ -1,7 +1,7 @@ import * as sdk from "@bitwarden/sdk-internal"; import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"; -import { GlobalWithWasmInit } from "src/platform/services/sdk/browser-sdk-load.service"; +import { GlobalWithWasmInit } from "./browser-sdk-load.service"; (globalThis as GlobalWithWasmInit).initSdk = () => { (sdk as any).init(wasm); From 07ee317a0f361534a14398dcd0af63638ffe8e01 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Mon, 13 Jan 2025 10:36:08 -0800 Subject: [PATCH 09/10] Resolve jest module import errors A CLI sdk load service that async imports our wasm binary doesn't seem to be needed to run, but jest isn't dealing with the ESM import properly. --- apps/cli/src/platform/services/cli-sdk-load.service.ts | 9 +++++++++ apps/cli/src/service-container/service-container.ts | 4 ++-- .../platform/services/sdk/default-sdk-load.service.ts | 4 ++-- 3 files changed, 13 insertions(+), 4 deletions(-) create mode 100644 apps/cli/src/platform/services/cli-sdk-load.service.ts diff --git a/apps/cli/src/platform/services/cli-sdk-load.service.ts b/apps/cli/src/platform/services/cli-sdk-load.service.ts new file mode 100644 index 00000000000..ee3b48e34d7 --- /dev/null +++ b/apps/cli/src/platform/services/cli-sdk-load.service.ts @@ -0,0 +1,9 @@ +import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service"; +import * as sdk from "@bitwarden/sdk-internal"; + +export class CliSdkLoadService implements SdkLoadService { + async load(): Promise { + const module = await import("@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"); + (sdk as any).init(module); + } +} diff --git a/apps/cli/src/service-container/service-container.ts b/apps/cli/src/service-container/service-container.ts index 230f09354bf..6a23be0f863 100644 --- a/apps/cli/src/service-container/service-container.ts +++ b/apps/cli/src/service-container/service-container.ts @@ -89,7 +89,6 @@ import { MemoryStorageService } from "@bitwarden/common/platform/services/memory import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; -import { DefaultSdkLoadService } from "@bitwarden/common/platform/services/sdk/default-sdk-load.service"; import { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { StateService } from "@bitwarden/common/platform/services/state.service"; @@ -170,6 +169,7 @@ import { import { CliBiometricsService } from "../key-management/cli-biometrics-service"; import { flagEnabled } from "../platform/flags"; import { CliPlatformUtilsService } from "../platform/services/cli-platform-utils.service"; +import { CliSdkLoadService } from "../platform/services/cli-sdk-load.service"; import { ConsoleLogService } from "../platform/services/console-log.service"; import { I18nService } from "../platform/services/i18n.service"; import { LowdbStorageService } from "../platform/services/lowdb-storage.service"; @@ -569,7 +569,7 @@ export class ServiceContainer { const sdkClientFactory = flagEnabled("sdk") ? new DefaultSdkClientFactory() : new NoopSdkClientFactory(); - this.sdkLoadService = new DefaultSdkLoadService(); + this.sdkLoadService = new CliSdkLoadService(); this.sdkService = new DefaultSdkService( sdkClientFactory, this.environmentService, diff --git a/libs/common/src/platform/services/sdk/default-sdk-load.service.ts b/libs/common/src/platform/services/sdk/default-sdk-load.service.ts index a4eca27676f..eff641f0351 100644 --- a/libs/common/src/platform/services/sdk/default-sdk-load.service.ts +++ b/libs/common/src/platform/services/sdk/default-sdk-load.service.ts @@ -1,5 +1,5 @@ import * as sdk from "@bitwarden/sdk-internal"; -import * as module from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"; +import * as bitwardenModule from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"; import { SdkLoadService } from "../../abstractions/sdk/sdk-load.service"; @@ -10,6 +10,6 @@ import { SdkLoadService } from "../../abstractions/sdk/sdk-load.service"; */ export class DefaultSdkLoadService implements SdkLoadService { async load(): Promise { - (sdk as any).init(module); + (sdk as any).init(bitwardenModule); } } From e62194ec5edd85818b6abb7e9cb2c48e107cf815 Mon Sep 17 00:00:00 2001 From: Matt Gibson Date: Tue, 14 Jan 2025 16:13:37 -0800 Subject: [PATCH 10/10] Fix linting --- .../src/platform/services/sdk/browser-sdk-load.service.ts | 2 +- apps/web/src/app/platform/web-sdk-load.service.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts b/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts index 573dd28f138..ca41127407c 100644 --- a/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts +++ b/apps/browser/src/platform/services/sdk/browser-sdk-load.service.ts @@ -18,7 +18,7 @@ const supported = (() => { return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; } } - } catch (e) { + } catch { // ignore } return false; diff --git a/apps/web/src/app/platform/web-sdk-load.service.ts b/apps/web/src/app/platform/web-sdk-load.service.ts index f1f98736f2f..cae3399b81e 100644 --- a/apps/web/src/app/platform/web-sdk-load.service.ts +++ b/apps/web/src/app/platform/web-sdk-load.service.ts @@ -12,7 +12,7 @@ const supported = (() => { return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; } } - } catch (e) { + } catch { // ignore } return false;