##// END OF EJS Templates
Removed ContextResolver, added DescriptoBuilder.wants(...), dependencies are declared statically
cin -
r4:d9e74143f779 default
parent child
Show More
@@ -4,13 +4,13 export interface ActivationItem {
4 4 }
5 5
6 6 export class ActivationError {
7 activationStack: ActivationItem[];
7 readonly activationStack: ActivationItem[];
8 8
9 service: string;
9 readonly service: string;
10 10
11 innerException: unknown;
11 readonly innerException: unknown;
12 12
13 message: string;
13 readonly message: string;
14 14
15 15 constructor(service: string, activationStack: ActivationItem[], innerException: unknown) {
16 16 this.message = "Failed to activate the service";
@@ -1,26 +1,28
1 import { Resolver, RegistrationBuilder, LifetimeContainer, ConfigurableKeys } from "./interfaces";
1 import { RegistrationBuilder, LifetimeContainer, ConfigurableKeys, IDescriptorBuilder, Ref, Resolved, DepsMap } from "./interfaces";
2 2 import { Descriptor, ILifetime, ActivationType } from "./interfaces";
3 3 import { DescriptorImpl, RegistrationOverridesMap } from "./DescriptorImpl";
4 4 import { LifetimeManager } from "./LifetimeManager";
5 import { each, isKey, isPromise, isString, oid } from "./traits";
5 import { each, isKey, isPromise, isString, key, oid } from "./traits";
6 6
7 7 /**
8 8 * @template {S} Карта доступных зависимостей, как правило `ContainerServices`
9 9 * @template {T} Тип сервиса
10 10 */
11 export class DescriptorBuilder<S extends object, T> {
11 export class DescriptorBuilder<S extends object, T, R extends object = object> implements IDescriptorBuilder<S, T, R> {
12 12 private readonly _lifetimeContainer: LifetimeContainer;
13 13 private readonly _cb: (d: Descriptor<S, T>) => void;
14 14
15 15 private readonly _eb: (err: unknown) => void;
16 16
17 private readonly _refs: DepsMap<key, keyof S>;
18
17 19 private _lifetime = LifetimeManager.empty<T>();
18 20
19 21 private _overrides: RegistrationOverridesMap<S>;
20 22
21 23 private _cleanup?: (item: T) => void;
22 24
23 private _factory?: (resolve: Resolver<S>) => T;
25 private _factory?: (refs: R) => T;
24 26
25 27 private _pending = 1;
26 28
@@ -41,7 +43,32 export class DescriptorBuilder<S extends
41 43 this._cb = cb;
42 44 this._eb = eb;
43 45 this._overrides = {};
46 this._refs = {};
44 47 }
48 wants<X extends { [k in Exclude<key, keyof R>]: keyof S | Ref<keyof S, boolean, unknown>; }>(refs: X):
49 IDescriptorBuilder<S, T, R & {
50 [k in keyof X]:
51 X[k] extends keyof S ? NonNullable<S[X[k]]> :
52 X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> :
53 never;
54 }> {
55
56 each(refs, (v, k) => this._refs[k] = v);
57
58 return this as IDescriptorBuilder<S, T, R & {
59 [k in keyof X]:
60 X[k] extends keyof S ? NonNullable<S[X[k]]> :
61 X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> :
62 never;
63 }>;
64 }
65 factory(f: (refs: R) => T): void {
66 this._assertBuilding();
67 this._factory = f;
68 this._finalize();
69 this._complete();
70 }
71
45 72
46 73 private _assertBuilding() {
47 74 if (this._finalized)
@@ -103,12 +130,12 export class DescriptorBuilder<S extends
103 130 return this;
104 131 }
105 132
106 factory(f: (resolve: Resolver<S>) => T): void {
133 /*factory(f: (resolve: Resolver<S>) => T): void {
107 134 this._assertBuilding();
108 135 this._factory = f;
109 136 this._finalize();
110 137 this._complete();
111 }
138 }*/
112 139
113 140 value(v: T): void {
114 141 this._assertBuilding();
@@ -1,18 +1,19
1 import { Descriptor, ILifetime, ConfigurableKeys, Resolver } from "./interfaces";
1 import { Descriptor, ILifetime, ConfigurableKeys, DepsMap, Ref } from "./interfaces";
2 2 import { ActivationContext } from "./ActivationContext";
3 import { ContextResolver } from "./ContextResolver";
4 import { each } from "./traits";
3 import { each, isKey, key } from "./traits";
5 4
6 5 export type RegistrationOverridesMap<S extends object> = { [k in ConfigurableKeys<S>]?: Descriptor<S, NonNullable<S[k]>> };
7 6
8 7 export interface DescriptorImplArgs<S extends object, T> {
9 8 lifetime: ILifetime<T>;
10 9
11 factory: (resolve: Resolver<S>) => T;
10 factory: (refs: Record<key, never>) => T;
12 11
13 12 cleanup?: (item: T) => void;
14 13
15 14 overrides?: RegistrationOverridesMap<S>;
15
16 dependencies?: DepsMap<key, keyof S>;
16 17 }
17 18
18 19
@@ -22,17 +23,21 export class DescriptorImpl<S extends ob
22 23
23 24 private readonly _lifetime: ILifetime<T>;
24 25
25 private readonly _factory: (resolve: Resolver<S>) => T;
26 private readonly _factory: (refs: Record<key, never>) => T;
26 27
27 28 private readonly _cleanup?: (item: T) => void;
28 29
29 constructor(args: DescriptorImplArgs<S, T>) {
30 this._lifetime = args.lifetime;
31 this._factory = args.factory;
32 if (args.cleanup)
33 this._cleanup = args.cleanup;
34 if (args.overrides)
35 this._overrides = args.overrides;
30 private readonly _deps?: DepsMap<key, keyof S>;
31
32 constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs<S, T>) {
33 this._lifetime = lifetime;
34 this._factory = factory;
35 if (cleanup)
36 this._cleanup = cleanup;
37 if (overrides)
38 this._overrides = overrides;
39 if (dependencies)
40 this._deps = dependencies;
36 41 }
37 42
38 43 activate(context: ActivationContext<S>): T {
@@ -45,11 +50,26 export class DescriptorImpl<S extends ob
45 50 if (this._overrides)
46 51 each(this._overrides, (v, k) => context.register(k, v));
47 52
48
49 const resolver = new ContextResolver(context);
53 const resolve = <K extends keyof S, L extends boolean, D = never>({ name, lazy, ...opts }: Ref<K, L, D>) => {
54 if (lazy) {
55 return () => "default" in opts ? context.resolve(name, opts.default) : context.resolve(name);
56 } else {
57 return "default" in opts ? context.resolve(name, opts.default) : context.resolve(name);
58 }
59 };
50 60
61 const makeRefs = (deps?: DepsMap<key, keyof S>) => deps ?
62 Object.keys(deps)
63 .map(k => {
64 const ref = deps[k];
65 return isKey(ref) ?
66 { [k]: resolve({ name: ref }) } :
67 { [k]: resolve(ref) };
68 })
69 .reduce((a, p) => ({ ...a, ...p }), {} ) as Record<key, never>:
70 {} as Record<key, never>;
51 71
52 const instance = this._factory.call(undefined, resolver.resolve.bind(resolver));
72 const instance = this._factory.call(undefined, makeRefs(this._deps));
53 73
54 74 this._lifetime.store(instance, this._cleanup);
55 75
@@ -62,21 +62,20 export class FluentConfiguration<S exten
62 62 }
63 63
64 64
65 apply<A extends Partial<S>>(target: ServiceContainer<A>) {
65 apply<T extends ServiceContainer<S>>(target: T) {
66 66
67 67 let pending = 1;
68 68
69 const _t2 = target as ServiceContainer<S>;
70
71 69 const reject = (ex: unknown) => { throw ex; };
72 70
73 71 const complete = () => !--pending;
74 72
75 73 each(this._builders, (v, k) => {
76 74 pending++;
77 const d = new DescriptorBuilder<ContainerServices<S>, NonNullable<ConfigurableServices<S>[typeof k]>>(_t2,
75 const d = new DescriptorBuilder<ContainerServices<S>, NonNullable<ConfigurableServices<S>[typeof k]>>(
76 target,
78 77 result => {
79 _t2.register(k, result);
78 target.register(k, result);
80 79 complete();
81 80 },
82 81 reject
@@ -88,7 +87,7 export class FluentConfiguration<S exten
88 87 if (!complete())
89 88 throw new Error("The configuration didn't complete.");
90 89
91 return _t2 as ServiceContainer<A & S>;
90 return target as T & ServiceContainer<S>;
92 91 }
93 92
94 93 }
@@ -1,4 +1,5
1 1 import { ActivationContext } from "./ActivationContext";
2 import { key } from "./traits";
2 3
3 4 export type primitive = number | string | null | undefined | symbol;
4 5
@@ -29,13 +30,28 export interface Resolver<S extends obje
29 30 <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 31 }
31 32
32 export interface DescriptorBuilder<S extends object, T> {
33 export type DepsMap<K extends key, SK extends key> = { [k in K]: SK | Ref<SK, boolean, unknown> };
34
35 export type Ref<K extends key, L extends boolean, D> = { name: K, lazy?: L} | { name: K, lazy?: L, default: D };
36
37 export type Resolved<S, K extends keyof S, L, D> =
38 L extends true ? () => NonNullable<S[K]> | (unknown extends D ? never : D) : NonNullable<S[K]> | (unknown extends D ? never : D);
39
40 export interface IDescriptorBuilder<S extends object, T, R extends object = object> {
33 41
34 42 /**
35 43 *
36 44 * @param f
37 45 */
38 factory(f: (resolve: Resolver<S>) => T): void;
46 factory(f: (refs: R) => T): void;
47
48 wants<X extends DepsMap<Exclude<key, keyof R>, keyof S>>(refs: X):
49 IDescriptorBuilder<S, T, R & {
50 [k in keyof X]:
51 X[k] extends keyof S ? NonNullable<S[X[k]]> :
52 X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S,K & keyof S,L,D>:
53 never
54 }>
39 55
40 56 override<K extends ConfigurableKeys<S>>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
41 57 override<K extends ConfigurableKeys<S>>(services: { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }): this;
@@ -48,7 +64,7 export interface DescriptorBuilder<S ext
48 64 value(v: T): void;
49 65 }
50 66
51 export type RegistrationBuilder<S extends object, T> = (d: DescriptorBuilder<S, T>) => void;
67 export type RegistrationBuilder<S extends object, T> = (d: IDescriptorBuilder<S, T>) => void;
52 68
53 69 export type RegistrationBuildersMap<S extends object, K extends ConfigurableKeys<S> = ConfigurableKeys<S>> = {
54 70 [k in K]-?: RegistrationBuilder<ContainerServices<S>, NonNullable<ConfigurableServices<S>[k]>>
@@ -1,7 +1,6
1 1 /* eslint max-classes-per-file: ["error", 20] */
2 2 import { describe, it } from "mocha";
3 import {LifetimeManager} from "../LifetimeManager";
4 import {Container} from "../Container";
3 import { Container } from "../Container";
5 4 import { fluent } from "../traits";
6 5
7 6 class Foo {
@@ -11,7 +10,7 class Foo {
11 10 class Bar {
12 11 bar = "bar";
13 12
14 constructor(foo?: () => Foo) {}
13 constructor(foo?: () => Foo) { }
15 14 }
16 15
17 16 interface Services {
@@ -44,15 +43,23 const config = fluent()
44 43 .declare<ServicesB>()
45 44 .register({
46 45 bar: it => it
47 .lifetime("context")
48 .factory($ => new Bar($("zoo", {lazy: true, default: new Foo()}))),
49 foo: it => it.factory($ => new Foo()),
46 .lifetime("context") // тип активации, время жизни
47 .wants({
48 zoo: "zoo", // зависимость
49
50 zoo$: { name: "zoo", lazy: true } // отложенная активация,
51 //фабричный метод
52 })
53 .factory(({ zoo$ }) => // фабрика получает объект с именованными зависимостями
54 // удобно для деструктурирования
55 new Bar(zoo$) // создается экземпляр сервиса
56 ),
57 foo: it => it.factory(() => new Foo()),
50 58 baz: it => it.value(new Foo())
51 59 })
52 60 .done({});
53 61
54 declare const container: Container<SharedServices>;
55
62 declare const container: Container<Record<string, never>>;
56 63 const c2 = config.apply(container);
57 64
58 c2.resolve("baz"); No newline at end of file
65 c2.resolve("foo"); No newline at end of file
1 NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now