1
0
mirror of https://github.com/bitwarden/browser.git synced 2025-01-30 22:41:33 +01:00

[PM-17479] Load-sdk-once (#12764)

* 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
This commit is contained in:
Matt Gibson 2025-01-23 11:34:22 -08:00 committed by GitHub
parent 620affd3d5
commit f9f30f8ec4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 151 additions and 97 deletions

View File

@ -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 { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.service";
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.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 { 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 { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service"; import { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import { import {
@ -125,6 +126,7 @@ import { FileUploadService } from "@bitwarden/common/platform/services/file-uplo
import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service"; import { KeyGenerationService } from "@bitwarden/common/platform/services/key-generation.service";
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; 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 { DefaultSdkService } from "@bitwarden/common/platform/services/sdk/default-sdk.service";
import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory"; import { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { StateService } from "@bitwarden/common/platform/services/state.service"; import { StateService } from "@bitwarden/common/platform/services/state.service";
@ -260,7 +262,7 @@ import { LocalBackedSessionStorageService } from "../platform/services/local-bac
import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service"; import { BackgroundPlatformUtilsService } from "../platform/services/platform-utils/background-platform-utils.service";
import { BrowserPlatformUtilsService } from "../platform/services/platform-utils/browser-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 { 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 { BackgroundTaskSchedulerService } from "../platform/services/task-scheduler/background-task-scheduler.service";
import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service"; import { BackgroundMemoryStorageService } from "../platform/storage/background-memory-storage.service";
import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider"; import { BrowserStorageServiceProvider } from "../platform/storage/browser-storage-service.provider";
@ -379,6 +381,7 @@ export default class MainBackground {
themeStateService: DefaultThemeStateService; themeStateService: DefaultThemeStateService;
autoSubmitLoginBackground: AutoSubmitLoginBackground; autoSubmitLoginBackground: AutoSubmitLoginBackground;
sdkService: SdkService; sdkService: SdkService;
sdkLoadService: SdkLoadService;
cipherAuthorizationService: CipherAuthorizationService; cipherAuthorizationService: CipherAuthorizationService;
inlineMenuFieldQualificationService: InlineMenuFieldQualificationService; inlineMenuFieldQualificationService: InlineMenuFieldQualificationService;
@ -730,8 +733,9 @@ export default class MainBackground {
); );
const sdkClientFactory = flagEnabled("sdk") const sdkClientFactory = flagEnabled("sdk")
? new BrowserSdkClientFactory(this.logService) ? new DefaultSdkClientFactory()
: new NoopSdkClientFactory(); : new NoopSdkClientFactory();
this.sdkLoadService = new BrowserSdkLoadService(this.logService);
this.sdkService = new DefaultSdkService( this.sdkService = new DefaultSdkService(
sdkClientFactory, sdkClientFactory,
this.environmentService, this.environmentService,
@ -1257,6 +1261,7 @@ export default class MainBackground {
async bootstrap() { async bootstrap() {
this.containerService.attachToGlobal(self); this.containerService.attachToGlobal(self);
await this.sdkLoadService.load();
// Only the "true" background should run migrations // Only the "true" background should run migrations
await this.stateService.init({ runMigrations: true }); await this.stateService.init({ runMigrations: true });

View File

@ -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 { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; import { SdkLoadService } from "@bitwarden/common/platform/abstractions/sdk/sdk-load.service";
import type { BitwardenClient } from "@bitwarden/sdk-internal";
import { BrowserApi } from "../../browser/browser-api"; import { BrowserApi } from "../../browser/browser-api";
export type GlobalWithWasmInit = typeof globalThis & {
initSdk: () => void;
};
// https://stackoverflow.com/a/47880734 // https://stackoverflow.com/a/47880734
const supported = (() => { const supported = (() => {
try { try {
@ -17,9 +18,7 @@ const supported = (() => {
return new WebAssembly.Instance(module) instanceof WebAssembly.Instance; return new WebAssembly.Instance(module) instanceof WebAssembly.Instance;
} }
} }
// FIXME: Remove when updating file. Eslint update } catch {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
// ignore // ignore
} }
return false; return false;
@ -33,54 +32,42 @@ let loadingPromise: Promise<any> | undefined;
if (BrowserApi.isManifestVersion(3)) { if (BrowserApi.isManifestVersion(3)) {
if (supported) { if (supported) {
// eslint-disable-next-line no-console // 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"); loadingPromise = import("./wasm");
} else { } else {
// eslint-disable-next-line no-console // 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"); loadingPromise = import("./fallback");
} }
} }
// Manifest v2 expects dynamic imports to prevent timing issues. // Manifest v2 expects dynamic imports to prevent timing issues.
async function load() { async function importModule(): Promise<GlobalWithWasmInit["initSdk"]> {
if (BrowserApi.isManifestVersion(3)) { if (BrowserApi.isManifestVersion(3)) {
// Ensure we have loaded the module // Ensure we have loaded the module
await loadingPromise; await loadingPromise;
return; } else if (supported) {
}
if (supported) {
// eslint-disable-next-line no-console // 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"); await import("./wasm");
} else { } else {
// eslint-disable-next-line no-console // 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"); 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 {
* SDK client factory with a js fallback for when WASM is not supported. constructor(readonly logService: LogService) {}
*
* Works both in popup and service worker.
*/
export class BrowserSdkClientFactory implements SdkClientFactory {
constructor(private logService: LogService) {}
async createSdkClient( async load(): Promise<void> {
...args: ConstructorParameters<typeof BitwardenClient>
): Promise<BitwardenClient> {
const startTime = performance.now(); const startTime = performance.now();
await load(); await importModule().then((initSdk) => initSdk());
const endTime = performance.now(); const endTime = performance.now();
const instance = (globalThis as any).init_sdk(...args); this.logService.info(`WASM SDK loaded in ${Math.round(endTime - startTime)}ms`);
this.logService.info("WASM SDK loaded in", Math.round(endTime - startTime), "ms");
return instance;
} }
} }

View File

@ -1,8 +1,8 @@
import * as sdk from "@bitwarden/sdk-internal"; import * as sdk from "@bitwarden/sdk-internal";
import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm.js"; import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm.js";
(globalThis as any).init_sdk = (...args: ConstructorParameters<typeof sdk.BitwardenClient>) => { import { GlobalWithWasmInit } from "./browser-sdk-load.service";
(sdk as any).init(wasm);
return new sdk.BitwardenClient(...args); (globalThis as GlobalWithWasmInit).initSdk = () => {
(sdk as any).init(wasm);
}; };

View File

@ -1,8 +1,8 @@
import * as sdk from "@bitwarden/sdk-internal"; import * as sdk from "@bitwarden/sdk-internal";
import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm"; import * as wasm from "@bitwarden/sdk-internal/bitwarden_wasm_internal_bg.wasm";
(globalThis as any).init_sdk = (...args: ConstructorParameters<typeof sdk.BitwardenClient>) => { import { GlobalWithWasmInit } from "./browser-sdk-load.service";
(sdk as any).init(wasm);
return new sdk.BitwardenClient(...args); (globalThis as GlobalWithWasmInit).initSdk = () => {
(sdk as any).init(wasm);
}; };

View File

@ -6,6 +6,7 @@ import { TwoFactorService } from "@bitwarden/common/auth/abstractions/two-factor
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service"; import { LogService as LogServiceAbstraction } from "@bitwarden/common/platform/abstractions/log.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.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 { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { BrowserApi } from "../../platform/browser/browser-api"; import { BrowserApi } from "../../platform/browser/browser-api";
@ -22,11 +23,13 @@ export class InitService {
private twoFactorService: TwoFactorService, private twoFactorService: TwoFactorService,
private logService: LogServiceAbstraction, private logService: LogServiceAbstraction,
private themingService: AbstractThemingService, private themingService: AbstractThemingService,
private sdkLoadService: SdkLoadService,
@Inject(DOCUMENT) private document: Document, @Inject(DOCUMENT) private document: Document,
) {} ) {}
init() { init() {
return async () => { return async () => {
await this.sdkLoadService.load();
await this.stateService.init({ runMigrations: false }); // Browser background is responsible for migrations await this.stateService.init({ runMigrations: false }); // Browser background is responsible for migrations
await this.i18nService.init(); await this.i18nService.init();
this.twoFactorService.init(); this.twoFactorService.init();

View File

@ -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 { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; 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 { StateService } from "@bitwarden/common/platform/abstractions/state.service";
import { import {
AbstractStorageService, AbstractStorageService,
@ -82,6 +83,7 @@ import { flagEnabled } from "@bitwarden/common/platform/misc/flags";
import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling"; import { TaskSchedulerService } from "@bitwarden/common/platform/scheduling";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.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 { NoopSdkClientFactory } from "@bitwarden/common/platform/services/sdk/noop-sdk-client-factory";
import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service"; import { WebCryptoFunctionService } from "@bitwarden/common/platform/services/web-crypto-function.service";
@ -144,7 +146,7 @@ import BrowserMemoryStorageService from "../../platform/services/browser-memory-
import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service"; import { BrowserScriptInjectorService } from "../../platform/services/browser-script-injector.service";
import I18nService from "../../platform/services/i18n.service"; import I18nService from "../../platform/services/i18n.service";
import { ForegroundPlatformUtilsService } from "../../platform/services/platform-utils/foreground-platform-utils.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 { ForegroundTaskSchedulerService } from "../../platform/services/task-scheduler/foreground-task-scheduler.service";
import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider"; import { BrowserStorageServiceProvider } from "../../platform/storage/browser-storage-service.provider";
import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service"; import { ForegroundMemoryStorageService } from "../../platform/storage/foreground-memory-storage.service";
@ -566,11 +568,16 @@ const safeProviders: SafeProvider[] = [
deps: [MessageSender, MessageListener], deps: [MessageSender, MessageListener],
}), }),
safeProvider({ safeProvider({
provide: SdkClientFactory, provide: SdkLoadService,
useFactory: (logService: LogService) => useClass: BrowserSdkLoadService,
flagEnabled("sdk") ? new BrowserSdkClientFactory(logService) : new NoopSdkClientFactory(),
deps: [LogService], deps: [LogService],
}), }),
safeProvider({
provide: SdkClientFactory,
useFactory: () =>
flagEnabled("sdk") ? new DefaultSdkClientFactory() : new NoopSdkClientFactory(),
deps: [],
}),
safeProvider({ safeProvider({
provide: LoginEmailService, provide: LoginEmailService,
useClass: LoginEmailService, useClass: LoginEmailService,

View File

@ -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);
}
}

View File

@ -68,6 +68,7 @@ import {
RegionConfig, RegionConfig,
} from "@bitwarden/common/platform/abstractions/environment.service"; } from "@bitwarden/common/platform/abstractions/environment.service";
import { KeyGenerationService as KeyGenerationServiceAbstraction } from "@bitwarden/common/platform/abstractions/key-generation.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 { SdkService } from "@bitwarden/common/platform/abstractions/sdk/sdk.service";
import { KeySuffixOptions, LogLevelType } from "@bitwarden/common/platform/enums"; import { KeySuffixOptions, LogLevelType } from "@bitwarden/common/platform/enums";
import { StateFactory } from "@bitwarden/common/platform/factories/state-factory"; import { StateFactory } from "@bitwarden/common/platform/factories/state-factory";
@ -171,6 +172,7 @@ import {
import { CliBiometricsService } from "../key-management/cli-biometrics-service"; import { CliBiometricsService } from "../key-management/cli-biometrics-service";
import { flagEnabled } from "../platform/flags"; import { flagEnabled } from "../platform/flags";
import { CliPlatformUtilsService } from "../platform/services/cli-platform-utils.service"; 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 { ConsoleLogService } from "../platform/services/console-log.service";
import { I18nService } from "../platform/services/i18n.service"; import { I18nService } from "../platform/services/i18n.service";
import { LowdbStorageService } from "../platform/services/lowdb-storage.service"; import { LowdbStorageService } from "../platform/services/lowdb-storage.service";
@ -270,6 +272,7 @@ export class ServiceContainer {
kdfConfigService: KdfConfigService; kdfConfigService: KdfConfigService;
taskSchedulerService: TaskSchedulerService; taskSchedulerService: TaskSchedulerService;
sdkService: SdkService; sdkService: SdkService;
sdkLoadService: SdkLoadService;
cipherAuthorizationService: CipherAuthorizationService; cipherAuthorizationService: CipherAuthorizationService;
constructor() { constructor() {
@ -570,6 +573,7 @@ export class ServiceContainer {
const sdkClientFactory = flagEnabled("sdk") const sdkClientFactory = flagEnabled("sdk")
? new DefaultSdkClientFactory() ? new DefaultSdkClientFactory()
: new NoopSdkClientFactory(); : new NoopSdkClientFactory();
this.sdkLoadService = new CliSdkLoadService();
this.sdkService = new DefaultSdkService( this.sdkService = new DefaultSdkService(
sdkClientFactory, sdkClientFactory,
this.environmentService, this.environmentService,
@ -859,6 +863,7 @@ export class ServiceContainer {
return; return;
} }
await this.sdkLoadService.load();
await this.storageService.init(); await this.storageService.init();
await this.stateService.init(); await this.stateService.init();
this.containerService.attachToGlobal(global); this.containerService.attachToGlobal(global);

View File

@ -11,6 +11,7 @@ import { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/comm
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.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 { 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 { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
@ -47,11 +48,13 @@ export class InitService {
private versionService: VersionService, private versionService: VersionService,
private sshAgentService: SshAgentService, private sshAgentService: SshAgentService,
private autofillService: DesktopAutofillService, private autofillService: DesktopAutofillService,
private sdkLoadService: SdkLoadService,
@Inject(DOCUMENT) private document: Document, @Inject(DOCUMENT) private document: Document,
) {} ) {}
init() { init() {
return async () => { return async () => {
await this.sdkLoadService.load();
await this.sshAgentService.init(); await this.sshAgentService.init();
this.nativeMessagingService.init(); this.nativeMessagingService.init();
await this.stateService.init({ runMigrations: false }); // Desktop will run them in main process await this.stateService.init({ runMigrations: false }); // Desktop will run them in main process

View File

@ -67,6 +67,7 @@ import {
import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service"; import { MessagingService as MessagingServiceAbstraction } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService as PlatformUtilsServiceAbstraction } from "@bitwarden/common/platform/abstractions/platform-utils.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 { 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 { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service"; import { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { SystemService as SystemServiceAbstraction } from "@bitwarden/common/platform/abstractions/system.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 { Fido2AuthenticatorService } from "@bitwarden/common/platform/services/fido2/fido2-authenticator.service";
import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service"; import { MemoryStorageService } from "@bitwarden/common/platform/services/memory-storage.service";
import { DefaultSdkClientFactory } from "@bitwarden/common/platform/services/sdk/default-sdk-client-factory"; 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 { 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 { SystemService } from "@bitwarden/common/platform/services/system.service";
import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state"; import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state";
// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage // eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage
@ -393,6 +396,11 @@ const safeProviders: SafeProvider[] = [
useClass: flagEnabled("sdk") ? DefaultSdkClientFactory : NoopSdkClientFactory, useClass: flagEnabled("sdk") ? DefaultSdkClientFactory : NoopSdkClientFactory,
deps: [], deps: [],
}), }),
safeProvider({
provide: SdkLoadService,
useClass: flagEnabled("sdk") ? DefaultSdkLoadService : NoopSdkLoadService,
deps: [],
}),
safeProvider({ safeProvider({
provide: LoginEmailService, provide: LoginEmailService,
useClass: LoginEmailService, useClass: LoginEmailService,

View File

@ -66,6 +66,7 @@ import { LogService } from "@bitwarden/common/platform/abstractions/log.service"
import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service"; import { MessagingService } from "@bitwarden/common/platform/abstractions/messaging.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import { SdkClientFactory } from "@bitwarden/common/platform/abstractions/sdk/sdk-client-factory"; 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 { AbstractStorageService } from "@bitwarden/common/platform/abstractions/storage.service";
import { ThemeType } from "@bitwarden/common/platform/enums"; import { ThemeType } from "@bitwarden/common/platform/enums";
import { AppIdService as DefaultAppIdService } from "@bitwarden/common/platform/services/app-id.service"; import { AppIdService as DefaultAppIdService } from "@bitwarden/common/platform/services/app-id.service";
@ -73,7 +74,9 @@ import { MemoryStorageService } from "@bitwarden/common/platform/services/memory
// eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage // eslint-disable-next-line import/no-restricted-paths -- Implementation for memory storage
import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service"; import { MigrationBuilderService } from "@bitwarden/common/platform/services/migration-builder.service";
import { MigrationRunner } from "@bitwarden/common/platform/services/migration-runner"; 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 { 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"; import { StorageServiceProvider } from "@bitwarden/common/platform/services/storage-service.provider";
/* eslint-disable import/no-restricted-paths -- Implementation for memory storage */ /* eslint-disable import/no-restricted-paths -- Implementation for memory storage */
import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state"; import { GlobalStateProvider, StateProvider } from "@bitwarden/common/platform/state";
@ -110,7 +113,7 @@ import { WebProcessReloadService } from "../key-management/services/web-process-
import { WebBiometricsService } from "../key-management/web-biometric.service"; import { WebBiometricsService } from "../key-management/web-biometric.service";
import { WebEnvironmentService } from "../platform/web-environment.service"; import { WebEnvironmentService } from "../platform/web-environment.service";
import { WebMigrationRunner } from "../platform/web-migration-runner"; 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 { WebStorageServiceProvider } from "../platform/web-storage-service.provider";
import { EventService } from "./event.service"; import { EventService } from "./event.service";
@ -288,9 +291,14 @@ const safeProviders: SafeProvider[] = [
useClass: DefaultCollectionAdminService, useClass: DefaultCollectionAdminService,
deps: [ApiService, KeyServiceAbstraction, EncryptService, CollectionService], deps: [ApiService, KeyServiceAbstraction, EncryptService, CollectionService],
}), }),
safeProvider({
provide: SdkLoadService,
useClass: flagEnabled("sdk") ? WebSdkLoadService : NoopSdkLoadService,
deps: [],
}),
safeProvider({ safeProvider({
provide: SdkClientFactory, provide: SdkClientFactory,
useClass: flagEnabled("sdk") ? WebSdkClientFactory : NoopSdkClientFactory, useClass: flagEnabled("sdk") ? DefaultSdkClientFactory : NoopSdkClientFactory,
deps: [], deps: [],
}), }),
safeProvider({ safeProvider({

View File

@ -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 { TwoFactorService as TwoFactorServiceAbstraction } from "@bitwarden/common/auth/abstractions/two-factor.service";
import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service"; import { EncryptService } from "@bitwarden/common/platform/abstractions/encrypt.service";
import { I18nService as I18nServiceAbstraction } from "@bitwarden/common/platform/abstractions/i18n.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 { StateService as StateServiceAbstraction } from "@bitwarden/common/platform/abstractions/state.service";
import { ContainerService } from "@bitwarden/common/platform/services/container.service"; import { ContainerService } from "@bitwarden/common/platform/services/container.service";
import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service"; import { UserAutoUnlockKeyService } from "@bitwarden/common/platform/services/user-auto-unlock-key.service";
@ -35,11 +36,13 @@ export class InitService {
private userAutoUnlockKeyService: UserAutoUnlockKeyService, private userAutoUnlockKeyService: UserAutoUnlockKeyService,
private accountService: AccountService, private accountService: AccountService,
private versionService: VersionService, private versionService: VersionService,
private sdkLoadService: SdkLoadService,
@Inject(DOCUMENT) private document: Document, @Inject(DOCUMENT) private document: Document,
) {} ) {}
init() { init() {
return async () => { return async () => {
await this.sdkLoadService.load();
await this.stateService.init(); await this.stateService.init();
const activeAccount = await firstValueFrom(this.accountService.activeAccount$); const activeAccount = await firstValueFrom(this.accountService.activeAccount$);

View File

@ -1,44 +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<typeof sdk.BitwardenClient>
): Promise<sdk.BitwardenClient> {
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");
}
}

View File

@ -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 {
// ignore
}
return false;
})();
export class WebSdkLoadService implements SdkLoadService {
async load(): Promise<void> {
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);
}
}

View File

@ -4,6 +4,10 @@ import type { BitwardenClient } from "@bitwarden/sdk-internal";
* Factory for creating SDK clients. * Factory for creating SDK clients.
*/ */
export abstract class SdkClientFactory { export abstract class SdkClientFactory {
/**
* Creates a new BitwardenClient. Assumes the SDK is already loaded.
* @param args Bitwarden client constructor parameters
*/
abstract createSdkClient( abstract createSdkClient(
...args: ConstructorParameters<typeof BitwardenClient> ...args: ConstructorParameters<typeof BitwardenClient>
): Promise<BitwardenClient>; ): Promise<BitwardenClient>;

View File

@ -0,0 +1,3 @@
export abstract class SdkLoadService {
abstract load(): Promise<void>;
}

View File

@ -1,19 +1,19 @@
import * as sdk from "@bitwarden/sdk-internal"; 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"; import { SdkClientFactory } from "../../abstractions/sdk/sdk-client-factory";
/** /**
* Directly imports the Bitwarden SDK and initializes it. * Default SDK client factory.
*
* **Warning**: This requires WASM support and will fail if the environment does not support it.
*/ */
export class DefaultSdkClientFactory implements SdkClientFactory { 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( async createSdkClient(
...args: ConstructorParameters<typeof sdk.BitwardenClient> ...args: ConstructorParameters<typeof sdk.BitwardenClient>
): Promise<sdk.BitwardenClient> { ): Promise<sdk.BitwardenClient> {
(sdk as any).init(module);
return Promise.resolve(new sdk.BitwardenClient(...args)); return Promise.resolve(new sdk.BitwardenClient(...args));
} }
} }

View File

@ -0,0 +1,15 @@
import * as sdk from "@bitwarden/sdk-internal";
import * as bitwardenModule 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.
*
* **Warning**: This requires WASM support and will fail if the environment does not support it.
*/
export class DefaultSdkLoadService implements SdkLoadService {
async load(): Promise<void> {
(sdk as any).init(bitwardenModule);
}
}

View File

@ -0,0 +1,7 @@
import { SdkLoadService } from "../../abstractions/sdk/sdk-load.service";
export class NoopSdkLoadService extends SdkLoadService {
async load() {
return;
}
}