1
0
mirror of https://github.com/bitwarden/browser synced 2026-01-28 15:23:53 +00:00
Files
browser/libs/angular/src/platform/services/decentralized-init.service.example.ts
2026-01-20 16:06:49 +01:00

156 lines
5.2 KiB
TypeScript

/**
* Example usage of DefaultDecentralizedInitService
*
* This file demonstrates how to:
* 1. Make services implement Initializable
* 2. Register services with INIT_SERVICES
* 3. Use DefaultDecentralizedInitService in your app
*
* This is NOT production code - it's a reference example.
*/
import { Injectable, Type } from "@angular/core";
import { Initializable, INIT_SERVICES } from "../abstractions/decentralized-init.service";
// ============================================================================
// STEP 1: Make your services implement Initializable
// ============================================================================
/**
* Example: Service with no dependencies
* This will run first (or in parallel with other no-dependency services)
*/
@Injectable({ providedIn: "root" })
export class ExampleConfigService implements Initializable {
dependencies: Type<Initializable>[] = []; // No dependencies
async init(): Promise<void> {
// Load config, etc.
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
/**
* Example: Service that depends on ConfigService
* This will run AFTER ConfigService
*/
@Injectable({ providedIn: "root" })
export class ExampleDatabaseService implements Initializable {
dependencies = [ExampleConfigService]; // Type-safe class reference
constructor(private configService: ExampleConfigService) {}
async init(): Promise<void> {
// ConfigService is guaranteed to be initialized already
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
/**
* Example: Service with multiple dependencies
* This will run AFTER both ConfigService and DatabaseService
*/
@Injectable({ providedIn: "root" })
export class ExampleSyncService implements Initializable {
dependencies = [ExampleConfigService, ExampleDatabaseService];
constructor(
private configService: ExampleConfigService,
private databaseService: ExampleDatabaseService,
) {}
async init(): Promise<void> {
// Both dependencies are guaranteed to be initialized
await new Promise((resolve) => setTimeout(resolve, 100));
}
}
// ============================================================================
// STEP 2: Register services in your library's provider bundle
// ============================================================================
/**
* Each library exports a provider array that apps can import.
* Services with providedIn: 'root' don't need to be in the array,
* but the INIT_SERVICES registration IS required to prevent tree-shaking.
*/
export const EXAMPLE_LIBRARY_PROVIDERS = [
// The multi-provider registration prevents tree-shaking
// while providedIn: 'root' handles the actual service instantiation
{ provide: INIT_SERVICES, useExisting: ExampleConfigService, multi: true },
{ provide: INIT_SERVICES, useExisting: ExampleDatabaseService, multi: true },
{ provide: INIT_SERVICES, useExisting: ExampleSyncService, multi: true },
];
// ============================================================================
// STEP 3: Use in your app config
// ============================================================================
/**
* In your app's main config (e.g., app.config.ts or main.ts):
*
* import { DefaultDecentralizedInitService } from '@bitwarden/angular/platform/services/default-decentralized-init.service';
* import { EXAMPLE_LIBRARY_PROVIDERS } from '@bitwarden/angular/platform/services/decentralized-init.service.example';
*
* export const appConfig: ApplicationConfig = {
* providers: [
* ...EXAMPLE_LIBRARY_PROVIDERS,
* DefaultDecentralizedInitService,
* {
* provide: APP_INITIALIZER,
* useFactory: (initService: DefaultDecentralizedInitService) => () => initService.init(),
* deps: [DefaultDecentralizedInitService],
* multi: true,
* },
* ]
* };
*
* Or in your root component:
*
* @Component({ ... })
* export class AppComponent {
* constructor(private initService: DefaultDecentralizedInitService) {}
*
* ngOnInit() {
* await this.initService.init();
* }
* }
*/
// ============================================================================
// EXECUTION ORDER
// ============================================================================
/**
* Based on the dependency graph above, the execution order will be:
*
* 1. ExampleConfigService (no dependencies)
* 2. ExampleDatabaseService (depends on ConfigService)
* 3. ExampleSyncService (depends on ConfigService + DatabaseService)
*
* The topological sort automatically determines this order at runtime.
*/
// ============================================================================
// ERROR HANDLING
// ============================================================================
/**
* Circular dependency detection:
*
* If you have services like:
* - ServiceA depends on ServiceB
* - ServiceB depends on ServiceA
*
* You'll get a clear error:
* "Circular dependency detected: ServiceA -> ServiceB -> ServiceA"
*
* Missing dependency detection:
*
* If a service declares a dependency that isn't registered:
* "ServiceA depends on ServiceB, but ServiceB is not registered in INIT_SERVICES.
* Make sure to add it to your providers array:
* { provide: INIT_SERVICES, useExisting: ServiceB, multi: true }"
*/