Skip to content

Commit

Permalink
[PM-17479] Load-sdk-once (#12764)
Browse files Browse the repository at this point in the history
* create service to load sdk on application init

* Eagerly load CLI SDK

* Remove wasm logging to api

* Fix imports

* 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.

* init wasm module from sdk load service

* Use default client factory

* Fix type imports

* 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.

* Fix linting

* remove example code
  • Loading branch information
MGibson1 authored Jan 23, 2025
1 parent 620affd commit f9f30f8
Show file tree
Hide file tree
Showing 19 changed files with 151 additions and 97 deletions.
9 changes: 7 additions & 2 deletions apps/browser/src/background/main.background.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -125,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";
Expand Down Expand Up @@ -260,7 +262,7 @@ 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";
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
Expand Down Expand Up @@ -379,6 +381,7 @@ export default class MainBackground {
themeStateService: DefaultThemeStateService;
autoSubmitLoginBackground: AutoSubmitLoginBackground;
sdkService: SdkService;
sdkLoadService: SdkLoadService;
cipherAuthorizationService: CipherAuthorizationService;
inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;

Expand Down Expand Up @@ -730,8 +733,9 @@ export default class MainBackground {
);

const sdkClientFactory = flagEnabled("sdk")
? new BrowserSdkClientFactory(this.logService)
? new DefaultSdkClientFactory()
: new NoopSdkClientFactory();
this.sdkLoadService = new BrowserSdkLoadService(this.logService);
this.sdkService = new DefaultSdkService(
sdkClientFactory,
this.environmentService,
Expand Down Expand Up @@ -1257,6 +1261,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 });

Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
// 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 { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";

import { BrowserApi } from "../../browser/browser-api";

export type GlobalWithWasmInit = typeof globalThis & {
initSdk: () => void;
};

// https://stackoverflow.com/a/47880734
const supported = (() => {
try {
Expand All @@ -17,9 +18,7 @@ const supported = (() => {
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) {
} catch {
// ignore
}
return false;
Expand All @@ -33,54 +32,42 @@ let loadingPromise: Promise<any> | 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 load() {
async function importModule(): Promise<GlobalWithWasmInit["initSdk"]> {
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;
}

/**
* 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) {}
export class BrowserSdkLoadService implements SdkLoadService {
constructor(readonly logService: LogService) {}

async createSdkClient(
...args: ConstructorParameters<typeof BitwardenClient>
): Promise<BitwardenClient> {
async load(): Promise<void> {
const startTime = performance.now();
await load();

await importModule().then((initSdk) => initSdk());
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;
this.logService.info(`WASM SDK loaded in ${Math.round(endTime - startTime)}ms`);
}
}
6 changes: 3 additions & 3 deletions apps/browser/src/platform/services/sdk/fallback.ts
Original file line number Diff line number Diff line change
@@ -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<typeof sdk.BitwardenClient>) => {
(sdk as any).init(wasm);
import { GlobalWithWasmInit } from "./browser-sdk-load.service";

return new sdk.BitwardenClient(...args);
(globalThis as GlobalWithWasmInit).initSdk = () => {
(sdk as any).init(wasm);
};
6 changes: 3 additions & 3 deletions apps/browser/src/platform/services/sdk/wasm.ts
Original file line number Diff line number Diff line change
@@ -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<typeof sdk.BitwardenClient>) => {
(sdk as any).init(wasm);
import { GlobalWithWasmInit } from "./browser-sdk-load.service";

return new sdk.BitwardenClient(...args);
(globalThis as GlobalWithWasmInit).initSdk = () => {
(sdk as any).init(wasm);
};
3 changes: 3 additions & 0 deletions apps/browser/src/popup/services/init.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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();
Expand Down
15 changes: 11 additions & 4 deletions apps/browser/src/popup/services/services.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -82,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";
Expand Down Expand Up @@ -144,7 +146,7 @@ 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";
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
Expand Down Expand Up @@ -566,11 +568,16 @@ const safeProviders: SafeProvider[] = [
deps: [MessageSender, MessageListener],
}),
safeProvider({
provide: SdkClientFactory,
useFactory: (logService: LogService) =>
flagEnabled("sdk") ? new BrowserSdkClientFactory(logService) : new NoopSdkClientFactory(),
provide: SdkLoadService,
useClass: BrowserSdkLoadService,
deps: [LogService],
}),
safeProvider({
provide: SdkClientFactory,
useFactory: () =>
flagEnabled("sdk") ? new DefaultSdkClientFactory() : new NoopSdkClientFactory(),
deps: [],
}),
safeProvider({
provide: LoginEmailService,
useClass: LoginEmailService,
Expand Down
9 changes: 9 additions & 0 deletions apps/cli/src/platform/services/cli-sdk-load.service.ts
Original file line number Diff line number Diff line change
@@ -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<void> {
const module = await import("@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm");
(sdk as any).init(module);
}
}
5 changes: 5 additions & 0 deletions apps/cli/src/service-container/service-container.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,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";
Expand Down Expand Up @@ -171,6 +172,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";
Expand Down Expand Up @@ -270,6 +272,7 @@ export class ServiceContainer {
kdfConfigService: KdfConfigService;
taskSchedulerService: TaskSchedulerService;
sdkService: SdkService;
sdkLoadService: SdkLoadService;
cipherAuthorizationService: CipherAuthorizationService;

constructor() {
Expand Down Expand Up @@ -570,6 +573,7 @@ export class ServiceContainer {
const sdkClientFactory = flagEnabled("sdk")
? new DefaultSdkClientFactory()
: new NoopSdkClientFactory();
this.sdkLoadService = new CliSdkLoadService();
this.sdkService = new DefaultSdkService(
sdkClientFactory,
this.environmentService,
Expand Down Expand Up @@ -859,6 +863,7 @@ export class ServiceContainer {
return;
}

await this.sdkLoadService.load();
await this.storageService.init();
await this.stateService.init();
this.containerService.attachToGlobal(global);
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/app/services/init.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions apps/desktop/src/app/services/services.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand Down
Loading

0 comments on commit f9f30f8

Please sign in to comment.