##// END OF EJS Templates
Removed ContextResolver, added DescriptoBuilder.wants(...), dependencies are declared statically
cin -
r4:d9e74143f779 default
parent child
Show More
@@ -1,41 +1,41
1 export interface ActivationItem {
1 export interface ActivationItem {
2 name: string;
2 name: string;
3 service: string;
3 service: string;
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";
17 this.activationStack = activationStack;
17 this.activationStack = activationStack;
18 this.service = service;
18 this.service = service;
19 this.innerException = innerException;
19 this.innerException = innerException;
20 }
20 }
21
21
22 toString() {
22 toString() {
23 const parts = [this.message];
23 const parts = [this.message];
24 if (this.service)
24 if (this.service)
25 parts.push(`when activating: ${String(this.service)}`);
25 parts.push(`when activating: ${String(this.service)}`);
26
26
27 if (this.innerException)
27 if (this.innerException)
28 parts.push(`caused by: ${String(this.innerException)}`);
28 parts.push(`caused by: ${String(this.innerException)}`);
29
29
30 if (this.activationStack) {
30 if (this.activationStack) {
31 parts.push("at");
31 parts.push("at");
32 parts.push.apply(null,
32 parts.push.apply(null,
33 this.activationStack
33 this.activationStack
34 .map(({ name, service }) => ` ${name} ${service}`)
34 .map(({ name, service }) => ` ${name} ${service}`)
35 );
35 );
36
36
37 }
37 }
38
38
39 return parts.join("\n");
39 return parts.join("\n");
40 }
40 }
41 }
41 }
@@ -1,169 +1,196
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
27 private _failed = false;
29 private _failed = false;
28
30
29 private _finalized = false;
31 private _finalized = false;
30
32
31 /**
33 /**
32 * Creates new DescriptorBuilder. Accepts a lifetime container for resolving "container"
34 * Creates new DescriptorBuilder. Accepts a lifetime container for resolving "container"
33 * lifetime.
35 * lifetime.
34 *
36 *
35 * @param container The lifetime container is the container where the service is to be registered.
37 * @param container The lifetime container is the container where the service is to be registered.
36 * @param cb The callback to receive the built service descriptor
38 * @param cb The callback to receive the built service descriptor
37 * @param eb The callback to receive the error due
39 * @param eb The callback to receive the error due
38 */
40 */
39 constructor(container: LifetimeContainer, cb: (d: Descriptor<S, T>) => void, eb: (err: unknown) => void) {
41 constructor(container: LifetimeContainer, cb: (d: Descriptor<S, T>) => void, eb: (err: unknown) => void) {
40 this._lifetimeContainer = container;
42 this._lifetimeContainer = container;
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)
48 throw new Error("The descriptor builder is finalized");
75 throw new Error("The descriptor builder is finalized");
49 }
76 }
50
77
51 private _finalize() {
78 private _finalize() {
52 this._finalized = true;
79 this._finalized = true;
53 }
80 }
54
81
55 override<K extends ConfigurableKeys<S>>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
82 override<K extends ConfigurableKeys<S>>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
56 override<K extends ConfigurableKeys<S>>(services: { [k in K]: RegistrationBuilder<S, NonNullable<S[k]>> }): this;
83 override<K extends ConfigurableKeys<S>>(services: { [k in K]: RegistrationBuilder<S, NonNullable<S[k]>> }): this;
57 override<K extends ConfigurableKeys<S>>(nameOrServices: K | { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }, builder?: RegistrationBuilder<S, NonNullable<S[K]>>): this {
84 override<K extends ConfigurableKeys<S>>(nameOrServices: K | { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }, builder?: RegistrationBuilder<S, NonNullable<S[K]>>): this {
58 this._assertBuilding();
85 this._assertBuilding();
59 const guard = (v: void | Promise<void>) => {
86 const guard = (v: void | Promise<void>) => {
60 if (isPromise(v))
87 if (isPromise(v))
61 v.catch(err => this._fail(err));
88 v.catch(err => this._fail(err));
62 };
89 };
63
90
64 if (isKey(nameOrServices)) {
91 if (isKey(nameOrServices)) {
65 if (builder) {
92 if (builder) {
66 this._defer();
93 this._defer();
67 const d = new DescriptorBuilder<S, NonNullable<S[K]>>(
94 const d = new DescriptorBuilder<S, NonNullable<S[K]>>(
68 this._lifetimeContainer,
95 this._lifetimeContainer,
69 result => {
96 result => {
70 this._overrides[nameOrServices] = result;
97 this._overrides[nameOrServices] = result;
71 this._complete();
98 this._complete();
72 },
99 },
73 err => this._fail(err)
100 err => this._fail(err)
74 );
101 );
75
102
76 try {
103 try {
77 guard(builder(d));
104 guard(builder(d));
78 } catch (err) {
105 } catch (err) {
79 this._fail(err);
106 this._fail(err);
80 }
107 }
81 }
108 }
82 } else {
109 } else {
83 each(nameOrServices, (v, k) => this.override(k, v));
110 each(nameOrServices, (v, k) => this.override(k, v));
84 }
111 }
85 return this;
112 return this;
86 }
113 }
87
114
88 lifetime(lifetime: "singleton", typeId: string): this;
115 lifetime(lifetime: "singleton", typeId: string): this;
89 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
116 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
90 lifetime(lifetime: ILifetime<T> | ActivationType, typeId?: string): this {
117 lifetime(lifetime: ILifetime<T> | ActivationType, typeId?: string): this {
91 this._assertBuilding();
118 this._assertBuilding();
92 if (isString(lifetime)) {
119 if (isString(lifetime)) {
93 this._lifetime = this._resolveLifetime(lifetime, typeId);
120 this._lifetime = this._resolveLifetime(lifetime, typeId);
94 } else {
121 } else {
95 this._lifetime = lifetime;
122 this._lifetime = lifetime;
96 }
123 }
97 return this;
124 return this;
98 }
125 }
99
126
100 cleanup(cb: (item: T) => void): this {
127 cleanup(cb: (item: T) => void): this {
101 this._assertBuilding();
128 this._assertBuilding();
102 this._cleanup = cb;
129 this._cleanup = cb;
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();
115 this._cb({
142 this._cb({
116 activate() {
143 activate() {
117 return v;
144 return v;
118 }
145 }
119 });
146 });
120 this._finalize();
147 this._finalize();
121 }
148 }
122
149
123 _resolveLifetime<T>(activation: ActivationType, typeId?: string | object): ILifetime<T> {
150 _resolveLifetime<T>(activation: ActivationType, typeId?: string | object): ILifetime<T> {
124 switch (activation) {
151 switch (activation) {
125 case "container":
152 case "container":
126 return LifetimeManager.containerLifetime(this._lifetimeContainer);
153 return LifetimeManager.containerLifetime(this._lifetimeContainer);
127 case "hierarchy":
154 case "hierarchy":
128 return LifetimeManager.hierarchyLifetime();
155 return LifetimeManager.hierarchyLifetime();
129 case "context":
156 case "context":
130 return LifetimeManager.contextLifetime();
157 return LifetimeManager.contextLifetime();
131 case "singleton": {
158 case "singleton": {
132 if (!typeId)
159 if (!typeId)
133 throw Error("The singleton activation requires a typeId");
160 throw Error("The singleton activation requires a typeId");
134
161
135 const _oid = isString(typeId) ? typeId : oid(typeId);
162 const _oid = isString(typeId) ? typeId : oid(typeId);
136
163
137 return LifetimeManager.singletonLifetime(_oid);
164 return LifetimeManager.singletonLifetime(_oid);
138 }
165 }
139 default:
166 default:
140 return LifetimeManager.empty();
167 return LifetimeManager.empty();
141 }
168 }
142 }
169 }
143
170
144 _defer() {
171 _defer() {
145 this._pending++;
172 this._pending++;
146 }
173 }
147
174
148 _complete() {
175 _complete() {
149 if (--this._pending === 0) {
176 if (--this._pending === 0) {
150 if (!this._factory)
177 if (!this._factory)
151 throw new Error("The factory must be specified");
178 throw new Error("The factory must be specified");
152
179
153 this._cb(new DescriptorImpl<S, T>({
180 this._cb(new DescriptorImpl<S, T>({
154 lifetime: this._lifetime,
181 lifetime: this._lifetime,
155 factory: this._factory,
182 factory: this._factory,
156 overrides: this._overrides,
183 overrides: this._overrides,
157 cleanup: this._cleanup
184 cleanup: this._cleanup
158 }));
185 }));
159 }
186 }
160 }
187 }
161
188
162 _fail(err: unknown) {
189 _fail(err: unknown) {
163 if (!this._failed) {
190 if (!this._failed) {
164 this._failed = true;
191 this._failed = true;
165 this._eb.call(undefined, err);
192 this._eb.call(undefined, err);
166 }
193 }
167 }
194 }
168
195
169 }
196 }
@@ -1,63 +1,83
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
19 export class DescriptorImpl<S extends object, T> implements Descriptor<S, T> {
20 export class DescriptorImpl<S extends object, T> implements Descriptor<S, T> {
20
21
21 private readonly _overrides?: RegistrationOverridesMap<S>;
22 private readonly _overrides?: RegistrationOverridesMap<S>;
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 {
39
44
40 if (this._lifetime.has())
45 if (this._lifetime.has())
41 return this._lifetime.get();
46 return this._lifetime.get();
42
47
43 this._lifetime.initialize(context);
48 this._lifetime.initialize(context);
44
49
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
56 return instance;
76 return instance;
57 }
77 }
58
78
59
79
60 toString() {
80 toString() {
61 return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`;
81 return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`;
62 }
82 }
63 }
83 }
@@ -1,94 +1,93
1 import { DescriptorBuilder } from "./DescriptorBuilder";
1 import { DescriptorBuilder } from "./DescriptorBuilder";
2 import { ConfigurableKeys, ContainerServices, ConfigurableServices, RegistrationBuildersMap, ExtractRequired } from "./interfaces";
2 import { ConfigurableKeys, ContainerServices, ConfigurableServices, RegistrationBuildersMap, ExtractRequired } from "./interfaces";
3 import { ServiceContainer } from "./interfaces";
3 import { ServiceContainer } from "./interfaces";
4 import { argumentNotNull, each, isKey } from "./traits";
4 import { argumentNotNull, each, isKey } from "./traits";
5
5
6 export class FluentConfiguration<S extends object, Y extends ConfigurableKeys<S> = ConfigurableKeys<S>> {
6 export class FluentConfiguration<S extends object, Y extends ConfigurableKeys<S> = ConfigurableKeys<S>> {
7
7
8 private _builders: Partial<RegistrationBuildersMap<S>> = {};
8 private _builders: Partial<RegistrationBuildersMap<S>> = {};
9
9
10 /** Adds a declaration of the services to the current config.
10 /** Adds a declaration of the services to the current config.
11 *
11 *
12 * @template D The map of the services
12 * @template D The map of the services
13 * @returns self
13 * @returns self
14 */
14 */
15 declare<D extends Partial<Pick<S, keyof D & keyof S>>>(): FluentConfiguration<S & D, Y | ConfigurableKeys<D>> {
15 declare<D extends Partial<Pick<S, keyof D & keyof S>>>(): FluentConfiguration<S & D, Y | ConfigurableKeys<D>> {
16 return this as FluentConfiguration<S & D, Y | ConfigurableKeys<D>>;
16 return this as FluentConfiguration<S & D, Y | ConfigurableKeys<D>>;
17 }
17 }
18
18
19 /** Adds compile-time information about the already provided services
19 /** Adds compile-time information about the already provided services
20 *
20 *
21 * @template P The map of the provided services
21 * @template P The map of the provided services
22 * @returns self
22 * @returns self
23 */
23 */
24 provided<P extends Pick<S, keyof P & keyof S>>(): FluentConfiguration<S & P, Exclude<Y, keyof P>> {
24 provided<P extends Pick<S, keyof P & keyof S>>(): FluentConfiguration<S & P, Exclude<Y, keyof P>> {
25 return this as FluentConfiguration<S & P, Exclude<Y, keyof P>>;
25 return this as FluentConfiguration<S & P, Exclude<Y, keyof P>>;
26 }
26 }
27
27
28 /** Register the service.
28 /** Register the service.
29 *
29 *
30 * @param name The name of the service
30 * @param name The name of the service
31 * @param builder The service builder
31 * @param builder The service builder
32 * @returns self
32 * @returns self
33 */
33 */
34 register<K extends Y>(name: K, builder: RegistrationBuildersMap<S>[K]): FluentConfiguration<S, Exclude<Y, K>>;
34 register<K extends Y>(name: K, builder: RegistrationBuildersMap<S>[K]): FluentConfiguration<S, Exclude<Y, K>>;
35 /** Registers the collection of services
35 /** Registers the collection of services
36 * @param config The collection of services to register.
36 * @param config The collection of services to register.
37 * @returns self
37 * @returns self
38 */
38 */
39 register<K extends Y>(config: RegistrationBuildersMap<S, K>): FluentConfiguration<S, Exclude<Y, K>>;
39 register<K extends Y>(config: RegistrationBuildersMap<S, K>): FluentConfiguration<S, Exclude<Y, K>>;
40 register<K extends Y>(nameOrConfig: K | RegistrationBuildersMap<S, K>, builder?: RegistrationBuildersMap<S>[K]) {
40 register<K extends Y>(nameOrConfig: K | RegistrationBuildersMap<S, K>, builder?: RegistrationBuildersMap<S>[K]) {
41 if (isKey(nameOrConfig)) {
41 if (isKey(nameOrConfig)) {
42 argumentNotNull(builder, "builder");
42 argumentNotNull(builder, "builder");
43 this._builders[nameOrConfig] = builder;
43 this._builders[nameOrConfig] = builder;
44 } else {
44 } else {
45 each(nameOrConfig, (v, k) => this.register(k, v));
45 each(nameOrConfig, (v, k) => this.register(k, v));
46 }
46 }
47
47
48 return this as FluentConfiguration<S, Exclude<Y, K>>;
48 return this as FluentConfiguration<S, Exclude<Y, K>>;
49 }
49 }
50
50
51 /**
51 /**
52 * This method is used to enable a compile time check of the configuration.
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
53 * If there are not configured services in the configuration the compiler
54 * will trigger the error.
54 * will trigger the error.
55 *
55 *
56 * @param missing Empty object literal should always be specified.
56 * @param missing Empty object literal should always be specified.
57 * @returns self
57 * @returns self
58 */
58 */
59 // eslint-disable-next-line @typescript-eslint/no-unused-vars
59 // eslint-disable-next-line @typescript-eslint/no-unused-vars
60 done<M extends ExtractRequired<S,Y>>(missing: M) {
60 done<M extends ExtractRequired<S,Y>>(missing: M) {
61 return this;
61 return this;
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
83 );
82 );
84
83
85
84
86 v(d);
85 v(d);
87 });
86 });
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,121 +1,137
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
5 export interface IDestroyable {
6 export interface IDestroyable {
6 destroy(): void;
7 destroy(): void;
7 }
8 }
8
9
9 /**
10 /**
10 * @template S Карта доступных зависимостей
11 * @template S Карта доступных зависимостей
11 */
12 */
12 export interface Resolver<S extends object> {
13 export interface Resolver<S extends object> {
13 /**
14 /**
14 * Функция для разрешения зависимостей, поддерживает создание фабричных методов,
15 * Функция для разрешения зависимостей, поддерживает создание фабричных методов,
15 * отложенную активацию и значение по-умолчанию для сервисов
16 * отложенную активацию и значение по-умолчанию для сервисов
16 * @template K Ключ сервиса из {@link S}
17 * @template K Ключ сервиса из {@link S}
17 * @template O Тип параметра {@link opts} используется для выведения типа
18 * @template O Тип параметра {@link opts} используется для выведения типа
18 * возвращаемого значения.
19 * возвращаемого значения.
19 * @param name Ключ сервиса, который будет разрешен.
20 * @param name Ключ сервиса, который будет разрешен.
20 * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
21 * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
21 * будет возвращен фабричный метод для получения зависимости. Если не указан,
22 * будет возвращен фабричный метод для получения зависимости. Если не указан,
22 * то считается `false`.
23 * то считается `false`.
23 * @param {any=} opts.default Значение по умолчанию, если в контейнере указанный
24 * @param {any=} opts.default Значение по умолчанию, если в контейнере указанный
24 * сервис не зарегистрирован
25 * сервис не зарегистрирован
25 * @returns Либо фабричный метод для получения зависимости, либо значение зависимости
26 * @returns Либо фабричный метод для получения зависимости, либо значение зависимости
26 * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
27 * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
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 <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 <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;
42
58
43 lifetime(lifetime: "singleton", typeId: string | number | object): this;
59 lifetime(lifetime: "singleton", typeId: string | number | object): this;
44 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
60 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
45
61
46 cleanup(cb: (item: T) => void): this;
62 cleanup(cb: (item: T) => void): this;
47
63
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]>>
55 };
71 };
56
72
57 export interface Descriptor<S extends object, T> {
73 export interface Descriptor<S extends object, T> {
58 activate(context: ActivationContext<S>): T;
74 activate(context: ActivationContext<S>): T;
59 }
75 }
60
76
61 export type ConfigurableDescriptor<S extends object, K extends ConfigurableKeys<S>> = Descriptor<ContainerServices<S>, ConfigurableServices<S>[K]>;
77 export type ConfigurableDescriptor<S extends object, K extends ConfigurableKeys<S>> = Descriptor<ContainerServices<S>, ConfigurableServices<S>[K]>;
62
78
63 export type RegistrationMap<S extends object, K extends keyof S = keyof S> = {
79 export type RegistrationMap<S extends object, K extends keyof S = keyof S> = {
64 [k in K]-?: Descriptor<S, S[k]>;
80 [k in K]-?: Descriptor<S, S[k]>;
65 };
81 };
66
82
67 export interface ProvidedServices<S extends object> {
83 export interface ProvidedServices<S extends object> {
68 container: ServiceLocator<ContainerServices<S>>;
84 container: ServiceLocator<ContainerServices<S>>;
69
85
70 childContainer: ServiceContainer<S>;
86 childContainer: ServiceContainer<S>;
71 }
87 }
72
88
73 export type ProvidedKeys = keyof ProvidedServices<object>;
89 export type ProvidedKeys = keyof ProvidedServices<object>;
74
90
75 export type ContainerKeys<S extends object> = keyof ContainerServices<S>;
91 export type ContainerKeys<S extends object> = keyof ContainerServices<S>;
76
92
77 export type Mix<S, X> = { [k in keyof (S & X)]: k extends keyof X ? X[k] : S[k & keyof S] };
93 export type Mix<S, X> = { [k in keyof (S & X)]: k extends keyof X ? X[k] : S[k & keyof S] };
78
94
79 export type ContainerServices<S extends object> = Mix<S, ProvidedServices<S>>;
95 export type ContainerServices<S extends object> = Mix<S, ProvidedServices<S>>;
80
96
81 export type ConfigurableKeys<S extends object> = Exclude<keyof S, ProvidedKeys>;
97 export type ConfigurableKeys<S extends object> = Exclude<keyof S, ProvidedKeys>;
82
98
83 export type ConfigurableServices<S extends object> = Pick<S, ConfigurableKeys<S>>;
99 export type ConfigurableServices<S extends object> = Pick<S, ConfigurableKeys<S>>;
84
100
85 export interface ServiceLocator<S extends object> {
101 export interface ServiceLocator<S extends object> {
86 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
102 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
87 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
103 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
88 }
104 }
89
105
90 export interface LifetimeContainer {
106 export interface LifetimeContainer {
91 createLifetime<T>(): ILifetime<T>;
107 createLifetime<T>(): ILifetime<T>;
92 }
108 }
93
109
94 export interface ServiceContainer<S extends object> extends ServiceLocator<ContainerServices<S>>, LifetimeContainer, IDestroyable {
110 export interface ServiceContainer<S extends object> extends ServiceLocator<ContainerServices<S>>, LifetimeContainer, IDestroyable {
95
111
96 register<K extends ConfigurableKeys<S>>(name: K, service: ConfigurableDescriptor<S, K>): void;
112 register<K extends ConfigurableKeys<S>>(name: K, service: ConfigurableDescriptor<S, K>): void;
97 register<K extends ConfigurableKeys<S>>(services: { [k in K]: ConfigurableDescriptor<S, K> }): void;
113 register<K extends ConfigurableKeys<S>>(services: { [k in K]: ConfigurableDescriptor<S, K> }): void;
98
114
99 createChildContainer(): ServiceContainer<S>;
115 createChildContainer(): ServiceContainer<S>;
100 }
116 }
101
117
102
118
103 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
119 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
104
120
105 /**
121 /**
106 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
122 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
107 * свой собственный объект `ILifetime`, который создается при первой активации
123 * свой собственный объект `ILifetime`, который создается при первой активации
108 */
124 */
109 export interface ILifetime<T> {
125 export interface ILifetime<T> {
110 /** Проверяет, что уже создан экземпляр объекта */
126 /** Проверяет, что уже создан экземпляр объекта */
111 has(): boolean;
127 has(): boolean;
112
128
113 get(): T;
129 get(): T;
114
130
115 initialize(context: ActivationContext<object>): void;
131 initialize(context: ActivationContext<object>): void;
116
132
117 store(item: T, cleanup?: (item: T) => void): void;
133 store(item: T, cleanup?: (item: T) => void): void;
118 }
134 }
119
135
120 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
136 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
121
137
@@ -1,58 +1,65
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";
3 import { Container } from "../Container";
4 import {Container} from "../Container";
5 import { fluent } from "../traits";
4 import { fluent } from "../traits";
6
5
7 class Foo {
6 class Foo {
8 foo = "foo";
7 foo = "foo";
9 }
8 }
10
9
11 class Bar {
10 class Bar {
12 bar = "bar";
11 bar = "bar";
13
12
14 constructor(foo?: () => Foo) {}
13 constructor(foo?: () => Foo) { }
15 }
14 }
16
15
17 interface Services {
16 interface Services {
18 foo: Foo;
17 foo: Foo;
19
18
20 bar?: Bar;
19 bar?: Bar;
21
20
22 baz: Foo;
21 baz: Foo;
23 }
22 }
24
23
25 interface ServicesB {
24 interface ServicesB {
26 // will give errors
25 // will give errors
27 // baz: Bar;
26 // baz: Bar;
28
27
29 baz: Foo;
28 baz: Foo;
30
29
31 zoo?: Foo;
30 zoo?: Foo;
32 }
31 }
33
32
34 interface SharedServices {
33 interface SharedServices {
35 foo: Foo;
34 foo: Foo;
36
35
37 bar?: Bar;
36 bar?: Bar;
38
37
39 baz: Bar;
38 baz: Bar;
40 }
39 }
41
40
42 const config = fluent()
41 const config = fluent()
43 .declare<Services>()
42 .declare<Services>()
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