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