1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-14 15:23:33 +00:00

[AC-2278] [AC-2296] Use SafeProvider in browser services module (#8418)

This commit is contained in:
Thomas Rittson
2024-03-28 08:28:51 +10:00
committed by GitHub
parent 5cb2e99b2f
commit b3b344866e
5 changed files with 381 additions and 361 deletions

View File

@@ -1,8 +1,5 @@
import { Injectable } from "@angular/core";
import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards"; import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards";
@Injectable()
export class UnauthGuardService extends BaseUnauthGuardService { export class UnauthGuardService extends BaseUnauthGuardService {
protected homepage = "tabs/current"; protected homepage = "tabs/current";
} }

View File

@@ -1,14 +1,14 @@
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { ConsoleLogService } from "@bitwarden/common/platform/services/console-log.service"; import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
import { SearchService } from "@bitwarden/common/services/search.service"; import { SearchService } from "@bitwarden/common/services/search.service";
export class PopupSearchService extends SearchService { export class PopupSearchService extends SearchService {
constructor( constructor(
private mainSearchService: SearchService, private mainSearchService: SearchService,
consoleLogService: ConsoleLogService, logService: LogService,
i18nService: I18nService, i18nService: I18nService,
) { ) {
super(consoleLogService, i18nService); super(logService, i18nService);
} }
clearIndex() { clearIndex() {

View File

@@ -1,15 +1,18 @@
import { APP_INITIALIZER, NgModule, NgZone } from "@angular/core"; import { APP_INITIALIZER, NgModule, NgZone } from "@angular/core";
import { DomSanitizer } from "@angular/platform-browser"; import { DomSanitizer } from "@angular/platform-browser";
import { Router } from "@angular/router";
import { ToastrService } from "ngx-toastr"; import { ToastrService } from "ngx-toastr";
import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards"; import { UnauthGuard as BaseUnauthGuardService } from "@bitwarden/angular/auth/guards";
import { AngularThemingService } from "@bitwarden/angular/platform/services/theming/angular-theming.service"; import { AngularThemingService } from "@bitwarden/angular/platform/services/theming/angular-theming.service";
import { SafeProvider, safeProvider } from "@bitwarden/angular/platform/utils/safe-provider";
import { import {
MEMORY_STORAGE, MEMORY_STORAGE,
SECURE_STORAGE, SECURE_STORAGE,
OBSERVABLE_DISK_STORAGE, OBSERVABLE_DISK_STORAGE,
OBSERVABLE_MEMORY_STORAGE, OBSERVABLE_MEMORY_STORAGE,
SYSTEM_THEME_OBSERVABLE, SYSTEM_THEME_OBSERVABLE,
SafeInjectionToken,
} from "@bitwarden/angular/services/injection-tokens"; } from "@bitwarden/angular/services/injection-tokens";
import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module"; import { JslibServicesModule } from "@bitwarden/angular/services/jslib-services.module";
import { import {
@@ -129,323 +132,351 @@ function getBgService<T>(service: keyof MainBackground) {
}; };
} }
/**
* Provider definitions used in the ngModule.
* Add your provider definition here using the safeProvider function as a wrapper. This will give you type safety.
* If you need help please ask for it, do NOT change the type of this array.
*/
const safeProviders: SafeProvider[] = [
safeProvider(InitService),
safeProvider(DebounceNavigationService),
safeProvider(DialogService),
safeProvider(PopupCloseWarningService),
safeProvider({
provide: APP_INITIALIZER as SafeInjectionToken<() => Promise<void>>,
useFactory: (initService: InitService) => initService.init(),
deps: [InitService],
multi: true,
}),
safeProvider({
provide: BaseUnauthGuardService,
useClass: UnauthGuardService,
deps: [AuthServiceAbstraction, Router],
}),
safeProvider({
provide: MessagingService,
useFactory: () => {
return needsBackgroundInit
? new BrowserMessagingPrivateModePopupService()
: new BrowserMessagingService();
},
deps: [],
}),
safeProvider({
provide: TwoFactorService,
useFactory: getBgService<TwoFactorService>("twoFactorService"),
deps: [],
}),
safeProvider({
provide: AuthServiceAbstraction,
useFactory: getBgService<AuthService>("authService"),
deps: [],
}),
safeProvider({
provide: LoginStrategyServiceAbstraction,
useFactory: getBgService<LoginStrategyServiceAbstraction>("loginStrategyService"),
deps: [],
}),
safeProvider({
provide: SsoLoginServiceAbstraction,
useFactory: getBgService<SsoLoginServiceAbstraction>("ssoLoginService"),
deps: [],
}),
safeProvider({
provide: SearchServiceAbstraction,
useFactory: (logService: LogService, i18nService: I18nServiceAbstraction) => {
return new PopupSearchService(
getBgService<SearchService>("searchService")(),
logService,
i18nService,
);
},
deps: [LogService, I18nServiceAbstraction],
}),
safeProvider({
provide: CipherFileUploadService,
useFactory: getBgService<CipherFileUploadService>("cipherFileUploadService"),
deps: [],
}),
safeProvider({
provide: CipherService,
useFactory: getBgService<CipherService>("cipherService"),
deps: [],
}),
safeProvider({
provide: CryptoFunctionService,
useFactory: () => new WebCryptoFunctionService(window),
deps: [],
}),
safeProvider({
provide: CollectionService,
useFactory: getBgService<CollectionService>("collectionService"),
deps: [],
}),
safeProvider({
provide: LogService,
useFactory: (platformUtilsService: PlatformUtilsService) =>
new ConsoleLogService(platformUtilsService.isDev()),
deps: [PlatformUtilsService],
}),
safeProvider({
provide: EnvironmentService,
useExisting: BrowserEnvironmentService,
}),
safeProvider({
provide: BrowserEnvironmentService,
useClass: BrowserEnvironmentService,
deps: [LogService, StateProvider, AccountServiceAbstraction],
}),
safeProvider({
provide: TotpService,
useFactory: getBgService<TotpService>("totpService"),
deps: [],
}),
safeProvider({
provide: I18nServiceAbstraction,
useFactory: (globalStateProvider: GlobalStateProvider) => {
return new I18nService(BrowserApi.getUILanguage(), globalStateProvider);
},
deps: [GlobalStateProvider],
}),
safeProvider({
provide: CryptoService,
useFactory: (encryptService: EncryptService) => {
const cryptoService = getBgService<CryptoService>("cryptoService")();
new ContainerService(cryptoService, encryptService).attachToGlobal(self);
return cryptoService;
},
deps: [EncryptService],
}),
safeProvider({
provide: AuthRequestServiceAbstraction,
useFactory: getBgService<AuthRequestServiceAbstraction>("authRequestService"),
deps: [],
}),
safeProvider({
provide: DeviceTrustCryptoServiceAbstraction,
useFactory: getBgService<DeviceTrustCryptoServiceAbstraction>("deviceTrustCryptoService"),
deps: [],
}),
safeProvider({
provide: DevicesServiceAbstraction,
useFactory: getBgService<DevicesServiceAbstraction>("devicesService"),
deps: [],
}),
safeProvider({
provide: PlatformUtilsService,
useExisting: ForegroundPlatformUtilsService,
}),
safeProvider({
provide: ForegroundPlatformUtilsService,
useClass: ForegroundPlatformUtilsService,
useFactory: (sanitizer: DomSanitizer, toastrService: ToastrService) => {
return new ForegroundPlatformUtilsService(
sanitizer,
toastrService,
(clipboardValue: string, clearMs: number) => {
void BrowserApi.sendMessage("clearClipboard", { clipboardValue, clearMs });
},
async () => {
const response = await BrowserApi.sendMessageWithResponse<{
result: boolean;
error: string;
}>("biometricUnlock");
if (!response.result) {
throw response.error;
}
return response.result;
},
window,
);
},
deps: [DomSanitizer, ToastrService],
}),
safeProvider({
provide: PasswordGenerationServiceAbstraction,
useFactory: getBgService<PasswordGenerationServiceAbstraction>("passwordGenerationService"),
deps: [],
}),
safeProvider({
provide: SyncService,
useFactory: getBgService<SyncService>("syncService"),
deps: [],
}),
safeProvider({
provide: DomainSettingsService,
useClass: DefaultDomainSettingsService,
deps: [StateProvider],
}),
safeProvider({
provide: AbstractStorageService,
useClass: BrowserLocalStorageService,
deps: [],
}),
safeProvider({
provide: AutofillService,
useFactory: getBgService<AutofillService>("autofillService"),
deps: [],
}),
safeProvider({
provide: VaultExportServiceAbstraction,
useFactory: getBgService<VaultExportServiceAbstraction>("exportService"),
deps: [],
}),
safeProvider({
provide: KeyConnectorService,
useFactory: getBgService<KeyConnectorService>("keyConnectorService"),
deps: [],
}),
safeProvider({
provide: UserVerificationService,
useFactory: getBgService<UserVerificationService>("userVerificationService"),
deps: [],
}),
safeProvider({
provide: VaultTimeoutSettingsService,
useFactory: getBgService<VaultTimeoutSettingsService>("vaultTimeoutSettingsService"),
deps: [],
}),
safeProvider({
provide: VaultTimeoutService,
useFactory: getBgService<VaultTimeoutService>("vaultTimeoutService"),
deps: [],
}),
safeProvider({
provide: NotificationsService,
useFactory: getBgService<NotificationsService>("notificationsService"),
deps: [],
}),
safeProvider({
provide: VaultFilterService,
useClass: VaultFilterService,
deps: [
OrganizationService,
FolderServiceAbstraction,
CipherService,
CollectionService,
PolicyService,
StateProvider,
AccountServiceAbstraction,
],
}),
safeProvider({
provide: SECURE_STORAGE,
useExisting: AbstractStorageService, // Secure storage is not available in the browser, so we use normal storage instead and warn users when it is used.
}),
safeProvider({
provide: MEMORY_STORAGE,
useFactory: getBgService<AbstractStorageService>("memoryStorageService"),
deps: [],
}),
safeProvider({
provide: OBSERVABLE_MEMORY_STORAGE,
useClass: ForegroundMemoryStorageService,
deps: [],
}),
safeProvider({
provide: OBSERVABLE_DISK_STORAGE,
useExisting: AbstractStorageService,
}),
safeProvider({
provide: StateServiceAbstraction,
useFactory: (
storageService: AbstractStorageService,
secureStorageService: AbstractStorageService,
memoryStorageService: AbstractMemoryStorageService,
logService: LogService,
accountService: AccountServiceAbstraction,
environmentService: EnvironmentService,
tokenService: TokenService,
migrationRunner: MigrationRunner,
) => {
return new BrowserStateService(
storageService,
secureStorageService,
memoryStorageService,
logService,
new StateFactory(GlobalState, Account),
accountService,
environmentService,
tokenService,
migrationRunner,
);
},
deps: [
AbstractStorageService,
SECURE_STORAGE,
MEMORY_STORAGE,
LogService,
AccountServiceAbstraction,
EnvironmentService,
TokenService,
MigrationRunner,
],
}),
safeProvider({
provide: UsernameGenerationServiceAbstraction,
useFactory: getBgService<UsernameGenerationServiceAbstraction>("usernameGenerationService"),
deps: [],
}),
safeProvider({
provide: BaseStateServiceAbstraction,
useExisting: StateServiceAbstraction,
deps: [],
}),
safeProvider({
provide: FileDownloadService,
useClass: BrowserFileDownloadService,
deps: [],
}),
safeProvider({
provide: LoginServiceAbstraction,
useClass: LoginService,
deps: [StateServiceAbstraction],
}),
safeProvider({
provide: SYSTEM_THEME_OBSERVABLE,
useFactory: (platformUtilsService: PlatformUtilsService) => {
// Safari doesn't properly handle the (prefers-color-scheme) media query in the popup window, it always returns light.
// In Safari, we have to use the background page instead, which comes with limitations like not dynamically changing the extension theme when the system theme is changed.
let windowContext = window;
const backgroundWindow = BrowserApi.getBackgroundPage();
if (platformUtilsService.isSafari() && backgroundWindow) {
windowContext = backgroundWindow;
}
return AngularThemingService.createSystemThemeFromWindow(windowContext);
},
deps: [PlatformUtilsService],
}),
safeProvider({
provide: FilePopoutUtilsService,
useFactory: (platformUtilsService: PlatformUtilsService) => {
return new FilePopoutUtilsService(platformUtilsService);
},
deps: [PlatformUtilsService],
}),
safeProvider({
provide: DerivedStateProvider,
useClass: ForegroundDerivedStateProvider,
deps: [OBSERVABLE_MEMORY_STORAGE, NgZone],
}),
safeProvider({
provide: AutofillSettingsServiceAbstraction,
useClass: AutofillSettingsService,
deps: [StateProvider, PolicyService],
}),
safeProvider({
provide: UserNotificationSettingsServiceAbstraction,
useClass: UserNotificationSettingsService,
deps: [StateProvider],
}),
];
@NgModule({ @NgModule({
imports: [JslibServicesModule], imports: [JslibServicesModule],
declarations: [], declarations: [],
providers: [ // Do not register your dependency here! Add it to the typesafeProviders array using the helper function
InitService, providers: safeProviders,
DebounceNavigationService,
DialogService,
PopupCloseWarningService,
{
provide: APP_INITIALIZER,
useFactory: (initService: InitService) => initService.init(),
deps: [InitService],
multi: true,
},
{ provide: BaseUnauthGuardService, useClass: UnauthGuardService },
{
provide: MessagingService,
useFactory: () => {
return needsBackgroundInit
? new BrowserMessagingPrivateModePopupService()
: new BrowserMessagingService();
},
},
{
provide: TwoFactorService,
useFactory: getBgService<TwoFactorService>("twoFactorService"),
deps: [],
},
{
provide: AuthServiceAbstraction,
useFactory: getBgService<AuthService>("authService"),
deps: [],
},
{
provide: LoginStrategyServiceAbstraction,
useFactory: getBgService<LoginStrategyServiceAbstraction>("loginStrategyService"),
},
{
provide: SsoLoginServiceAbstraction,
useFactory: getBgService<SsoLoginServiceAbstraction>("ssoLoginService"),
deps: [],
},
{
provide: SearchServiceAbstraction,
useFactory: (logService: ConsoleLogService, i18nService: I18nServiceAbstraction) => {
return new PopupSearchService(
getBgService<SearchService>("searchService")(),
logService,
i18nService,
);
},
deps: [LogService, I18nServiceAbstraction],
},
{
provide: CipherFileUploadService,
useFactory: getBgService<CipherFileUploadService>("cipherFileUploadService"),
deps: [],
},
{ provide: CipherService, useFactory: getBgService<CipherService>("cipherService"), deps: [] },
{
provide: CryptoFunctionService,
useFactory: () => new WebCryptoFunctionService(window),
deps: [],
},
{
provide: CollectionService,
useFactory: getBgService<CollectionService>("collectionService"),
deps: [],
},
{
provide: LogService,
useFactory: (platformUtilsService: PlatformUtilsService) =>
new ConsoleLogService(platformUtilsService.isDev()),
deps: [PlatformUtilsService],
},
{
provide: BrowserEnvironmentService,
useClass: BrowserEnvironmentService,
deps: [LogService, StateProvider, AccountServiceAbstraction],
},
{
provide: EnvironmentService,
useExisting: BrowserEnvironmentService,
},
{ provide: TotpService, useFactory: getBgService<TotpService>("totpService"), deps: [] },
{
provide: I18nServiceAbstraction,
useFactory: (globalStateProvider: GlobalStateProvider) => {
return new I18nService(BrowserApi.getUILanguage(), globalStateProvider);
},
deps: [GlobalStateProvider],
},
{
provide: CryptoService,
useFactory: (encryptService: EncryptService) => {
const cryptoService = getBgService<CryptoService>("cryptoService")();
new ContainerService(cryptoService, encryptService).attachToGlobal(self);
return cryptoService;
},
deps: [EncryptService],
},
{
provide: AuthRequestServiceAbstraction,
useFactory: getBgService<AuthRequestServiceAbstraction>("authRequestService"),
deps: [],
},
{
provide: DeviceTrustCryptoServiceAbstraction,
useFactory: getBgService<DeviceTrustCryptoServiceAbstraction>("deviceTrustCryptoService"),
deps: [],
},
{
provide: DevicesServiceAbstraction,
useFactory: getBgService<DevicesServiceAbstraction>("devicesService"),
deps: [],
},
{
provide: PlatformUtilsService,
useExisting: ForegroundPlatformUtilsService,
},
{
provide: ForegroundPlatformUtilsService,
useClass: ForegroundPlatformUtilsService,
useFactory: (sanitizer: DomSanitizer, toastrService: ToastrService) => {
return new ForegroundPlatformUtilsService(
sanitizer,
toastrService,
(clipboardValue: string, clearMs: number) => {
void BrowserApi.sendMessage("clearClipboard", { clipboardValue, clearMs });
},
async () => {
const response = await BrowserApi.sendMessageWithResponse<{
result: boolean;
error: string;
}>("biometricUnlock");
if (!response.result) {
throw response.error;
}
return response.result;
},
window,
);
},
deps: [DomSanitizer, ToastrService],
},
{
provide: PasswordGenerationServiceAbstraction,
useFactory: getBgService<PasswordGenerationServiceAbstraction>("passwordGenerationService"),
deps: [],
},
{ provide: SyncService, useFactory: getBgService<SyncService>("syncService"), deps: [] },
{
provide: DomainSettingsService,
useClass: DefaultDomainSettingsService,
deps: [StateProvider],
},
{
provide: AbstractStorageService,
useClass: BrowserLocalStorageService,
deps: [],
},
{
provide: AutofillService,
useFactory: getBgService<AutofillService>("autofillService"),
deps: [],
},
{
provide: VaultExportServiceAbstraction,
useFactory: getBgService<VaultExportServiceAbstraction>("exportService"),
deps: [],
},
{
provide: KeyConnectorService,
useFactory: getBgService<KeyConnectorService>("keyConnectorService"),
deps: [],
},
{
provide: UserVerificationService,
useFactory: getBgService<UserVerificationService>("userVerificationService"),
deps: [],
},
{
provide: VaultTimeoutSettingsService,
useFactory: getBgService<VaultTimeoutSettingsService>("vaultTimeoutSettingsService"),
deps: [],
},
{
provide: VaultTimeoutService,
useFactory: getBgService<VaultTimeoutService>("vaultTimeoutService"),
deps: [],
},
{
provide: NotificationsService,
useFactory: getBgService<NotificationsService>("notificationsService"),
deps: [],
},
{
provide: VaultFilterService,
useClass: VaultFilterService,
deps: [
OrganizationService,
FolderServiceAbstraction,
CipherService,
CollectionService,
PolicyService,
StateProvider,
AccountServiceAbstraction,
],
},
{
provide: SECURE_STORAGE,
useExisting: AbstractStorageService, // Secure storage is not available in the browser, so we use normal storage instead and warn users when it is used.
},
{
provide: MEMORY_STORAGE,
useFactory: getBgService<AbstractStorageService>("memoryStorageService"),
},
{
provide: OBSERVABLE_MEMORY_STORAGE,
useClass: ForegroundMemoryStorageService,
deps: [],
},
{
provide: OBSERVABLE_DISK_STORAGE,
useExisting: AbstractStorageService,
},
{
provide: StateServiceAbstraction,
useFactory: (
storageService: AbstractStorageService,
secureStorageService: AbstractStorageService,
memoryStorageService: AbstractMemoryStorageService,
logService: LogService,
accountService: AccountServiceAbstraction,
environmentService: EnvironmentService,
tokenService: TokenService,
migrationRunner: MigrationRunner,
) => {
return new BrowserStateService(
storageService,
secureStorageService,
memoryStorageService,
logService,
new StateFactory(GlobalState, Account),
accountService,
environmentService,
tokenService,
migrationRunner,
);
},
deps: [
AbstractStorageService,
SECURE_STORAGE,
MEMORY_STORAGE,
LogService,
AccountServiceAbstraction,
EnvironmentService,
TokenService,
MigrationRunner,
],
},
{
provide: UsernameGenerationServiceAbstraction,
useFactory: getBgService<UsernameGenerationServiceAbstraction>("usernameGenerationService"),
deps: [],
},
{
provide: BaseStateServiceAbstraction,
useExisting: StateServiceAbstraction,
deps: [],
},
{
provide: FileDownloadService,
useClass: BrowserFileDownloadService,
},
{
provide: LoginServiceAbstraction,
useClass: LoginService,
deps: [StateServiceAbstraction],
},
{
provide: SYSTEM_THEME_OBSERVABLE,
useFactory: (platformUtilsService: PlatformUtilsService) => {
// Safari doesn't properly handle the (prefers-color-scheme) media query in the popup window, it always returns light.
// In Safari, we have to use the background page instead, which comes with limitations like not dynamically changing the extension theme when the system theme is changed.
let windowContext = window;
const backgroundWindow = BrowserApi.getBackgroundPage();
if (platformUtilsService.isSafari() && backgroundWindow) {
windowContext = backgroundWindow;
}
return AngularThemingService.createSystemThemeFromWindow(windowContext);
},
deps: [PlatformUtilsService],
},
{
provide: FilePopoutUtilsService,
useFactory: (platformUtilsService: PlatformUtilsService) => {
return new FilePopoutUtilsService(platformUtilsService);
},
deps: [PlatformUtilsService],
},
{
provide: DerivedStateProvider,
useClass: ForegroundDerivedStateProvider,
deps: [OBSERVABLE_MEMORY_STORAGE, NgZone],
},
{
provide: AutofillSettingsServiceAbstraction,
useClass: AutofillSettingsService,
deps: [StateProvider, PolicyService],
},
{
provide: UserNotificationSettingsServiceAbstraction,
useClass: UserNotificationSettingsService,
deps: [StateProvider],
},
],
}) })
export class ServicesModule {} export class ServicesModule {}

View File

@@ -4,7 +4,7 @@ import { Constructor, Opaque } from "type-fest";
import { SafeInjectionToken } from "../../services/injection-tokens"; import { SafeInjectionToken } from "../../services/injection-tokens";
/** /**
* The return type of our dependency helper functions. * The return type of the {@link safeProvider} helper function.
* Used to distinguish a type safe provider definition from a non-type safe provider definition. * Used to distinguish a type safe provider definition from a non-type safe provider definition.
*/ */
export type SafeProvider = Opaque<Provider>; export type SafeProvider = Opaque<Provider>;
@@ -18,12 +18,22 @@ type MapParametersToDeps<T> = {
type SafeInjectionTokenType<T> = T extends SafeInjectionToken<infer J> ? J : never; type SafeInjectionTokenType<T> = T extends SafeInjectionToken<infer J> ? J : never;
/**
* Gets the instance type from a constructor, abstract constructor, or SafeInjectionToken
*/
type ProviderInstanceType<T> =
T extends SafeInjectionToken<any>
? InstanceType<SafeInjectionTokenType<T>>
: T extends Constructor<any> | AbstractConstructor<any>
? InstanceType<T>
: never;
/** /**
* Represents a dependency provided with the useClass option. * Represents a dependency provided with the useClass option.
*/ */
type SafeClassProvider< type SafeClassProvider<
A extends AbstractConstructor<any>, A extends AbstractConstructor<any> | SafeInjectionToken<any>,
I extends Constructor<InstanceType<A>>, I extends Constructor<ProviderInstanceType<A>>,
D extends MapParametersToDeps<ConstructorParameters<I>>, D extends MapParametersToDeps<ConstructorParameters<I>>,
> = { > = {
provide: A; provide: A;
@@ -40,37 +50,25 @@ type SafeValueProvider<A extends SafeInjectionToken<any>, V extends SafeInjectio
}; };
/** /**
* Represents a dependency provided with the useFactory option where a SafeInjectionToken is used as the token. * Represents a dependency provided with the useFactory option.
*/ */
type SafeFactoryProviderWithToken< type SafeFactoryProvider<
A extends SafeInjectionToken<any>, A extends AbstractConstructor<any> | SafeInjectionToken<any>,
I extends (...args: any) => InstanceType<SafeInjectionTokenType<A>>, I extends (...args: any) => ProviderInstanceType<A>,
D extends MapParametersToDeps<Parameters<I>>,
> = {
provide: A;
useFactory: I;
deps: D;
};
/**
* Represents a dependency provided with the useFactory option where an abstract class is used as the token.
*/
type SafeFactoryProviderWithClass<
A extends AbstractConstructor<any>,
I extends (...args: any) => InstanceType<A>,
D extends MapParametersToDeps<Parameters<I>>, D extends MapParametersToDeps<Parameters<I>>,
> = { > = {
provide: A; provide: A;
useFactory: I; useFactory: I;
deps: D; deps: D;
multi?: boolean;
}; };
/** /**
* Represents a dependency provided with the useExisting option. * Represents a dependency provided with the useExisting option.
*/ */
type SafeExistingProvider< type SafeExistingProvider<
A extends Constructor<any> | AbstractConstructor<any>, A extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>,
I extends Constructor<InstanceType<A>> | AbstractConstructor<InstanceType<A>>, I extends Constructor<ProviderInstanceType<A>> | AbstractConstructor<ProviderInstanceType<A>>,
> = { > = {
provide: A; provide: A;
useExisting: I; useExisting: I;
@@ -84,31 +82,26 @@ type SafeExistingProvider<
*/ */
export const safeProvider = < export const safeProvider = <
// types for useClass // types for useClass
AClass extends AbstractConstructor<any>, AClass extends AbstractConstructor<any> | SafeInjectionToken<any>,
IClass extends Constructor<InstanceType<AClass>>, IClass extends Constructor<ProviderInstanceType<AClass>>,
DClass extends MapParametersToDeps<ConstructorParameters<IClass>>, DClass extends MapParametersToDeps<ConstructorParameters<IClass>>,
// types for useValue // types for useValue
AValue extends SafeInjectionToken<any>, AValue extends SafeInjectionToken<any>,
VValue extends SafeInjectionTokenType<AValue>, VValue extends SafeInjectionTokenType<AValue>,
// types for useFactoryWithToken // types for useFactory
AFactoryToken extends SafeInjectionToken<any>, AFactory extends AbstractConstructor<any> | SafeInjectionToken<any>,
IFactoryToken extends (...args: any) => InstanceType<SafeInjectionTokenType<AFactoryToken>>, IFactory extends (...args: any) => ProviderInstanceType<AFactory>,
DFactoryToken extends MapParametersToDeps<Parameters<IFactoryToken>>, DFactory extends MapParametersToDeps<Parameters<IFactory>>,
// types for useFactoryWithClass
AFactoryClass extends AbstractConstructor<any>,
IFactoryClass extends (...args: any) => InstanceType<AFactoryClass>,
DFactoryClass extends MapParametersToDeps<Parameters<IFactoryClass>>,
// types for useExisting // types for useExisting
AExisting extends Constructor<any> | AbstractConstructor<any>, AExisting extends Constructor<any> | AbstractConstructor<any> | SafeInjectionToken<any>,
IExisting extends IExisting extends
| Constructor<InstanceType<AExisting>> | Constructor<ProviderInstanceType<AExisting>>
| AbstractConstructor<InstanceType<AExisting>>, | AbstractConstructor<ProviderInstanceType<AExisting>>,
>( >(
provider: provider:
| SafeClassProvider<AClass, IClass, DClass> | SafeClassProvider<AClass, IClass, DClass>
| SafeValueProvider<AValue, VValue> | SafeValueProvider<AValue, VValue>
| SafeFactoryProviderWithToken<AFactoryToken, IFactoryToken, DFactoryToken> | SafeFactoryProvider<AFactory, IFactory, DFactory>
| SafeFactoryProviderWithClass<AFactoryClass, IFactoryClass, DFactoryClass>
| SafeExistingProvider<AExisting, IExisting> | SafeExistingProvider<AExisting, IExisting>
| Constructor<unknown>, | Constructor<unknown>,
): SafeProvider => provider as SafeProvider; ): SafeProvider => provider as SafeProvider;

View File

@@ -1,5 +1,4 @@
import { LOCALE_ID, NgModule } from "@angular/core"; import { LOCALE_ID, NgModule } from "@angular/core";
import { UnwrapOpaque } from "type-fest";
import { import {
AuthRequestServiceAbstraction, AuthRequestServiceAbstraction,
@@ -267,7 +266,7 @@ import { ModalService } from "./modal.service";
* Add your provider definition here using the safeProvider function as a wrapper. This will give you type safety. * Add your provider definition here using the safeProvider function as a wrapper. This will give you type safety.
* If you need help please ask for it, do NOT change the type of this array. * If you need help please ask for it, do NOT change the type of this array.
*/ */
const typesafeProviders: Array<SafeProvider> = [ const safeProviders: SafeProvider[] = [
safeProvider(AuthGuard), safeProvider(AuthGuard),
safeProvider(UnauthGuard), safeProvider(UnauthGuard),
safeProvider(ModalService), safeProvider(ModalService),
@@ -1085,6 +1084,6 @@ function encryptServiceFactory(
@NgModule({ @NgModule({
declarations: [], declarations: [],
// Do not register your dependency here! Add it to the typesafeProviders array using the helper function // Do not register your dependency here! Add it to the typesafeProviders array using the helper function
providers: typesafeProviders as UnwrapOpaque<SafeProvider>[], providers: safeProviders,
}) })
export class JslibServicesModule {} export class JslibServicesModule {}