feat: start implementing multiple service factories

Co-authored-by: Patrik Oldsberg <poldsberg@gmail.com>
Signed-off-by: Camila Belo <camilaibs@gmail.com>
This commit is contained in:
Camila Belo
2024-07-08 16:08:04 +02:00
committed by Patrik Oldsberg
parent 88ed7f5926
commit 11aaaa496a
33 changed files with 563 additions and 250 deletions
@@ -10,6 +10,7 @@ import { ServiceFactoryCompat } from '@backstage/backend-plugin-api';
export const featureDiscoveryServiceFactory: ServiceFactoryCompat<
FeatureDiscoveryService,
'root',
true,
undefined
>;
+19 -2
View File
@@ -49,6 +49,7 @@ import { UserInfoService } from '@backstage/backend-plugin-api';
export const authServiceFactory: ServiceFactoryCompat<
AuthService,
'plugin',
true,
undefined
>;
@@ -80,6 +81,7 @@ export interface Backend {
export const cacheServiceFactory: ServiceFactoryCompat<
CacheService,
'plugin',
true,
undefined
>;
@@ -113,6 +115,7 @@ export interface CreateSpecializedBackendOptions {
export const databaseServiceFactory: ServiceFactoryCompat<
DatabaseService,
'plugin',
true,
undefined
>;
@@ -135,6 +138,7 @@ export type DefaultRootHttpRouterOptions = DefaultRootHttpRouterOptions_2;
export const discoveryServiceFactory: ServiceFactoryCompat<
DiscoveryService,
'plugin',
true,
undefined
>;
@@ -161,6 +165,7 @@ export class HostDiscovery implements DiscoveryService {
export const httpAuthServiceFactory: ServiceFactoryCompat<
HttpAuthService,
'plugin',
true,
undefined
>;
@@ -168,6 +173,7 @@ export const httpAuthServiceFactory: ServiceFactoryCompat<
export const httpRouterServiceFactory: ServiceFactoryCompat<
HttpRouterService,
'plugin',
true,
undefined
>;
@@ -191,6 +197,7 @@ export type IdentityFactoryOptions = {
export const identityServiceFactory: ServiceFactoryCompat<
IdentityService,
'plugin',
true,
IdentityFactoryOptions
>;
@@ -203,6 +210,7 @@ export type LifecycleMiddlewareOptions = LifecycleMiddlewareOptions_2;
export const lifecycleServiceFactory: ServiceFactoryCompat<
LifecycleService,
'plugin',
true,
undefined
>;
@@ -220,6 +228,7 @@ export function loadBackendConfig(options: {
export const loggerServiceFactory: ServiceFactoryCompat<
LoggerService,
'plugin',
true,
undefined
>;
@@ -248,6 +257,7 @@ export type MiddlewareFactoryOptions = MiddlewareFactoryOptions_2;
export const permissionsServiceFactory: ServiceFactoryCompat<
PermissionsService,
'plugin',
true,
undefined
>;
@@ -278,6 +288,7 @@ export interface RootConfigFactoryOptions {
export const rootConfigServiceFactory: ServiceFactoryCompat<
RootConfigService,
'root',
true,
RootConfigFactoryOptions
>;
@@ -294,13 +305,14 @@ export type RootHttpRouterFactoryOptions = RootHttpRouterFactoryOptions_2;
// @public @deprecated (undocumented)
export const rootHttpRouterServiceFactory: ((
options?: RootHttpRouterFactoryOptions_2 | undefined,
) => ServiceFactory<RootHttpRouterService, 'root'>) &
ServiceFactory<RootHttpRouterService, 'root'>;
) => ServiceFactory<RootHttpRouterService, 'root', true>) &
ServiceFactory<RootHttpRouterService, 'root', true>;
// @public @deprecated
export const rootLifecycleServiceFactory: ServiceFactoryCompat<
RootLifecycleService,
'root',
true,
undefined
>;
@@ -308,6 +320,7 @@ export const rootLifecycleServiceFactory: ServiceFactoryCompat<
export const rootLoggerServiceFactory: ServiceFactoryCompat<
RootLoggerService,
'root',
true,
undefined
>;
@@ -315,6 +328,7 @@ export const rootLoggerServiceFactory: ServiceFactoryCompat<
export const schedulerServiceFactory: ServiceFactoryCompat<
SchedulerService,
'plugin',
true,
undefined
>;
@@ -322,6 +336,7 @@ export const schedulerServiceFactory: ServiceFactoryCompat<
export const tokenManagerServiceFactory: ServiceFactoryCompat<
TokenManagerService,
'plugin',
true,
undefined
>;
@@ -329,6 +344,7 @@ export const tokenManagerServiceFactory: ServiceFactoryCompat<
export const urlReaderServiceFactory: ServiceFactoryCompat<
UrlReaderService,
'plugin',
true,
undefined
>;
@@ -336,6 +352,7 @@ export const urlReaderServiceFactory: ServiceFactoryCompat<
export const userInfoServiceFactory: ServiceFactoryCompat<
UserInfoService,
'plugin',
true,
undefined
>;
@@ -58,12 +58,24 @@ function createPluginMetadataServiceFactory(pluginId: string) {
export class ServiceRegistry {
static create(factories: Array<ServiceFactory>): ServiceRegistry {
const registry = new ServiceRegistry(factories);
const factoryMap = new Map<string, InternalServiceFactory[]>();
for (const factory of factories) {
if (factory.service.singleton) {
factoryMap.set(factory.service.id, [toInternalServiceFactory(factory)]);
} else {
const existing = factoryMap.get(factory.service.id) ?? [];
factoryMap.set(
factory.service.id,
existing.concat(toInternalServiceFactory(factory)),
);
}
}
const registry = new ServiceRegistry(factoryMap);
registry.checkForCircularDeps();
return registry;
}
readonly #providedFactories: Map<string, InternalServiceFactory>;
readonly #providedFactories: Map<string, InternalServiceFactory[]>;
readonly #loadedDefaultFactories: Map<
Function,
Promise<InternalServiceFactory>
@@ -82,10 +94,8 @@ export class ServiceRegistry {
readonly #addedFactoryIds = new Set<string>();
readonly #instantiatedFactories = new Set<string>();
private constructor(factories: Array<ServiceFactory>) {
this.#providedFactories = new Map(
factories.map(sf => [sf.service.id, toInternalServiceFactory(sf)]),
);
private constructor(factories: Map<string, InternalServiceFactory[]>) {
this.#providedFactories = factories;
this.#loadedDefaultFactories = new Map();
this.#implementations = new Map();
}
@@ -93,17 +103,17 @@ export class ServiceRegistry {
#resolveFactory(
ref: ServiceRef<unknown>,
pluginId: string,
): Promise<InternalServiceFactory> | undefined {
): Promise<InternalServiceFactory[]> | undefined {
// Special case handling of the plugin metadata service, generating a custom factory for it each time
if (ref.id === coreServices.pluginMetadata.id) {
return Promise.resolve(
return Promise.resolve([
toInternalServiceFactory(createPluginMetadataServiceFactory(pluginId)),
);
]);
}
let resolvedFactory:
| Promise<InternalServiceFactory>
| InternalServiceFactory
| Promise<InternalServiceFactory[]>
| InternalServiceFactory[]
| undefined = this.#providedFactories.get(ref.id);
const { __defaultFactory: defaultFactory } = ref as InternalServiceRef;
if (!resolvedFactory && !defaultFactory) {
@@ -120,15 +130,18 @@ export class ServiceRegistry {
);
this.#loadedDefaultFactories.set(defaultFactory!, loadedFactory);
}
resolvedFactory = loadedFactory.catch(error => {
throw new Error(
`Failed to instantiate service '${
ref.id
}' because the default factory loader threw an error, ${stringifyError(
error,
)}`,
);
});
resolvedFactory = loadedFactory.then(
factory => [factory],
error => {
throw new Error(
`Failed to instantiate service '${
ref.id
}' because the default factory loader threw an error, ${stringifyError(
error,
)}`,
);
},
);
}
return Promise.resolve(resolvedFactory);
@@ -142,6 +155,9 @@ export class ServiceRegistry {
if (this.#providedFactories.get(ref.id)) {
return false;
}
if (!ref.singleton) {
return false;
}
return !(ref as InternalServiceRef).__defaultFactory;
});
@@ -156,13 +172,13 @@ export class ServiceRegistry {
checkForCircularDeps(): void {
const graph = DependencyGraph.fromIterable(
Array.from(this.#providedFactories).map(
([serviceId, serviceFactory]) => ({
value: serviceId,
provides: [serviceId],
consumes: Object.values(serviceFactory.deps).map(d => d.id),
}),
),
Array.from(this.#providedFactories).map(([serviceId, factories]) => ({
value: serviceId,
provides: [serviceId],
consumes: factories.flatMap(factory =>
Object.values(factory.deps).map(d => d.id),
),
})),
);
const circularDependencies = Array.from(graph.detectCircularDependencies());
@@ -183,27 +199,36 @@ export class ServiceRegistry {
);
}
if (this.#addedFactoryIds.has(factoryId)) {
throw new Error(
`Duplicate service implementations provided for ${factoryId}`,
);
}
if (this.#instantiatedFactories.has(factoryId)) {
throw new Error(
`Unable to set service factory with id ${factoryId}, service has already been instantiated`,
);
}
this.#addedFactoryIds.add(factoryId);
this.#providedFactories.set(factoryId, toInternalServiceFactory(factory));
if (factory.service.singleton) {
if (this.#addedFactoryIds.has(factoryId)) {
throw new Error(
`Duplicate service implementations provided for ${factoryId}`,
);
}
this.#addedFactoryIds.add(factoryId);
this.#providedFactories.set(factoryId, [
toInternalServiceFactory(factory),
]);
} else {
const newFactories = (
this.#providedFactories.get(factoryId) ?? []
).concat(toInternalServiceFactory(factory));
this.#providedFactories.set(factoryId, newFactories);
}
}
async initializeEagerServicesWithScope(
scope: 'root' | 'plugin',
pluginId: string = 'root',
) {
for (const factory of this.#providedFactories.values()) {
for (const [factory] of this.#providedFactories.values()) {
if (factory.service.scope === scope) {
// Root-scoped services are eager by default, plugin-scoped are lazy by default
if (scope === 'root' && factory.initialization !== 'lazy') {
@@ -215,88 +240,112 @@ export class ServiceRegistry {
}
}
get<T>(ref: ServiceRef<T>, pluginId: string): Promise<T> | undefined {
get<T, TSingleton extends boolean>(
ref: ServiceRef<T, 'plugin' | 'root', TSingleton>,
pluginId: string,
): Promise<TSingleton extends true ? T : T[]> | undefined {
this.#instantiatedFactories.add(ref.id);
return this.#resolveFactory(ref, pluginId)?.then(factory => {
if (factory.service.scope === 'root') {
let existing = this.#rootServiceImplementations.get(factory);
if (!existing) {
this.#checkForMissingDeps(factory, pluginId);
const rootDeps = new Array<Promise<[name: string, impl: unknown]>>();
const resolvedFactory = this.#resolveFactory(ref, pluginId);
for (const [name, serviceRef] of Object.entries(factory.deps)) {
if (serviceRef.scope !== 'root') {
throw new Error(
`Failed to instantiate 'root' scoped service '${ref.id}' because it depends on '${serviceRef.scope}' scoped service '${serviceRef.id}'.`,
);
if (!resolvedFactory) {
return ref.singleton
? undefined
: (Promise.resolve([]) as
| Promise<TSingleton extends true ? T : T[]>
| undefined);
}
return resolvedFactory
.then(factories => {
return Promise.all(
factories.map(factory => {
if (factory.service.scope === 'root') {
let existing = this.#rootServiceImplementations.get(factory);
if (!existing) {
this.#checkForMissingDeps(factory, pluginId);
const rootDeps = new Array<
Promise<[name: string, impl: unknown]>
>();
for (const [name, serviceRef] of Object.entries(factory.deps)) {
if (serviceRef.scope !== 'root') {
throw new Error(
`Failed to instantiate 'root' scoped service '${ref.id}' because it depends on '${serviceRef.scope}' scoped service '${serviceRef.id}'.`,
);
}
const target = this.get(serviceRef, pluginId)!;
rootDeps.push(target.then(impl => [name, impl]));
}
existing = Promise.all(rootDeps).then(entries =>
factory.factory(Object.fromEntries(entries), undefined),
);
this.#rootServiceImplementations.set(factory, existing);
}
return existing as Promise<T>;
}
const target = this.get(serviceRef, pluginId)!;
rootDeps.push(target.then(impl => [name, impl]));
}
existing = Promise.all(rootDeps).then(entries =>
factory.factory(Object.fromEntries(entries), undefined),
);
this.#rootServiceImplementations.set(factory, existing);
}
return existing as Promise<T>;
}
let implementation = this.#implementations.get(factory);
if (!implementation) {
this.#checkForMissingDeps(factory, pluginId);
const rootDeps = new Array<
Promise<[name: string, impl: unknown]>
>();
let implementation = this.#implementations.get(factory);
if (!implementation) {
this.#checkForMissingDeps(factory, pluginId);
const rootDeps = new Array<Promise<[name: string, impl: unknown]>>();
for (const [name, serviceRef] of Object.entries(factory.deps)) {
if (serviceRef.scope === 'root') {
const target = this.get(serviceRef, pluginId)!;
rootDeps.push(target.then(impl => [name, impl]));
}
}
for (const [name, serviceRef] of Object.entries(factory.deps)) {
if (serviceRef.scope === 'root') {
const target = this.get(serviceRef, pluginId)!;
rootDeps.push(target.then(impl => [name, impl]));
}
}
implementation = {
context: Promise.all(rootDeps)
.then(entries =>
factory.createRootContext?.(Object.fromEntries(entries)),
)
.catch(error => {
const cause = stringifyError(error);
throw new Error(
`Failed to instantiate service '${ref.id}' because createRootContext threw an error, ${cause}`,
);
}),
byPlugin: new Map(),
};
implementation = {
context: Promise.all(rootDeps)
.then(entries =>
factory.createRootContext?.(Object.fromEntries(entries)),
)
.catch(error => {
const cause = stringifyError(error);
throw new Error(
`Failed to instantiate service '${ref.id}' because createRootContext threw an error, ${cause}`,
);
}),
byPlugin: new Map(),
};
this.#implementations.set(factory, implementation);
}
this.#implementations.set(factory, implementation);
}
let result = implementation.byPlugin.get(pluginId) as Promise<any>;
if (!result) {
const allDeps = new Array<
Promise<[name: string, impl: unknown]>
>();
let result = implementation.byPlugin.get(pluginId) as Promise<any>;
if (!result) {
const allDeps = new Array<Promise<[name: string, impl: unknown]>>();
for (const [name, serviceRef] of Object.entries(factory.deps)) {
const target = this.get(serviceRef, pluginId)!;
allDeps.push(target.then(impl => [name, impl]));
}
for (const [name, serviceRef] of Object.entries(factory.deps)) {
const target = this.get(serviceRef, pluginId)!;
allDeps.push(target.then(impl => [name, impl]));
}
result = implementation.context
.then(context =>
Promise.all(allDeps).then(entries =>
factory.factory(Object.fromEntries(entries), context),
),
)
.catch(error => {
const cause = stringifyError(error);
throw new Error(
`Failed to instantiate service '${ref.id}' for '${pluginId}' because the factory function threw an error, ${cause}`,
);
});
implementation.byPlugin.set(pluginId, result);
}
return result;
});
result = implementation.context
.then(context =>
Promise.all(allDeps).then(entries =>
factory.factory(Object.fromEntries(entries), context),
),
)
.catch(error => {
const cause = stringifyError(error);
throw new Error(
`Failed to instantiate service '${ref.id}' for '${pluginId}' because the factory function threw an error, ${cause}`,
);
});
implementation.byPlugin.set(pluginId, result);
}
return result;
}),
);
})
.then(results => (ref.singleton ? results[0] : results));
}
}
@@ -10,6 +10,7 @@ import { ServiceFactoryCompat } from '@backstage/backend-plugin-api';
export const authServiceFactory: ServiceFactoryCompat<
AuthService,
'plugin',
true,
undefined
>;
@@ -28,6 +28,7 @@ export type CacheManagerOptions = {
export const cacheServiceFactory: ServiceFactoryCompat<
CacheService,
'plugin',
true,
undefined
>;
@@ -35,6 +35,7 @@ export type DatabaseManagerOptions = {
export const databaseServiceFactory: ServiceFactoryCompat<
DatabaseService,
'plugin',
true,
undefined
>;
@@ -11,6 +11,7 @@ import { ServiceFactoryCompat } from '@backstage/backend-plugin-api';
export const discoveryServiceFactory: ServiceFactoryCompat<
DiscoveryService,
'plugin',
true,
undefined
>;
@@ -10,6 +10,7 @@ import { ServiceFactoryCompat } from '@backstage/backend-plugin-api';
export const httpAuthServiceFactory: ServiceFactoryCompat<
HttpAuthService,
'plugin',
true,
undefined
>;
@@ -18,6 +18,7 @@ export function createLifecycleMiddleware(
export const httpRouterServiceFactory: ServiceFactoryCompat<
HttpRouterService,
'plugin',
true,
undefined
>;
@@ -10,6 +10,7 @@ import { ServiceFactoryCompat } from '@backstage/backend-plugin-api';
export const lifecycleServiceFactory: ServiceFactoryCompat<
LifecycleService,
'plugin',
true,
undefined
>;
@@ -10,6 +10,7 @@ import { ServiceFactoryCompat } from '@backstage/backend-plugin-api';
export const loggerServiceFactory: ServiceFactoryCompat<
LoggerService,
'plugin',
true,
undefined
>;
@@ -10,6 +10,7 @@ import { ServiceFactoryCompat } from '@backstage/backend-plugin-api';
export const permissionsServiceFactory: ServiceFactoryCompat<
PermissionsService,
'plugin',
true,
undefined
>;
@@ -28,8 +28,8 @@ export interface RootConfigFactoryOptions {
// @public (undocumented)
export const rootConfigServiceFactory: ((
options?: RootConfigFactoryOptions,
) => ServiceFactory<RootConfigService, 'root'>) &
ServiceFactory<RootConfigService, 'root'>;
) => ServiceFactory<RootConfigService, 'root', true>) &
ServiceFactory<RootConfigService, 'root', true>;
// (No @packageDocumentation comment for this package)
```
@@ -10,6 +10,7 @@ import { ServiceFactoryCompat } from '@backstage/backend-plugin-api';
export const rootHealthServiceFactory: ServiceFactoryCompat<
RootHealthService,
'root',
true,
undefined
>;
@@ -143,8 +143,8 @@ export type RootHttpRouterFactoryOptions = {
// @public (undocumented)
export const rootHttpRouterServiceFactory: ((
options?: RootHttpRouterFactoryOptions,
) => ServiceFactory<RootHttpRouterService, 'root'>) &
ServiceFactory<RootHttpRouterService, 'root'>;
) => ServiceFactory<RootHttpRouterService, 'root', true>) &
ServiceFactory<RootHttpRouterService, 'root', true>;
// (No @packageDocumentation comment for this package)
```
@@ -10,6 +10,7 @@ import { ServiceFactoryCompat } from '@backstage/backend-plugin-api';
export const rootLifecycleServiceFactory: ServiceFactoryCompat<
RootLifecycleService,
'root',
true,
undefined
>;
@@ -14,6 +14,7 @@ import { transport } from 'winston';
export const rootLoggerServiceFactory: ServiceFactoryCompat<
RootLoggerService,
'root',
true,
undefined
>;
@@ -21,6 +21,7 @@ export class DefaultSchedulerService {
export const schedulerServiceFactory: ServiceFactoryCompat<
SchedulerService,
'plugin',
true,
undefined
>;
@@ -429,6 +429,7 @@ export class UrlReaders {
export const urlReaderServiceFactory: ServiceFactoryCompat<
UrlReaderService,
'plugin',
true,
undefined
>;
@@ -10,6 +10,7 @@ import { UserInfoService } from '@backstage/backend-plugin-api';
export const userInfoServiceFactory: ServiceFactoryCompat<
UserInfoService,
'plugin',
true,
undefined
>;
@@ -14,12 +14,37 @@
* limitations under the License.
*/
import { ReaderFactory } from './lib';
import { UrlReaders } from './lib/UrlReaders';
import {
coreServices,
createServiceFactory,
createServiceRef,
} from '@backstage/backend-plugin-api';
/**
* @public
* A non-singleton reference to URL Reader factory services.
*
* @example
* Creating a service factory implementation for a Custom URL Reader.
* ```ts
* createServiceFactory({
* service: urlReaderProviderFactoriesServiceRef,
* deps: {},
* async factory() {
* return CustomUrlReader.factory;
* },
* });
* ```
*/
export const urlReaderProviderFactoriesServiceRef =
createServiceRef<ReaderFactory>({
id: 'core.urlReader.factories',
scope: 'plugin',
singleton: false,
});
/**
* Reading content from external systems.
*
@@ -34,11 +59,13 @@ export const urlReaderServiceFactory = createServiceFactory({
deps: {
config: coreServices.rootConfig,
logger: coreServices.logger,
factories: urlReaderProviderFactoriesServiceRef,
},
async factory({ config, logger }) {
async factory({ config, logger, factories }) {
return UrlReaders.default({
config,
logger,
factories,
});
},
});
@@ -117,6 +117,7 @@ export interface DynamicPluginsFactoryOptions {
export const dynamicPluginsFeatureDiscoveryServiceFactory: ServiceFactoryCompat<
FeatureDiscoveryService,
'root',
true,
undefined
>;
@@ -127,6 +128,7 @@ export const dynamicPluginsFrontendSchemas: BackendFeatureCompat;
export const dynamicPluginsRootLoggerServiceFactory: ServiceFactoryCompat<
RootLoggerService,
'root',
true,
undefined
>;
@@ -147,6 +149,7 @@ export interface DynamicPluginsSchemasService {
export const dynamicPluginsSchemasServiceFactory: ServiceFactoryCompat<
DynamicPluginsSchemasService,
'root',
true,
DynamicPluginsSchemasOptions
>;
@@ -154,13 +157,15 @@ export const dynamicPluginsSchemasServiceFactory: ServiceFactoryCompat<
export const dynamicPluginsServiceFactory: ServiceFactoryCompat<
DynamicPluginProvider,
'root',
true,
DynamicPluginsFactoryOptions
>;
// @public (undocumented)
export const dynamicPluginsServiceRef: ServiceRef<
DynamicPluginProvider,
'root'
'root',
true
>;
// @public (undocumented)
@@ -17,7 +17,8 @@ export interface FeatureDiscoveryService {
// @alpha
export const featureDiscoveryServiceRef: ServiceRef<
FeatureDiscoveryService,
'root'
'root',
true
>;
// (No @packageDocumentation comment for this package)
+83 -42
View File
@@ -189,28 +189,28 @@ export type CacheServiceSetOptions = {
// @public
export namespace coreServices {
const auth: ServiceRef<AuthService, 'plugin'>;
const userInfo: ServiceRef<UserInfoService, 'plugin'>;
const cache: ServiceRef<CacheService, 'plugin'>;
const rootConfig: ServiceRef<RootConfigService, 'root'>;
const database: ServiceRef<DatabaseService, 'plugin'>;
const discovery: ServiceRef<DiscoveryService, 'plugin'>;
const rootHealth: ServiceRef<RootHealthService, 'root'>;
const httpAuth: ServiceRef<HttpAuthService, 'plugin'>;
const httpRouter: ServiceRef<HttpRouterService, 'plugin'>;
const lifecycle: ServiceRef<LifecycleService, 'plugin'>;
const logger: ServiceRef<LoggerService, 'plugin'>;
const permissions: ServiceRef<PermissionsService, 'plugin'>;
const pluginMetadata: ServiceRef<PluginMetadataService, 'plugin'>;
const rootHttpRouter: ServiceRef<RootHttpRouterService, 'root'>;
const rootLifecycle: ServiceRef<RootLifecycleService, 'root'>;
const rootLogger: ServiceRef<RootLoggerService, 'root'>;
const scheduler: ServiceRef<SchedulerService, 'plugin'>;
const auth: ServiceRef<AuthService, 'plugin', true>;
const userInfo: ServiceRef<UserInfoService, 'plugin', true>;
const cache: ServiceRef<CacheService, 'plugin', true>;
const rootConfig: ServiceRef<RootConfigService, 'root', true>;
const database: ServiceRef<DatabaseService, 'plugin', true>;
const discovery: ServiceRef<DiscoveryService, 'plugin', true>;
const rootHealth: ServiceRef<RootHealthService, 'root', true>;
const httpAuth: ServiceRef<HttpAuthService, 'plugin', true>;
const httpRouter: ServiceRef<HttpRouterService, 'plugin', true>;
const lifecycle: ServiceRef<LifecycleService, 'plugin', true>;
const logger: ServiceRef<LoggerService, 'plugin', true>;
const permissions: ServiceRef<PermissionsService, 'plugin', true>;
const pluginMetadata: ServiceRef<PluginMetadataService, 'plugin', true>;
const rootHttpRouter: ServiceRef<RootHttpRouterService, 'root', true>;
const rootLifecycle: ServiceRef<RootLifecycleService, 'root', true>;
const rootLogger: ServiceRef<RootLoggerService, 'root', true>;
const scheduler: ServiceRef<SchedulerService, 'plugin', true>;
const // @deprecated
tokenManager: ServiceRef<TokenManagerService, 'plugin'>;
const urlReader: ServiceRef<UrlReaderService, 'plugin'>;
tokenManager: ServiceRef<TokenManagerService, 'plugin', true>;
const urlReader: ServiceRef<UrlReaderService, 'plugin', true>;
const // @deprecated
identity: ServiceRef<IdentityService, 'plugin'>;
identity: ServiceRef<IdentityService, 'plugin', true>;
}
// @public
@@ -251,18 +251,20 @@ export interface CreateExtensionPointOptions {
// @public
export function createServiceFactory<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends {
[name in string]: ServiceRef<unknown, 'root'>;
},
TOpts extends object | undefined = undefined,
>(
options: RootServiceFactoryOptions<TService, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root'>;
options: RootServiceFactoryOptions<TService, TSingleton, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TSingleton>;
// @public @deprecated
export function createServiceFactory<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends {
[name in string]: ServiceRef<unknown, 'root'>;
@@ -271,12 +273,13 @@ export function createServiceFactory<
>(
options: (
options?: TOpts,
) => RootServiceFactoryOptions<TService, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TOpts>;
) => RootServiceFactoryOptions<TService, TSingleton, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TSingleton, TOpts>;
// @public
export function createServiceFactory<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends {
[name in string]: ServiceRef<unknown>;
@@ -284,12 +287,19 @@ export function createServiceFactory<
TContext = undefined,
TOpts extends object | undefined = undefined,
>(
options: PluginServiceFactoryOptions<TService, TContext, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'plugin'>;
options: PluginServiceFactoryOptions<
TService,
TSingleton,
TContext,
TImpl,
TDeps
>,
): ServiceFactoryCompat<TService, 'plugin', TSingleton>;
// @public @deprecated
export function createServiceFactory<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends {
[name in string]: ServiceRef<unknown>;
@@ -299,18 +309,34 @@ export function createServiceFactory<
>(
options: (
options?: TOpts,
) => PluginServiceFactoryOptions<TService, TContext, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'plugin', TOpts>;
) => PluginServiceFactoryOptions<
TService,
TSingleton,
TContext,
TImpl,
TDeps
>,
): ServiceFactoryCompat<TService, 'plugin', TSingleton, TOpts>;
// @public
export function createServiceRef<TService>(
options: ServiceRefOptions<TService, 'plugin'>,
): ServiceRef<TService, 'plugin'>;
options: ServiceRefOptions<TService, 'plugin', true>,
): ServiceRef<TService, 'plugin', true>;
// @public
export function createServiceRef<TService>(
options: ServiceRefOptions<TService, 'root'>,
): ServiceRef<TService, 'root'>;
options: ServiceRefOptions<TService, 'root', true>,
): ServiceRef<TService, 'root', true>;
// @public
export function createServiceRef<TService>(
options: ServiceRefOptions<TService, 'plugin', false>,
): ServiceRef<TService, 'plugin', false>;
// @public
export function createServiceRef<TService>(
options: ServiceRefOptions<TService, 'root', false>,
): ServiceRef<TService, 'root', false>;
// @public
export interface DatabaseService {
@@ -447,16 +473,18 @@ export interface PluginMetadataService {
// @public @deprecated (undocumented)
export type PluginServiceFactoryConfig<
TService,
TSingleton extends boolean,
TContext,
TImpl extends TService,
TDeps extends {
[name in string]: ServiceRef<unknown>;
},
> = PluginServiceFactoryOptions<TService, TContext, TImpl, TDeps>;
> = PluginServiceFactoryOptions<TService, TSingleton, TContext, TImpl, TDeps>;
// @public (undocumented)
export interface PluginServiceFactoryOptions<
TService,
TSingleton extends boolean,
TContext,
TImpl extends TService,
TDeps extends {
@@ -476,7 +504,7 @@ export interface PluginServiceFactoryOptions<
): TImpl | Promise<TImpl>;
initialization?: 'always' | 'lazy';
// (undocumented)
service: ServiceRef<TService, 'plugin'>;
service: ServiceRef<TService, 'plugin', TSingleton>;
}
// @public
@@ -538,15 +566,17 @@ export interface RootLoggerService extends LoggerService {}
// @public @deprecated (undocumented)
export type RootServiceFactoryConfig<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends {
[name in string]: ServiceRef<unknown>;
},
> = RootServiceFactoryOptions<TService, TImpl, TDeps>;
> = RootServiceFactoryOptions<TService, TSingleton, TImpl, TDeps>;
// @public (undocumented)
export interface RootServiceFactoryOptions<
TService,
TService, // TODO(Rugvip): Can we forward the entire service ref type here instead of forwarding each type arg once the callback form is gone?
TSingleton extends boolean,
TImpl extends TService,
TDeps extends {
[name in string]: ServiceRef<unknown>;
@@ -558,7 +588,7 @@ export interface RootServiceFactoryOptions<
factory(deps: ServiceRefsToInstances<TDeps, 'root'>): TImpl | Promise<TImpl>;
initialization?: 'always' | 'lazy';
// (undocumented)
service: ServiceRef<TService, 'root'>;
service: ServiceRef<TService, 'root', TSingleton>;
}
// @public
@@ -639,21 +669,23 @@ export type SearchResponseFile = UrlReaderServiceSearchResponseFile;
export interface ServiceFactory<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
TSingleton extends boolean = boolean,
> extends BackendFeature {
// (undocumented)
service: ServiceRef<TService, TScope>;
service: ServiceRef<TService, TScope, TSingleton>;
}
// @public @deprecated (undocumented)
export interface ServiceFactoryCompat<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
TSingleton extends boolean = boolean,
TOpts extends object | undefined = undefined,
> extends ServiceFactory<TService, TScope> {
> extends ServiceFactory<TService, TScope, TSingleton> {
// @deprecated (undocumented)
(
...options: undefined extends TOpts ? [] : [options?: TOpts]
): ServiceFactory<TService, TScope>;
): ServiceFactory<TService, TScope, TSingleton>;
}
// @public @deprecated
@@ -663,9 +695,11 @@ export type ServiceFactoryOrFunction = ServiceFactory | (() => ServiceFactory);
export type ServiceRef<
TService,
TScope extends 'root' | 'plugin' = 'root' | 'plugin',
TSingleton extends boolean = boolean,
> = {
id: string;
scope: TScope;
singleton: TSingleton;
T: TService;
$$type: '@backstage/ServiceRef';
};
@@ -674,10 +708,15 @@ export type ServiceRef<
export type ServiceRefConfig<
TService,
TScope extends 'root' | 'plugin',
> = ServiceRefOptions<TService, TScope>;
TSingleton extends boolean,
> = ServiceRefOptions<TService, TScope, TSingleton>;
// @public (undocumented)
export interface ServiceRefOptions<TService, TScope extends 'root' | 'plugin'> {
export interface ServiceRefOptions<
TService,
TScope extends 'root' | 'plugin',
TSingleton extends boolean,
> {
// (undocumented)
defaultFactory?(
service: ServiceRef<TService, TScope>,
@@ -690,6 +729,8 @@ export interface ServiceRefOptions<TService, TScope extends 'root' | 'plugin'> {
id: string;
// (undocumented)
scope?: TScope;
// (undocumented)
singleton?: TSingleton;
}
// @public @deprecated
@@ -28,7 +28,8 @@ import {
export type ServiceRefConfig<
TService,
TScope extends 'root' | 'plugin',
> = ServiceRefOptions<TService, TScope>;
TSingleton extends boolean,
> = ServiceRefOptions<TService, TScope, TSingleton>;
/**
* @public
@@ -36,9 +37,10 @@ export type ServiceRefConfig<
*/
export type RootServiceFactoryConfig<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
> = RootServiceFactoryOptions<TService, TImpl, TDeps>;
> = RootServiceFactoryOptions<TService, TSingleton, TImpl, TDeps>;
/**
* @public
@@ -46,7 +48,8 @@ export type RootServiceFactoryConfig<
*/
export type PluginServiceFactoryConfig<
TService,
TSingleton extends boolean,
TContext,
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
> = PluginServiceFactoryOptions<TService, TContext, TImpl, TDeps>;
> = PluginServiceFactoryOptions<TService, TSingleton, TContext, TImpl, TDeps>;
@@ -24,6 +24,7 @@ import { BackendFeature } from '../../types';
export type ServiceRef<
TService,
TScope extends 'root' | 'plugin' = 'root' | 'plugin',
TSingleton extends boolean = boolean,
> = {
id: string;
@@ -38,6 +39,8 @@ export type ServiceRef<
*/
scope: TScope;
singleton: TSingleton;
/**
* Utility for getting the type of the service, using `typeof serviceRef.T`.
* Attempting to actually read this value will result in an exception.
@@ -51,8 +54,9 @@ export type ServiceRef<
export interface ServiceFactory<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
TSingleton extends boolean = boolean,
> extends BackendFeature {
service: ServiceRef<TService, TScope>;
service: ServiceRef<TService, TScope, TSingleton>;
}
/**
@@ -62,21 +66,23 @@ export interface ServiceFactory<
export interface ServiceFactoryCompat<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
TSingleton extends boolean = boolean,
TOpts extends object | undefined = undefined,
> extends ServiceFactory<TService, TScope> {
> extends ServiceFactory<TService, TScope, TSingleton> {
/**
* @deprecated Callable service factories will be removed in a future release, please re-implement the service factory using the available APIs instead. If no options are being passed, you can simply remove the trailing `()`.
*/
(
...options: undefined extends TOpts ? [] : [options?: TOpts]
): ServiceFactory<TService, TScope>;
): ServiceFactory<TService, TScope, TSingleton>;
}
/** @internal */
export interface InternalServiceFactory<
TService = unknown,
TScope extends 'plugin' | 'root' = 'plugin' | 'root',
> extends ServiceFactory<TService, TScope> {
TSingleton extends boolean = boolean,
> extends ServiceFactory<TService, TScope, TSingleton> {
version: 'v1';
initialization?: 'always' | 'lazy';
deps: { [key in string]: ServiceRef<unknown> };
@@ -96,9 +102,14 @@ export interface InternalServiceFactory<
export type ServiceFactoryOrFunction = ServiceFactory | (() => ServiceFactory);
/** @public */
export interface ServiceRefOptions<TService, TScope extends 'root' | 'plugin'> {
export interface ServiceRefOptions<
TService,
TScope extends 'root' | 'plugin',
TSingleton extends boolean,
> {
id: string;
scope?: TScope;
singleton?: TSingleton;
defaultFactory?(
service: ServiceRef<TService, TScope>,
): Promise<ServiceFactory>;
@@ -116,8 +127,8 @@ export interface ServiceRefOptions<TService, TScope extends 'root' | 'plugin'> {
* @public
*/
export function createServiceRef<TService>(
options: ServiceRefOptions<TService, 'plugin'>,
): ServiceRef<TService, 'plugin'>;
options: ServiceRefOptions<TService, 'plugin', true>,
): ServiceRef<TService, 'plugin', true>;
/**
* Creates a new service definition. This overload is used to create root scoped services.
@@ -125,16 +136,34 @@ export function createServiceRef<TService>(
* @public
*/
export function createServiceRef<TService>(
options: ServiceRefOptions<TService, 'root'>,
): ServiceRef<TService, 'root'>;
options: ServiceRefOptions<TService, 'root', true>,
): ServiceRef<TService, 'root', true>;
/**
* Creates a new service definition. This overload is used to create plugin scoped services.
*
* @public
*/
export function createServiceRef<TService>(
options: ServiceRefOptions<TService, any>,
): ServiceRef<TService, any> {
const { id, scope = 'plugin', defaultFactory } = options;
options: ServiceRefOptions<TService, 'plugin', false>,
): ServiceRef<TService, 'plugin', false>;
/**
* Creates a new service definition. This overload is used to create root scoped services.
*
* @public
*/
export function createServiceRef<TService>(
options: ServiceRefOptions<TService, 'root', false>,
): ServiceRef<TService, 'root', false>;
export function createServiceRef<TService, TSingleton extends boolean>(
options: ServiceRefOptions<TService, any, TSingleton>,
): ServiceRef<TService, any, TSingleton> {
const { id, scope = 'plugin', singleton = true, defaultFactory } = options;
return {
id,
scope,
singleton,
get T(): TService {
throw new Error(`tried to read ServiceRef.T of ${this}`);
},
@@ -143,7 +172,7 @@ export function createServiceRef<TService>(
},
$$type: '@backstage/ServiceRef',
__defaultFactory: defaultFactory,
} as ServiceRef<TService, typeof scope> & {
} as ServiceRef<TService, typeof scope, TSingleton> & {
__defaultFactory?: (
service: ServiceRef<TService>,
) => Promise<ServiceFactory<TService> | (() => ServiceFactory<TService>)>;
@@ -155,12 +184,17 @@ type ServiceRefsToInstances<
T extends { [key in string]: ServiceRef<unknown> },
TScope extends 'root' | 'plugin' = 'root' | 'plugin',
> = {
[key in keyof T as T[key]['scope'] extends TScope ? key : never]: T[key]['T'];
[key in keyof T as T[key]['scope'] extends TScope
? key
: never]: T[key]['singleton'] extends true
? T[key]['T']
: Array<T[key]['T']>;
};
/** @public */
export interface RootServiceFactoryOptions<
TService,
TService, // TODO(Rugvip): Can we forward the entire service ref type here instead of forwarding each type arg once the callback form is gone?
TSingleton extends boolean,
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
> {
@@ -175,7 +209,7 @@ export interface RootServiceFactoryOptions<
* Service factories for root scoped services use `always` as the default, while plugin scoped services use `lazy`.
*/
initialization?: 'always' | 'lazy';
service: ServiceRef<TService, 'root'>;
service: ServiceRef<TService, 'root', TSingleton>;
deps: TDeps;
factory(deps: ServiceRefsToInstances<TDeps, 'root'>): TImpl | Promise<TImpl>;
}
@@ -183,6 +217,7 @@ export interface RootServiceFactoryOptions<
/** @public */
export interface PluginServiceFactoryOptions<
TService,
TSingleton extends boolean,
TContext,
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
@@ -198,7 +233,7 @@ export interface PluginServiceFactoryOptions<
* Service factories for root scoped services use `always` as the default, while plugin scoped services use `lazy`.
*/
initialization?: 'always' | 'lazy';
service: ServiceRef<TService, 'plugin'>;
service: ServiceRef<TService, 'plugin', TSingleton>;
deps: TDeps;
createRootContext?(
deps: ServiceRefsToInstances<TDeps, 'root'>,
@@ -217,12 +252,13 @@ export interface PluginServiceFactoryOptions<
*/
export function createServiceFactory<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown, 'root'> },
TOpts extends object | undefined = undefined,
>(
options: RootServiceFactoryOptions<TService, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root'>;
options: RootServiceFactoryOptions<TService, TSingleton, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TSingleton>;
/**
* Creates a root scoped service factory with optional options.
*
@@ -235,14 +271,15 @@ export function createServiceFactory<
*/
export function createServiceFactory<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown, 'root'> },
TOpts extends object | undefined = undefined,
>(
options: (
options?: TOpts,
) => RootServiceFactoryOptions<TService, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TOpts>;
) => RootServiceFactoryOptions<TService, TSingleton, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'root', TSingleton, TOpts>;
/**
* Creates a plugin scoped service factory without options.
*
@@ -251,13 +288,20 @@ export function createServiceFactory<
*/
export function createServiceFactory<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
TContext = undefined,
TOpts extends object | undefined = undefined,
>(
options: PluginServiceFactoryOptions<TService, TContext, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'plugin'>;
options: PluginServiceFactoryOptions<
TService,
TSingleton,
TContext,
TImpl,
TDeps
>,
): ServiceFactoryCompat<TService, 'plugin', TSingleton>;
/**
* Creates a plugin scoped service factory with optional options.
*
@@ -270,6 +314,7 @@ export function createServiceFactory<
*/
export function createServiceFactory<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
TContext = undefined,
@@ -277,25 +322,46 @@ export function createServiceFactory<
>(
options: (
options?: TOpts,
) => PluginServiceFactoryOptions<TService, TContext, TImpl, TDeps>,
): ServiceFactoryCompat<TService, 'plugin', TOpts>;
) => PluginServiceFactoryOptions<
TService,
TSingleton,
TContext,
TImpl,
TDeps
>,
): ServiceFactoryCompat<TService, 'plugin', TSingleton, TOpts>;
export function createServiceFactory<
TService,
TSingleton extends boolean,
TImpl extends TService,
TDeps extends { [name in string]: ServiceRef<unknown> },
TContext,
TOpts extends object | undefined = undefined,
>(
options:
| RootServiceFactoryOptions<TService, TImpl, TDeps>
| PluginServiceFactoryOptions<TService, TContext, TImpl, TDeps>
| ((options: TOpts) => RootServiceFactoryOptions<TService, TImpl, TDeps>)
| RootServiceFactoryOptions<TService, TSingleton, TImpl, TDeps>
| PluginServiceFactoryOptions<TService, TSingleton, TContext, TImpl, TDeps>
| ((
options: TOpts,
) => PluginServiceFactoryOptions<TService, TContext, TImpl, TDeps>)
| (() => RootServiceFactoryOptions<TService, TImpl, TDeps>)
| (() => PluginServiceFactoryOptions<TService, TContext, TImpl, TDeps>),
): ServiceFactoryCompat<TService, 'root' | 'plugin', TOpts> {
) => RootServiceFactoryOptions<TService, TSingleton, TImpl, TDeps>)
| ((
options: TOpts,
) => PluginServiceFactoryOptions<
TService,
TSingleton,
TContext,
TImpl,
TDeps
>)
| (() => RootServiceFactoryOptions<TService, TSingleton, TImpl, TDeps>)
| (() => PluginServiceFactoryOptions<
TService,
TSingleton,
TContext,
TImpl,
TDeps
>),
): ServiceFactoryCompat<TService, 'root' | 'plugin', boolean, TOpts> {
const configCallback =
typeof options === 'function' ? options : () => options;
const factory = (
@@ -303,18 +369,25 @@ export function createServiceFactory<
): InternalServiceFactory<TService, 'plugin' | 'root'> => {
const anyConf = configCallback(o!);
if (anyConf.service.scope === 'root') {
const c = anyConf as RootServiceFactoryOptions<TService, TImpl, TDeps>;
const c = anyConf as RootServiceFactoryOptions<
TService,
TSingleton,
TImpl,
TDeps
>;
return {
$$type: '@backstage/BackendFeature',
version: 'v1',
service: c.service,
initialization: c.initialization,
deps: c.deps,
factory: async (deps: TDeps) => c.factory(deps),
factory: async (deps: ServiceRefsToInstances<TDeps, 'root'>) =>
c.factory(deps),
};
}
const c = anyConf as PluginServiceFactoryOptions<
TService,
TSingleton,
TContext,
TImpl,
TDeps
@@ -326,12 +399,14 @@ export function createServiceFactory<
initialization: c.initialization,
...('createRootContext' in c
? {
createRootContext: async (deps: TDeps) =>
c?.createRootContext?.(deps),
createRootContext: async (
deps: ServiceRefsToInstances<TDeps, 'root'>,
) => c?.createRootContext?.(deps),
}
: {}),
deps: c.deps,
factory: async (deps: TDeps, ctx: TContext) => c.factory(deps, ctx),
factory: async (deps: ServiceRefsToInstances<TDeps>, ctx: TContext) =>
c.factory(deps, ctx),
};
};
+90 -33
View File
@@ -156,7 +156,7 @@ export namespace mockServices {
// (undocumented)
export namespace auth {
const // (undocumented)
factory: ServiceFactoryCompat<AuthService, 'plugin', undefined>;
factory: ServiceFactoryCompat<AuthService, 'plugin', true, undefined>;
const // (undocumented)
mock: (
partialImpl?: Partial<AuthService> | undefined,
@@ -165,7 +165,7 @@ export namespace mockServices {
// (undocumented)
export namespace cache {
const // (undocumented)
factory: ServiceFactoryCompat<CacheService, 'plugin', undefined>;
factory: ServiceFactoryCompat<CacheService, 'plugin', true, undefined>;
const // (undocumented)
mock: (
partialImpl?: Partial<CacheService> | undefined,
@@ -174,7 +174,7 @@ export namespace mockServices {
// (undocumented)
export namespace database {
const // (undocumented)
factory: ServiceFactoryCompat<DatabaseService, 'plugin', undefined>;
factory: ServiceFactoryCompat<DatabaseService, 'plugin', true, undefined>;
const // (undocumented)
mock: (
partialImpl?: Partial<DatabaseService> | undefined,
@@ -185,7 +185,12 @@ export namespace mockServices {
// (undocumented)
export namespace discovery {
const // (undocumented)
factory: ServiceFactoryCompat<DiscoveryService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
DiscoveryService,
'plugin',
true,
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<DiscoveryService> | undefined,
@@ -194,7 +199,7 @@ export namespace mockServices {
// (undocumented)
export namespace events {
const // (undocumented)
factory: ServiceFactoryCompat<EventsService, 'plugin', undefined>;
factory: ServiceFactoryCompat<EventsService, 'plugin', true, undefined>;
const // (undocumented)
mock: (
partialImpl?: Partial<EventsService> | undefined,
@@ -208,8 +213,8 @@ export namespace mockServices {
export namespace httpAuth {
const factory: ((options?: {
defaultCredentials?: BackstageCredentials;
}) => ServiceFactory<HttpAuthService, 'plugin'>) &
ServiceFactory<HttpAuthService, 'plugin'>;
}) => ServiceFactory<HttpAuthService, 'plugin', true>) &
ServiceFactory<HttpAuthService, 'plugin', true>;
const // (undocumented)
mock: (
partialImpl?: Partial<HttpAuthService> | undefined,
@@ -218,7 +223,12 @@ export namespace mockServices {
// (undocumented)
export namespace httpRouter {
const // (undocumented)
factory: ServiceFactoryCompat<HttpRouterService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
HttpRouterService,
'plugin',
true,
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<HttpRouterService> | undefined,
@@ -229,7 +239,7 @@ export namespace mockServices {
// (undocumented)
export namespace identity {
const // (undocumented)
factory: ServiceFactoryCompat<IdentityService, 'plugin', undefined>;
factory: ServiceFactoryCompat<IdentityService, 'plugin', true, undefined>;
const // (undocumented)
mock: (
partialImpl?: Partial<IdentityService> | undefined,
@@ -238,7 +248,12 @@ export namespace mockServices {
// (undocumented)
export namespace lifecycle {
const // (undocumented)
factory: ServiceFactoryCompat<LifecycleService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
LifecycleService,
'plugin',
true,
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<LifecycleService> | undefined,
@@ -247,7 +262,7 @@ export namespace mockServices {
// (undocumented)
export namespace logger {
const // (undocumented)
factory: ServiceFactoryCompat<LoggerService, 'plugin', undefined>;
factory: ServiceFactoryCompat<LoggerService, 'plugin', true, undefined>;
const // (undocumented)
mock: (
partialImpl?: Partial<LoggerService> | undefined,
@@ -256,7 +271,12 @@ export namespace mockServices {
// (undocumented)
export namespace permissions {
const // (undocumented)
factory: ServiceFactoryCompat<PermissionsService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
PermissionsService,
'plugin',
true,
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<PermissionsService> | undefined,
@@ -271,15 +291,15 @@ export namespace mockServices {
data?: JsonObject;
};
const // (undocumented)
factory: ServiceFactory<RootConfigService, 'root'> &
factory: ServiceFactory<RootConfigService, 'root', boolean> &
((
options?: Options | undefined,
) => ServiceFactory<RootConfigService, 'root'>);
) => ServiceFactory<RootConfigService, 'root', boolean>);
}
// (undocumented)
export namespace rootHealth {
const // (undocumented)
factory: ServiceFactoryCompat<RootHealthService, 'root', undefined>;
factory: ServiceFactoryCompat<RootHealthService, 'root', true, undefined>;
const // (undocumented)
mock: (
partialImpl?: Partial<RootHealthService> | undefined,
@@ -290,8 +310,8 @@ export namespace mockServices {
const // (undocumented)
factory: ((
options?: RootHttpRouterFactoryOptions | undefined,
) => ServiceFactory<RootHttpRouterService, 'root'>) &
ServiceFactory<RootHttpRouterService, 'root'>;
) => ServiceFactory<RootHttpRouterService, 'root', true>) &
ServiceFactory<RootHttpRouterService, 'root', true>;
const // (undocumented)
mock: (
partialImpl?: Partial<RootHttpRouterService> | undefined,
@@ -300,7 +320,12 @@ export namespace mockServices {
// (undocumented)
export namespace rootLifecycle {
const // (undocumented)
factory: ServiceFactoryCompat<RootLifecycleService, 'root', undefined>;
factory: ServiceFactoryCompat<
RootLifecycleService,
'root',
true,
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<RootLifecycleService> | undefined,
@@ -315,10 +340,10 @@ export namespace mockServices {
level?: 'none' | 'error' | 'warn' | 'info' | 'debug';
};
const // (undocumented)
factory: ServiceFactory<LoggerService, 'root'> &
factory: ServiceFactory<LoggerService, 'root', boolean> &
((
options?: Options | undefined,
) => ServiceFactory<LoggerService, 'root'>);
) => ServiceFactory<LoggerService, 'root', boolean>);
const // (undocumented)
mock: (
partialImpl?: Partial<RootLoggerService> | undefined,
@@ -327,7 +352,12 @@ export namespace mockServices {
// (undocumented)
export namespace scheduler {
const // (undocumented)
factory: ServiceFactoryCompat<SchedulerService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
SchedulerService,
'plugin',
true,
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<SchedulerService> | undefined,
@@ -338,7 +368,12 @@ export namespace mockServices {
// (undocumented)
export namespace tokenManager {
const // (undocumented)
factory: ServiceFactoryCompat<TokenManagerService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
TokenManagerService,
'plugin',
true,
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<TokenManagerService> | undefined,
@@ -347,7 +382,12 @@ export namespace mockServices {
// (undocumented)
export namespace urlReader {
const // (undocumented)
factory: ServiceFactoryCompat<UrlReaderService, 'plugin', undefined>;
factory: ServiceFactoryCompat<
UrlReaderService,
'plugin',
true,
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<UrlReaderService> | undefined,
@@ -358,7 +398,12 @@ export namespace mockServices {
): UserInfoService;
// (undocumented)
export namespace userInfo {
const factory: ServiceFactoryCompat<UserInfoService, 'plugin', undefined>;
const factory: ServiceFactoryCompat<
UserInfoService,
'plugin',
true,
undefined
>;
const // (undocumented)
mock: (
partialImpl?: Partial<UserInfoService> | undefined,
@@ -374,22 +419,34 @@ export function registerMswTestHooks(worker: {
}): void;
// @public
export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
static from<TService, TScope extends 'root' | 'plugin'>(
subject: ServiceFactory<TService, TScope>,
export class ServiceFactoryTester<
TService,
TScope extends 'root' | 'plugin',
TSingleton extends boolean = true,
> {
static from<
TService,
TScope extends 'root' | 'plugin',
TSingleton extends boolean = true,
>(
subject: ServiceFactory<TService, TScope, TSingleton>,
options?: ServiceFactoryTesterOptions,
): ServiceFactoryTester<TService, TScope>;
): ServiceFactoryTester<TService, TScope, TSingleton>;
// @deprecated
get(
...args: 'root' extends TScope ? [] : [pluginId?: string]
): Promise<TService>;
getService<TGetService, TGetScope extends 'root' | 'plugin'>(
service: ServiceRef<TGetService, TGetScope>,
): Promise<TSingleton extends true ? TService : TService[]>;
getService<
TGetService,
TGetScope extends 'root' | 'plugin',
TGetSingleton extends boolean,
>(
service: ServiceRef<TGetService, TGetScope, TGetSingleton>,
...args: 'root' extends TGetScope ? [] : [pluginId?: string]
): Promise<TGetService>;
): Promise<TGetSingleton extends true ? TGetService : TGetService[]>;
getSubject(
...args: 'root' extends TScope ? [] : [pluginId?: string]
): Promise<TService>;
): Promise<TSingleton extends true ? TService : TService[]>;
}
// @public
@@ -43,8 +43,12 @@ export interface ServiceFactoryTesterOptions {
*
* @public
*/
export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
readonly #subject: ServiceRef<TService, TScope>;
export class ServiceFactoryTester<
TService,
TScope extends 'root' | 'plugin',
TSingleton extends boolean = true,
> {
readonly #subject: ServiceRef<TService, TScope, TSingleton>;
readonly #registry: ServiceRegistry;
/**
@@ -54,10 +58,14 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
* @param options - Additional options
* @returns A new tester instance for the provided subject.
*/
static from<TService, TScope extends 'root' | 'plugin'>(
subject: ServiceFactory<TService, TScope>,
static from<
TService,
TScope extends 'root' | 'plugin',
TSingleton extends boolean = true,
>(
subject: ServiceFactory<TService, TScope, TSingleton>,
options?: ServiceFactoryTesterOptions,
) {
): ServiceFactoryTester<TService, TScope, TSingleton> {
const registry = ServiceRegistry.create([
...defaultServiceFactories,
...(options?.dependencies ?? []),
@@ -67,7 +75,7 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
}
private constructor(
subject: ServiceRef<TService, TScope>,
subject: ServiceRef<TService, TScope, TSingleton>,
registry: ServiceRegistry,
) {
this.#subject = subject;
@@ -81,7 +89,7 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
*/
async get(
...args: 'root' extends TScope ? [] : [pluginId?: string]
): Promise<TService> {
): Promise<TSingleton extends true ? TService : TService[]> {
return this.getSubject(...args);
}
@@ -97,9 +105,10 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
*/
async getSubject(
...args: 'root' extends TScope ? [] : [pluginId?: string]
): Promise<TService> {
): Promise<TSingleton extends true ? TService : TService[]> {
const [pluginId] = args;
return this.#registry.get(this.#subject, pluginId ?? 'test')!;
const instance = this.#registry.get(this.#subject, pluginId ?? 'test')!;
return instance;
}
/**
@@ -109,10 +118,14 @@ export class ServiceFactoryTester<TService, TScope extends 'root' | 'plugin'> {
*
* A plugin ID can optionally be provided for plugin scoped services, otherwise the plugin ID 'test' is used.
*/
async getService<TGetService, TGetScope extends 'root' | 'plugin'>(
service: ServiceRef<TGetService, TGetScope>,
async getService<
TGetService,
TGetScope extends 'root' | 'plugin',
TGetSingleton extends boolean,
>(
service: ServiceRef<TGetService, TGetScope, TGetSingleton>,
...args: 'root' extends TGetScope ? [] : [pluginId?: string]
): Promise<TGetService> {
): Promise<TGetSingleton extends true ? TGetService : TGetService[]> {
const [pluginId] = args;
const instance = await this.#registry.get(service, pluginId ?? 'test');
if (instance === undefined) {
+1 -1
View File
@@ -88,7 +88,7 @@ export interface CatalogProcessingExtensionPoint {
export const catalogProcessingExtensionPoint: ExtensionPoint<CatalogProcessingExtensionPoint>;
// @alpha
export const catalogServiceRef: ServiceRef<CatalogApi, 'plugin'>;
export const catalogServiceRef: ServiceRef<CatalogApi, 'plugin', true>;
// (No @packageDocumentation comment for this package)
```
+2 -1
View File
@@ -66,11 +66,12 @@ export type EventsServiceEventHandler = (params: EventParams) => Promise<void>;
export const eventsServiceFactory: ServiceFactoryCompat<
EventsService,
'plugin',
true,
undefined
>;
// @public
export const eventsServiceRef: ServiceRef<EventsService, 'plugin'>;
export const eventsServiceRef: ServiceRef<EventsService, 'plugin', true>;
// @public (undocumented)
export type EventsServiceSubscribeOptions = {
+5 -1
View File
@@ -65,7 +65,11 @@ export interface NotificationService {
}
// @public (undocumented)
export const notificationService: ServiceRef<NotificationService, 'plugin'>;
export const notificationService: ServiceRef<
NotificationService,
'plugin',
true
>;
// @public (undocumented)
export type NotificationServiceOptions = {
@@ -46,7 +46,11 @@ export type SearchIndexServiceInitOptions = {
};
// @alpha
export const searchIndexServiceRef: ServiceRef<SearchIndexService, 'plugin'>;
export const searchIndexServiceRef: ServiceRef<
SearchIndexService,
'plugin',
true
>;
// (No @packageDocumentation comment for this package)
```
+2 -2
View File
@@ -37,7 +37,7 @@ export type SignalPayload<TMessage extends JsonObject = JsonObject> = {
export interface SignalService extends SignalsService {}
// @public @deprecated (undocumented)
export const signalService: ServiceRef<SignalsService, 'plugin'>;
export const signalService: ServiceRef<SignalsService, 'plugin', true>;
// @public (undocumented)
export interface SignalsService {
@@ -52,7 +52,7 @@ export type SignalsServiceOptions = {
};
// @public (undocumented)
export const signalsServiceRef: ServiceRef<SignalsService, 'plugin'>;
export const signalsServiceRef: ServiceRef<SignalsService, 'plugin', true>;
// (No @packageDocumentation comment for this package)
```