diff --git a/packages/backend-plugin-api/src/services/system/types.test.ts b/packages/backend-plugin-api/src/services/system/types.test.ts index 4d39ba0f62..9cbc45151d 100644 --- a/packages/backend-plugin-api/src/services/system/types.test.ts +++ b/packages/backend-plugin-api/src/services/system/types.test.ts @@ -16,9 +16,12 @@ import { createServiceFactory, createServiceRef } from './types'; +const ref = createServiceRef({ id: 'x' }); +const rootDep = createServiceRef({ id: 'y', scope: 'root' }); +const pluginDep = createServiceRef({ id: 'z' }); + describe('createServiceFactory', () => { it('should create a meta factory with no options', () => { - const ref = createServiceRef({ id: 'x' }); const metaFactory = createServiceFactory({ service: ref, deps: {}, @@ -37,12 +40,12 @@ describe('createServiceFactory', () => { metaFactory({ x: 1 }); // @ts-expect-error metaFactory(null); + // @ts-expect-error metaFactory(undefined); metaFactory(); }); it('should create a meta factory with optional options', () => { - const ref = createServiceRef({ id: 'x' }); const metaFactory = createServiceFactory((_opts?: { x: number }) => ({ service: ref, deps: {}, @@ -66,7 +69,6 @@ describe('createServiceFactory', () => { }); it('should create a meta factory with required options', () => { - const ref = createServiceRef({ id: 'x' }); const metaFactory = createServiceFactory((_opts: { x: number }) => ({ service: ref, deps: {}, @@ -95,7 +97,6 @@ describe('createServiceFactory', () => { interface TestOptions { x: number; } - const ref = createServiceRef({ id: 'x' }); const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ service: ref, deps: {}, @@ -122,7 +123,6 @@ describe('createServiceFactory', () => { interface TestOptions { x: number; } - const ref = createServiceRef({ id: 'x' }); const metaFactory = createServiceFactory((_opts: TestOptions) => ({ service: ref, deps: {}, @@ -147,8 +147,91 @@ describe('createServiceFactory', () => { metaFactory(); }); + it('should create factory with required options and dependencies', () => { + interface TestOptions { + x: number; + } + + function unused(..._any: any[]) {} + + const metaFactory = createServiceFactory((_opts: TestOptions) => ({ + service: ref, + deps: { + root: rootDep, + plugin: pluginDep, + }, + async factory({ root }) { + const root1: number = root; + // @ts-expect-error + const root2: string = root; + return async ({ plugin }) => { + const plugin3: boolean = plugin; + // @ts-expect-error + const plugin4: number = plugin; + unused(root1, root2, plugin3, plugin4); + return 'x'; + }; + }, + })); + expect(metaFactory).toEqual(expect.any(Function)); + + // @ts-expect-error + metaFactory('string'); + // @ts-expect-error + metaFactory({}); + metaFactory({ x: 1 }); + // @ts-expect-error + metaFactory({ x: 1, y: 2 }); + // @ts-expect-error + metaFactory(null); + // @ts-expect-error + metaFactory(undefined); + // @ts-expect-error + metaFactory(); + }); + + it('should create factory with optional options and dependencies', () => { + interface TestOptions { + x: number; + } + + function unused(..._any: any[]) {} + + const metaFactory = createServiceFactory((_opts?: TestOptions) => ({ + service: ref, + deps: { + root: rootDep, + plugin: pluginDep, + }, + async factory({ root }) { + const root1: number = root; + // @ts-expect-error + const root2: string = root; + return async ({ plugin }) => { + const plugin3: boolean = plugin; + // @ts-expect-error + const plugin4: number = plugin; + unused(root1, root2, plugin3, plugin4); + return 'x'; + }; + }, + })); + expect(metaFactory).toEqual(expect.any(Function)); + + // @ts-expect-error + metaFactory('string'); + // @ts-expect-error + metaFactory({}); + metaFactory({ x: 1 }); + // @ts-expect-error + metaFactory({ x: 1, y: 2 }); + // @ts-expect-error + metaFactory(null); + metaFactory(undefined); + metaFactory(); + }); + it('should only allow objects as options', () => { - const ref = createServiceRef({ id: 'x' }); // @ts-expect-error const metaFactory = createServiceFactory((_opts: string) => ({ service: ref, diff --git a/packages/backend-plugin-api/src/services/system/types.ts b/packages/backend-plugin-api/src/services/system/types.ts index 89528f9fc4..e9fd7bf971 100644 --- a/packages/backend-plugin-api/src/services/system/types.ts +++ b/packages/backend-plugin-api/src/services/system/types.ts @@ -14,11 +14,7 @@ * limitations under the License. */ -import { - FactoryFunctionConfig, - FactoryFunctionWithOptions, - MaybeOptions, -} from '../../types'; +import { FactoryFunctionConfig, FactoryFunction } from '../../types'; /** * TODO @@ -137,9 +133,7 @@ type ServiceRefsToInstances< T extends { [key in string]: ServiceRef }, TScope extends 'root' | 'plugin' = 'root' | 'plugin', > = { - [name in { - [key in keyof T]: T[key] extends ServiceRef ? key : never; - }[keyof T]]: T[name] extends ServiceRef ? TImpl : never; + [key in keyof T as T[key]['scope'] extends TScope ? key : never]: T[key]['T']; }; /** @public */ @@ -166,16 +160,16 @@ export function createServiceFactory< TScope extends 'root' | 'plugin', TImpl extends TService, TDeps extends { [name in string]: ServiceRef }, - TOpts extends MaybeOptions = undefined, + TOpts extends [options?: object] = [], >( config: FactoryFunctionConfig< ServiceFactoryConfig, TOpts >, -): FactoryFunctionWithOptions, TOpts> { +): FactoryFunction, TOpts> { if (typeof config === 'function') { - return (opts?: TOpts) => { - const c = config(opts!); + return (...opts: TOpts) => { + const c = config(...opts); return { ...c, scope: c.service.scope } as ServiceFactory; }; } diff --git a/packages/backend-plugin-api/src/types.ts b/packages/backend-plugin-api/src/types.ts index 1bd9d43390..a9f2316662 100644 --- a/packages/backend-plugin-api/src/types.ts +++ b/packages/backend-plugin-api/src/types.ts @@ -15,26 +15,17 @@ */ /** - * Base type for options objects that aren't required. - * * @ignore */ -export type MaybeOptions = object | undefined; - -/** - * @ignore - */ -export type FactoryFunctionConfig = +export type FactoryFunctionConfig = | TConfig - | ((options: TOptions) => TConfig) - | (() => TConfig); + | ((...params: TParams) => TConfig); /** * Helper type that makes the options argument optional if options are not required. * * @ignore */ -export type FactoryFunctionWithOptions = - undefined extends TOptions - ? (options?: TOptions) => TResult - : (options: TOptions) => TResult; +export type FactoryFunction = ( + ...params: TParams +) => TResult; diff --git a/packages/backend-plugin-api/src/wiring/factories.ts b/packages/backend-plugin-api/src/wiring/factories.ts index c9ccc9c0aa..134471f7c1 100644 --- a/packages/backend-plugin-api/src/wiring/factories.ts +++ b/packages/backend-plugin-api/src/wiring/factories.ts @@ -14,11 +14,7 @@ * limitations under the License. */ -import { - FactoryFunctionWithOptions, - MaybeOptions, - FactoryFunctionConfig, -} from '../types'; +import { FactoryFunction, FactoryFunctionConfig } from '../types'; import { BackendRegistrationPoints, BackendFeature, @@ -53,11 +49,11 @@ export interface BackendPluginConfig { } /** @public */ -export function createBackendPlugin( +export function createBackendPlugin( config: FactoryFunctionConfig, -): FactoryFunctionWithOptions { +): FactoryFunction { if (typeof config === 'function') { - return config as FactoryFunctionWithOptions; + return config as FactoryFunction; } return () => config; @@ -86,12 +82,12 @@ export interface BackendModuleConfig { * * The `pluginId` should exactly match the `id` of the plugin that the module extends. */ -export function createBackendModule( +export function createBackendModule( config: FactoryFunctionConfig, -): FactoryFunctionWithOptions { +): FactoryFunction { if (typeof config === 'function') { - return (options?: TOptions) => { - const c = config(options!); + return (...options: TOptions) => { + const c = config(...options); return { id: `${c.pluginId}.${c.moduleId}`, register: c.register,