##// END OF EJS Templates
code comments
cin -
r2:154d88dba49c default
parent child
Show More
@@ -1,67 +1,94
1 1 import { DescriptorBuilder } from "./DescriptorBuilder";
2 import { ConfigurableKeys, ContainerServices, ConfigurableServices, RegistrationBuildersMap, RequiredKeys } from "./interfaces";
2 import { ConfigurableKeys, ContainerServices, ConfigurableServices, RegistrationBuildersMap, ExtractRequired } from "./interfaces";
3 3 import { ServiceContainer } from "./interfaces";
4 4 import { argumentNotNull, each, isKey } from "./traits";
5 5
6 6 export class FluentConfiguration<S extends object, Y extends ConfigurableKeys<S> = ConfigurableKeys<S>> {
7 7
8 _builders: Partial<RegistrationBuildersMap<S>> = {};
8 private _builders: Partial<RegistrationBuildersMap<S>> = {};
9 9
10 /** Adds a declaration of the services to the current config.
11 *
12 * @template D The map of the services
13 * @returns self
14 */
10 15 declare<D extends Partial<Pick<S, keyof D & keyof S>>>(): FluentConfiguration<S & D, Y | ConfigurableKeys<D>> {
11 16 return this as FluentConfiguration<S & D, Y | ConfigurableKeys<D>>;
12 17 }
13 18
19 /** Adds compile-time information about the already provided services
20 *
21 * @template P The map of the provided services
22 * @returns self
23 */
14 24 provided<P extends Pick<S, keyof P & keyof S>>(): FluentConfiguration<S & P, Exclude<Y, keyof P>> {
15 25 return this as FluentConfiguration<S & P, Exclude<Y, keyof P>>;
16 26 }
17 27
28 /** Register the service.
29 *
30 * @param name The name of the service
31 * @param builder The service builder
32 * @returns self
33 */
18 34 register<K extends Y>(name: K, builder: RegistrationBuildersMap<S>[K]): FluentConfiguration<S, Exclude<Y, K>>;
35 /** Registers the collection of services
36 * @param config The collection of services to register.
37 * @returns self
38 */
19 39 register<K extends Y>(config: RegistrationBuildersMap<S, K>): FluentConfiguration<S, Exclude<Y, K>>;
20 40 register<K extends Y>(nameOrConfig: K | RegistrationBuildersMap<S, K>, builder?: RegistrationBuildersMap<S>[K]) {
21 41 if (isKey(nameOrConfig)) {
22 42 argumentNotNull(builder, "builder");
23 43 this._builders[nameOrConfig] = builder;
24 44 } else {
25 45 each(nameOrConfig, (v, k) => this.register(k, v));
26 46 }
27 47
28 48 return this as FluentConfiguration<S, Exclude<Y, K>>;
29 49 }
30 50
31 done(missing: RequiredKeys<S, Y> extends never ? void : RequiredKeys<S, Y>) {
32 if (missing !== undefined)
33 throw new Error("The configuration isn't complete");
51 /**
52 * This method is used to enable a compile time check of the configuration.
53 * If there are not configured services in the configuration the compiler
54 * will trigger the error.
55 *
56 * @param missing Empty object literal should always be specified.
57 * @returns self
58 */
59 // eslint-disable-next-line @typescript-eslint/no-unused-vars
60 done<M extends ExtractRequired<S,Y>>(missing: M) {
34 61 return this;
35 62 }
36 63
37 64
38 65 apply<A extends Partial<S>>(target: ServiceContainer<A>) {
39 66
40 67 let pending = 1;
41 68
42 69 const _t2 = target as ServiceContainer<S>;
43 70
44 71 const reject = (ex: unknown) => { throw ex; };
45 72
46 73 const complete = () => !--pending;
47 74
48 75 each(this._builders, (v, k) => {
49 76 pending++;
50 77 const d = new DescriptorBuilder<ContainerServices<S>, NonNullable<ConfigurableServices<S>[typeof k]>>(_t2,
51 78 result => {
52 79 _t2.register(k, result);
53 80 complete();
54 81 },
55 82 reject
56 83 );
57 84
58 85
59 86 v(d);
60 87 });
61 88 if (!complete())
62 89 throw new Error("The configuration didn't complete.");
63 90
64 91 return _t2 as ServiceContainer<A & S>;
65 92 }
66 93
67 94 }
@@ -1,121 +1,121
1 1 import { ActivationContext } from "./ActivationContext";
2 2
3 3 export type primitive = number | string | null | undefined | symbol;
4 4
5 5 export interface IDestroyable {
6 6 destroy(): void;
7 7 }
8 8
9 9 /**
10 10 * @template S ΠšΠ°Ρ€Ρ‚Π° доступных зависимостСй
11 11 */
12 12 export interface Resolver<S extends object> {
13 13 /**
14 14 * Ѐункция для Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½ΠΈΡ зависимостСй, ΠΏΠΎΠ΄Π΄Π΅Ρ€ΠΆΠΈΠ²Π°Π΅Ρ‚ созданиС Ρ„Π°Π±Ρ€ΠΈΡ‡Π½Ρ‹Ρ… ΠΌΠ΅Ρ‚ΠΎΠ΄ΠΎΠ²,
15 15 * ΠΎΡ‚Π»ΠΎΠΆΠ΅Π½Π½ΡƒΡŽ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΡŽ ΠΈ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ-ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ для сСрвисов
16 16 * @template K ΠšΠ»ΡŽΡ‡ сСрвиса ΠΈΠ· {@link S}
17 17 * @template O Π’ΠΈΠΏ ΠΏΠ°Ρ€Π°ΠΌΠ΅Ρ‚Ρ€Π° {@link opts} ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅Ρ‚ΡΡ для вывСдСния Ρ‚ΠΈΠΏΠ°
18 18 * Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌΠΎΠ³ΠΎ значСния.
19 19 * @param name ΠšΠ»ΡŽΡ‡ сСрвиса, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ Π±ΡƒΠ΄Π΅Ρ‚ Ρ€Π°Π·Ρ€Π΅ΡˆΠ΅Π½.
20 20 * @param {boolean=} opts.lazy ΠŸΡ€ΠΈΠ·Π½Π°ΠΊ Ρ‚ΠΎΠ³ΠΎ, Ρ‡Ρ‚ΠΎ трСбуСтся отлоТСнная активация,
21 21 * Π±ΡƒΠ΄Π΅Ρ‚ Π²ΠΎΠ·Π²Ρ€Π°Ρ‰Π΅Π½ Ρ„Π°Π±Ρ€ΠΈΡ‡Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ для получСния зависимости. Если Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½,
22 22 * Ρ‚ΠΎ считаСтся `false`.
23 23 * @param {any=} opts.default Π—Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ, Ссли Π² ΠΊΠΎΠ½Ρ‚Π΅ΠΉΠ½Π΅Ρ€Π΅ ΡƒΠΊΠ°Π·Π°Π½Π½Ρ‹ΠΉ
24 24 * сСрвис Π½Π΅ зарСгистрирован
25 25 * @returns Π›ΠΈΠ±ΠΎ Ρ„Π°Π±Ρ€ΠΈΡ‡Π½Ρ‹ΠΉ ΠΌΠ΅Ρ‚ΠΎΠ΄ для получСния зависимости, Π»ΠΈΠ±ΠΎ Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ зависимости
26 26 * @throws Error Если Π·Π°Π²ΠΈΡΠΈΠΌΠΎΡΡ‚ΡŒ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Π° ΠΈ Π½Π΅ прСдоставлСно Π·Π½Π°Ρ‡Π΅Π½ΠΈΠ΅ ΠΏΠΎ-ΡƒΠΌΠΎΠ»Ρ‡Π°Π½ΠΈΡŽ
27 27 */
28 28 <K extends keyof S, O extends { lazy: true; default?: unknown }>(name: K, opts?: O): () => (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
29 29 <K extends keyof S, O extends { lazy?: false; default?: unknown }>(name: K, opts?: O): (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
30 30 }
31 31
32 32 export interface DescriptorBuilder<S extends object, T> {
33 33
34 34 /**
35 35 *
36 36 * @param f
37 37 */
38 38 factory(f: (resolve: Resolver<S>) => T): void;
39 39
40 40 override<K extends ConfigurableKeys<S>>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
41 41 override<K extends ConfigurableKeys<S>>(services: { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }): this;
42 42
43 43 lifetime(lifetime: "singleton", typeId: string | number | object): this;
44 44 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
45 45
46 46 cleanup(cb: (item: T) => void): this;
47 47
48 48 value(v: T): void;
49 49 }
50 50
51 51 export type RegistrationBuilder<S extends object, T> = (d: DescriptorBuilder<S, T>) => void;
52 52
53 53 export type RegistrationBuildersMap<S extends object, K extends ConfigurableKeys<S> = ConfigurableKeys<S>> = {
54 54 [k in K]-?: RegistrationBuilder<ContainerServices<S>, NonNullable<ConfigurableServices<S>[k]>>
55 55 };
56 56
57 57 export interface Descriptor<S extends object, T> {
58 58 activate(context: ActivationContext<S>): T;
59 59 }
60 60
61 61 export type ConfigurableDescriptor<S extends object, K extends ConfigurableKeys<S>> = Descriptor<ContainerServices<S>, ConfigurableServices<S>[K]>;
62 62
63 63 export type RegistrationMap<S extends object, K extends keyof S = keyof S> = {
64 64 [k in K]-?: Descriptor<S, S[k]>;
65 65 };
66 66
67 67 export interface ProvidedServices<S extends object> {
68 68 container: ServiceLocator<ContainerServices<S>>;
69 69
70 70 childContainer: ServiceContainer<S>;
71 71 }
72 72
73 73 export type ProvidedKeys = keyof ProvidedServices<object>;
74 74
75 75 export type ContainerKeys<S extends object> = keyof ContainerServices<S>;
76 76
77 77 export type Mix<S, X> = { [k in keyof (S & X)]: k extends keyof X ? X[k] : S[k & keyof S] };
78 78
79 79 export type ContainerServices<S extends object> = Mix<S, ProvidedServices<S>>;
80 80
81 81 export type ConfigurableKeys<S extends object> = Exclude<keyof S, ProvidedKeys>;
82 82
83 83 export type ConfigurableServices<S extends object> = Pick<S, ConfigurableKeys<S>>;
84 84
85 85 export interface ServiceLocator<S extends object> {
86 86 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
87 87 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
88 88 }
89 89
90 90 export interface LifetimeContainer {
91 91 createLifetime<T>(): ILifetime<T>;
92 92 }
93 93
94 94 export interface ServiceContainer<S extends object> extends ServiceLocator<ContainerServices<S>>, LifetimeContainer, IDestroyable {
95 95
96 96 register<K extends ConfigurableKeys<S>>(name: K, service: ConfigurableDescriptor<S, K>): void;
97 97 register<K extends ConfigurableKeys<S>>(services: { [k in K]: ConfigurableDescriptor<S, K> }): void;
98 98
99 99 createChildContainer(): ServiceContainer<S>;
100 100 }
101 101
102 102
103 103 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
104 104
105 105 /**
106 106 * Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅ΠΉΡ для управлСния Тизнью экзСмпляра ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°. КаТдая рСгистрация ΠΈΠΌΠ΅Π΅Ρ‚
107 107 * свой собствСнный ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ `ILifetime`, ΠΊΠΎΡ‚ΠΎΡ€Ρ‹ΠΉ создаСтся ΠΏΡ€ΠΈ ΠΏΠ΅Ρ€Π²ΠΎΠΉ Π°ΠΊΡ‚ΠΈΠ²Π°Ρ†ΠΈΠΈ
108 108 */
109 109 export interface ILifetime<T> {
110 110 /** ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅Ρ‚, Ρ‡Ρ‚ΠΎ ΡƒΠΆΠ΅ создан экзСмпляр ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° */
111 111 has(): boolean;
112 112
113 113 get(): T;
114 114
115 115 initialize(context: ActivationContext<object>): void;
116 116
117 117 store(item: T, cleanup?: (item: T) => void): void;
118 118 }
119 119
120 export type RequiredKeys<T, K extends keyof T = keyof T> = keyof { [p in K as (T[p] extends undefined ? never : p)]-?: T[p] };
120 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
121 121
@@ -1,58 +1,58
1 1 /* eslint max-classes-per-file: ["error", 20] */
2 2 import { describe, it } from "mocha";
3 3 import {LifetimeManager} from "../LifetimeManager";
4 4 import {Container} from "../Container";
5 5 import { fluent } from "../traits";
6 6
7 7 class Foo {
8 8 foo = "foo";
9 9 }
10 10
11 11 class Bar {
12 12 bar = "bar";
13 13
14 14 constructor(foo?: () => Foo) {}
15 15 }
16 16
17 17 interface Services {
18 18 foo: Foo;
19 19
20 20 bar?: Bar;
21 21
22 22 baz: Foo;
23 23 }
24 24
25 25 interface ServicesB {
26 26 // will give errors
27 27 // baz: Bar;
28 28
29 29 baz: Foo;
30 30
31 31 zoo?: Foo;
32 32 }
33 33
34 34 interface SharedServices {
35 35 foo: Foo;
36 36
37 37 bar?: Bar;
38 38
39 39 baz: Bar;
40 40 }
41 41
42 42 const config = fluent()
43 43 .declare<Services>()
44 44 .declare<ServicesB>()
45 45 .register({
46 46 bar: it => it
47 47 .lifetime("context")
48 48 .factory($ => new Bar($("zoo", {lazy: true, default: new Foo()}))),
49 49 foo: it => it.factory($ => new Foo()),
50 50 baz: it => it.value(new Foo())
51 51 })
52 .done();
52 .done({});
53 53
54 54 declare const container: Container<SharedServices>;
55 55
56 56 const c2 = config.apply(container);
57 57
58 58 c2.resolve("baz"); No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now