| @@ -3,7 +3,7 import { ConfigurableKeys, ContainerServ | |||
|
|
3 | 3 | import { ServiceContainer } from "./interfaces"; |
|
|
4 | 4 | import { argumentNotNull, each, isKey } from "./traits"; |
|
|
5 | 5 | |
|
|
6 |
export class FluentConfiguration<S |
|
|
|
6 | export class FluentConfiguration<S, Y extends ConfigurableKeys<S> = ConfigurableKeys<S>> { | |
|
|
7 | 7 | |
|
|
8 | 8 | private _builders: Partial<RegistrationBuildersMap<S>> = {}; |
|
|
9 | 9 | |
| @@ -12,7 +12,7 export class FluentConfiguration<S exten | |||
|
|
12 | 12 | * @template D The map of the services |
|
|
13 | 13 | * @returns self |
|
|
14 | 14 | */ |
|
|
15 |
declare<D extends |
|
|
|
15 | declare<D extends Pick<S, keyof D & keyof S>>(): FluentConfiguration<S & D, Y | ConfigurableKeys<D>> { | |
|
|
16 | 16 | return this as FluentConfiguration<S & D, Y | ConfigurableKeys<D>>; |
|
|
17 | 17 | } |
|
|
18 | 18 | |
| @@ -7,7 +7,7 export interface IDestroyable { | |||
|
|
7 | 7 | /** |
|
|
8 | 8 | * @template S Карта доступных зависимостей |
|
|
9 | 9 | */ |
|
|
10 |
export interface Resolver<S |
|
|
|
10 | export interface Resolver<S> { | |
|
|
11 | 11 | /** |
|
|
12 | 12 | * Функция для разрешения зависимостей, поддерживает создание фабричных методов, |
|
|
13 | 13 | * отложенную активацию и значение по-умолчанию для сервисов |
| @@ -27,13 +27,13 export interface Resolver<S extends obje | |||
|
|
27 | 27 | <K extends keyof S, O extends { lazy?: false; default?: unknown }>(name: K, opts?: O): (O extends { default: infer T } ? T : never) | NonNullable<S[K]>; |
|
|
28 | 28 | } |
|
|
29 | 29 | |
|
|
30 |
export type DepsMap<S |
|
|
|
31 | [k in key]: Refs<S> | |
|
|
30 | export type DepsMap<S> = { | |
|
|
31 | [k in key]: Refs<S> | keyof S; | |
|
|
32 | 32 | }; |
|
|
33 | 33 | |
|
|
34 |
export type Refs<S |
|
|
|
34 | export type Refs<S> = { | |
|
|
35 | 35 | [k in keyof S]: Ref<k, boolean, S[k]>; |
|
|
36 |
}[keyof S] |
|
|
|
36 | }[keyof S]; | |
|
|
37 | 37 | |
|
|
38 | 38 | export type Ref<K extends key, L extends boolean, D> = { name: K, lazy?: L, default?: D | null }; |
|
|
39 | 39 | |
| @@ -41,21 +41,40 export type Lazy<T, L extends boolean> = | |||
|
|
41 | 41 | |
|
|
42 | 42 | export type InferDefault<T> = T extends { default: infer D } ? D : never; |
|
|
43 | 43 | |
|
|
44 |
export type Resolve<S |
|
|
|
44 | export type Resolve<S, R> = | |
|
|
45 | 45 | R extends keyof S ? NonNullable<S[R]> : |
|
|
46 | 46 |
R extends Ref<infer K, infer L, unknown> ? |
|
|
47 | 47 |
|
|
|
48 | 48 |
|
|
|
49 | 49 | never; |
|
|
50 | 50 | |
|
|
51 | export interface IDescriptorBuilder<S extends object, T, R extends object, O extends keyof S> { | |
|
|
51 | /** | |
|
|
52 | * Интерфейс для конфигурирования сервиса в контейнере | |
|
|
53 | */ | |
|
|
54 | export interface IDescriptorBuilder<S, T, R, O extends keyof S> { | |
|
|
52 | 55 | |
|
|
53 | /** | |
|
|
56 | /** Указывает фабрика для создания экземпляра сервиса, фабрика передается | |
|
|
57 | * в виде параметра. При вызове фабрике будет передан объект с зависимостями, | |
|
|
58 | * которые были предварительно указаны вызовами метода `wants(...)` | |
|
|
54 | 59 | * |
|
|
55 | * @param f | |
|
|
60 | * Вызов данного метода завершает конфигурирование сервиса. | |
|
|
61 | * | |
|
|
62 | * @param f Фабрика для создания экземпляра сервиса | |
|
|
56 | 63 | */ |
|
|
57 | 64 | factory(f: (refs: R) => T): void; |
|
|
58 | 65 | |
|
|
66 | /** | |
|
|
67 | * Используется для указания зависимостей, которые потребуются фабричному | |
|
|
68 | * методу при создании нового экземпляра сервиса. Данный метод может быть | |
|
|
69 | * вызван несколько раз подряд, при этом вызовы этого метода имеют | |
|
|
70 | * кумулятивный эффект. | |
|
|
71 | * | |
|
|
72 | * @template X Тип объекта с зависимостями, которые требуется получить при | |
|
|
73 | * создании экземпляра фабрики при помощи фабричного метода. | |
|
|
74 | * @param refs Объект с описанием зависимостей | |
|
|
75 | * @returns Возвращает дескриптор сервиса, в котором указаны необходимые | |
|
|
76 | * зависимости | |
|
|
77 | */ | |
|
|
59 | 78 | wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X): |
|
|
60 | 79 | IDescriptorBuilder<S, T, R & { |
|
|
61 | 80 | [k in keyof X]: Resolve<S, X[k]>; |
| @@ -67,50 +86,60 export interface IDescriptorBuilder<S ex | |||
|
|
67 | 86 | lifetime(lifetime: "singleton", typeId: string | number | object): this; |
|
|
68 | 87 | lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this; |
|
|
69 | 88 | |
|
|
89 | /** Указывает функцию для освобождения экземпляра сервиса для случаев, когда | |
|
|
90 | * время жизни привязано к контейнеру. | |
|
|
91 | */ | |
|
|
70 | 92 | cleanup(cb: (item: T) => void): this; |
|
|
71 | 93 | |
|
|
94 | /** | |
|
|
95 | * Регистрирует в контейнере постоянное значение в качестве реализации сервиса. | |
|
|
96 | * | |
|
|
97 | * @param v Экземпляр реализации сервиса. | |
|
|
98 | */ | |
|
|
72 | 99 | value(v: T): void; |
|
|
73 | 100 | } |
|
|
74 | 101 | |
|
|
75 |
export type RegistrationBuilder<S |
|
|
|
102 | export type RegistrationBuilder<S, T> = (d: IDescriptorBuilder<S, T, object, ConfigurableKeys<S>>) => void; | |
|
|
76 | 103 | |
|
|
77 | 104 | export type RegistrationBuildersMap<S extends Configurable<S>, K extends keyof S = keyof S> = { |
|
|
78 | 105 | [k in K]-?: RegistrationBuilder<ContainerServices<S>, NonNullable<S[k]>> |
|
|
79 | 106 | }; |
|
|
80 | 107 | |
|
|
81 |
export interface Descriptor<S |
|
|
|
108 | export interface Descriptor<S, T> { | |
|
|
82 | 109 | activate(context: IActivationContext<S>): T; |
|
|
83 | 110 | } |
|
|
84 | 111 | |
|
|
85 |
export interface IActivationContext<S |
|
|
|
112 | export interface IActivationContext<S> extends ServiceLocator<S> { | |
|
|
86 | 113 | createLifetime<T>(): ILifetime<T>; |
|
|
87 | 114 | |
|
|
88 | 115 | createContainerLifetime<T>(): ILifetime<T>; |
|
|
89 | 116 | } |
|
|
90 | 117 | |
|
|
91 |
export type RegistrationMap<S |
|
|
|
118 | export type RegistrationMap<S, K extends keyof S = keyof S> = { | |
|
|
92 | 119 | [k in K]-?: Descriptor<S, S[k]>; |
|
|
93 | 120 | }; |
|
|
94 | 121 | |
|
|
95 |
export interface ContainerProvided<S |
|
|
|
122 | export interface ContainerProvided<S> { | |
|
|
96 | 123 | container: ServiceLocator<ContainerServices<S>>; |
|
|
97 | 124 | |
|
|
98 | 125 | childContainer: IContainerBuilder<S>; |
|
|
99 | 126 | } |
|
|
100 | 127 | |
|
|
101 |
export type Configurable<S> = { [k in keyof S |
|
|
|
128 | export type Configurable<S> = { [k in keyof S]: k extends ProvidedKeys ? never : S[k]; }; | |
|
|
102 | 129 | |
|
|
103 | 130 | export type ProvidedKeys = keyof ContainerProvided<never>; |
|
|
104 | 131 | |
|
|
105 | export type ContainerServices<S extends Configurable<S>> = S & ContainerProvided<S>; | |
|
|
132 | export type ContainerServices<S> = | |
|
|
133 | { [k in keyof S as k extends ProvidedKeys ? never: k]: S[k] } & | |
|
|
134 | ContainerProvided<S>; | |
|
|
106 | 135 | |
|
|
107 |
export type ConfigurableKeys<S |
|
|
|
136 | export type ConfigurableKeys<S> = Exclude<keyof S, ProvidedKeys>; | |
|
|
108 | 137 | |
|
|
109 |
export type ConfigurableServices<S |
|
|
|
138 | export type ConfigurableServices<S> = Pick<S, ConfigurableKeys<S>>; | |
|
|
110 | 139 | |
|
|
111 |
export type ContainerKeys<S |
|
|
|
140 | export type ContainerKeys<S> = keyof S | ProvidedKeys; | |
|
|
112 | 141 | |
|
|
113 |
export interface ServiceLocator<S |
|
|
|
142 | export interface ServiceLocator<S> { | |
|
|
114 | 143 | resolve<K extends keyof S>(name: K): NonNullable<S[K]>; |
|
|
115 | 144 | resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T; |
|
|
116 | 145 | } |
| @@ -119,14 +148,14 export interface LifetimeContainer { | |||
|
|
119 | 148 | createLifetime<T>(): ILifetime<T>; |
|
|
120 | 149 | } |
|
|
121 | 150 | |
|
|
122 |
export interface ServiceContainer<S |
|
|
|
151 | export interface ServiceContainer<S> extends | |
|
|
123 | 152 | ServiceLocator<ContainerServices<S>>, |
|
|
124 | 153 | IDestroyable { |
|
|
125 | 154 | |
|
|
126 | 155 | createChildContainer(): IContainerBuilder<S>; |
|
|
127 | 156 | } |
|
|
128 | 157 | |
|
|
129 |
export interface IContainerBuilder<S |
|
|
|
158 | export interface IContainerBuilder<S> { | |
|
|
130 | 159 | createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<S, NonNullable<S[K]>, object, keyof S>; |
|
|
131 | 160 | |
|
|
132 | 161 | build(): ServiceContainer<S>; |
| @@ -1,7 +1,7 | |||
|
|
1 | 1 | import { FluentConfiguration } from "./FluentConfiguration"; |
|
|
2 | 2 | import { IDestroyable } from "./interfaces"; |
|
|
3 | 3 | |
|
|
4 |
export function fluent<S |
|
|
|
4 | export function fluent<S = {}>() { | |
|
|
5 | 5 | return new FluentConfiguration<S>(); |
|
|
6 | 6 | } |
|
|
7 | 7 | |
| @@ -1,7 +1,8 | |||
|
|
1 | 1 | /* eslint max-classes-per-file: ["error", 20] */ |
|
|
2 | 2 | import { describe, it } from "mocha"; |
|
|
3 | 3 | import { Container } from "../Container"; |
|
|
4 |
import { Container |
|
|
|
4 | import { ContainerBuilder } from "../ContainerBuilder"; | |
|
|
5 | import { ConfigurableKeys, ContainerProvided, ContainerServices, DepsMap, Refs, Resolver } from "../interfaces"; | |
|
|
5 | 6 | import { fluent } from "../traits"; |
|
|
6 | 7 | |
|
|
7 | 8 | class Foo { |
| @@ -20,6 +21,8 interface Services { | |||
|
|
20 | 21 | bar?: Bar; |
|
|
21 | 22 | |
|
|
22 | 23 | baz: Foo; |
|
|
24 | ||
|
|
25 | container: string; | |
|
|
23 | 26 | } |
|
|
24 | 27 | |
|
|
25 | 28 | interface ServicesB { |
| @@ -35,9 +38,18 declare const resolver: Resolver<Service | |||
|
|
35 | 38 | |
|
|
36 | 39 | const foo = resolver("foo", {lazy: true}); |
|
|
37 | 40 | |
|
|
38 |
const mmap = <X extends DepsMap<Services |
|
|
|
41 | const mmap = <X extends DepsMap<Services>>(m: X) => {}; | |
|
|
42 | ||
|
|
43 | declare const refs: Refs<Services>; | |
|
|
39 | 44 | |
|
|
40 | const refs: Refs<Services> = {name: "bar", default: undefined}; | |
|
|
45 | if (refs && refs.name === "foo") { | |
|
|
46 | refs.default; | |
|
|
47 | } | |
|
|
48 | ||
|
|
49 | declare const x: ContainerServices<Services>; | |
|
|
50 | ||
|
|
51 | x.container.resolve("container"); | |
|
|
52 | ||
|
|
41 | 53 | |
|
|
42 | 54 | mmap({ |
|
|
43 | 55 | fooz: {name: "foo", lazy: false, default: undefined }, |
| @@ -56,6 +68,7 const config = fluent() | |||
|
|
56 | 68 | .declare<Services>() |
|
|
57 | 69 | .declare<ServicesB>() |
|
|
58 | 70 | .register({ |
|
|
71 | zoo: it => {}, | |
|
|
59 | 72 | bar: it => it |
|
|
60 | 73 | .lifetime("context") // тип активации, время жизни |
|
|
61 | 74 | .wants({ |
| @@ -77,11 +90,11 const config = fluent() | |||
|
|
77 | 90 | }) |
|
|
78 | 91 | .done({}); |
|
|
79 | 92 | |
|
|
80 |
declare const container: Container< |
|
|
|
93 | declare const container: ContainerBuilder<{}>; | |
|
|
81 | 94 | const c2 = config.configure(container); |
|
|
82 | 95 | |
|
|
83 | 96 | c2.resolve("foo"); |
|
|
84 | 97 | |
|
|
85 | 98 | declare const m :ContainerServices<{foo: Foo}>["container"]; |
|
|
86 | 99 | |
|
|
87 | m.resolve("container").resolve("container"); No newline at end of file | |
|
|
100 | m.resolve("container").resolve("container").resolve("foo"); No newline at end of file | |
General Comments 0
You need to be logged in to leave comments.
Login now
