##// END OF EJS Templates
almost woking typings
cin -
r9:988f0f6aab67 default
parent child
Show More
@@ -1,3 +1,6
1 {
1 {
2 "java.configuration.updateBuildConfiguration": "automatic"
2 "java.configuration.updateBuildConfiguration": "automatic",
3 "cSpell.words": [
4 "linkcode"
5 ]
3 } No newline at end of file
6 }
@@ -1,5 +1,4
1 import { Descriptor, ILifetime, RegistrationMap, LifetimeContainer, ConfigurableKeys } from "./interfaces";
1 import { Descriptor, ILifetime, IActivationContext, DescriptorMap, ILifetimeManager } from "./interfaces";
2 import { LifetimeManager } from "./LifetimeManager";
3 import { argumentNotNull } from "./traits";
2 import { argumentNotNull } from "./traits";
4
3
5 export interface ActivationContextInfo {
4 export interface ActivationContextInfo {
@@ -11,20 +10,25 export interface ActivationContextInfo {
11
10
12 let nextId = 1;
11 let nextId = 1;
13
12
14 /** This class is created once per `Container.resolve` method call and used to
13 /** This object is created once per `Container.resolve` method call and used to
15 * cache dependencies and to track created instances. The activation context
14 * cache dependencies and to track created instances. The activation context
16 * tracks services with `context` activation type.
15 * tracks services with `context` activation type.
16 *
17 * @template S The service map used in the activation context, services from
18 * this map are available to resolution.
19 * @template U A set of keys from the service map which can be overridden in
20 * this activation context.
17 */
21 */
18 export class ActivationContext<S extends object> {
22 export class ActivationContext<S> implements IActivationContext<S> {
19 private readonly _cache: Record<string, unknown>;
23 private readonly _cache: Record<string, unknown>;
20
24
21 private readonly _services: Partial<RegistrationMap<S>>;
25 private readonly _services: DescriptorMap<S>;
22
26
23 private readonly _name: string;
27 private readonly _name: string;
24
28
25 private readonly _service: Descriptor<S, unknown>;
29 private readonly _service: Descriptor<S, unknown>;
26
30
27 private readonly _containerLifetimeManager: LifetimeManager;
31 private readonly _containerLifetimeManager: ILifetimeManager;
28
32
29 private readonly _parent: ActivationContext<S> | undefined;
33 private readonly _parent: ActivationContext<S> | undefined;
30
34
@@ -36,7 +40,7 export class ActivationContext<S extends
36 * @param service the service to activate, this parameter is used for the
40 * @param service the service to activate, this parameter is used for the
37 * debug purpose.
41 * debug purpose.
38 */
42 */
39 constructor(containerLifetimeManager: LifetimeManager, services: Partial<RegistrationMap<S>>, name: string, service: Descriptor<S, unknown>, cache = {}) {
43 constructor(containerLifetimeManager: ILifetimeManager, services: DescriptorMap<S>, name: string, service: Descriptor<S, unknown>, cache = {}) {
40 this._name = name;
44 this._name = name;
41 this._service = service;
45 this._service = service;
42 this._cache = cache;
46 this._cache = cache;
@@ -53,19 +57,9 export class ActivationContext<S extends
53 return this._containerLifetimeManager.create<T>();
57 return this._containerLifetimeManager.create<T>();
54 }
58 }
55
59
56 /** Resolves the specified dependency in the current context
57 * @param name The name of the dependency being resolved
58 */
59 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
60 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
60 /** Resolves the specified dependency with the specified default value if
61 * the dependency is missing.
62 *
63 * @param name The name of the dependency being resolved
64 * @param def A default value to return in case of the specified dependency
65 * is missing.
66 */
67 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
61 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
68 resolve<K extends keyof S, T>(name: K, def?: T): S[K] | T | undefined {
62 resolve<K extends keyof S, T>(name: K, def?: T): NonNullable<S[K]> | T | undefined {
69 const d = this._services[name];
63 const d = this._services[name];
70
64
71 if (d !== undefined) {
65 if (d !== undefined) {
@@ -84,9 +78,13 export class ActivationContext<S extends
84 * @name{string} the name of the service
78 * @name{string} the name of the service
85 * @service{string} the service descriptor to register
79 * @service{string} the service descriptor to register
86 */
80 */
87 register<K extends ConfigurableKeys<S>>(name: K, service: RegistrationMap<S>[K]) {
81 register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]) {
88 argumentNotNull(name, "name");
82 argumentNotNull(name, "name");
89
83
84 const d = this._services[name];
85 if (d !== undefined && !d.configurable)
86 throw new Error(`Service ${String(name)} can't be overridden`);
87
90 this._services[name] = service;
88 this._services[name] = service;
91 }
89 }
92
90
@@ -95,7 +93,12 export class ActivationContext<S extends
95 return {
93 return {
96 initialize() {},
94 initialize() { },
97 has: () => id in this._cache,
95 has: () => id in this._cache,
98 get: () => this._cache[id] as T,
96 get: () => {
97 const v = this._cache[id] as T;
98 if (v === undefined || v === null)
99 throw new Error("The value isn't present in the activation context");
100 return v;
101 },
99 store: item => {
102 store: item => {
100 this._cache[id] = item;
103 this._cache[id] = item;
101 }
104 }
@@ -130,7 +133,9 export class ActivationContext<S extends
130 private enter(service: Descriptor<S, unknown>, name: string) {
133 private enter(service: Descriptor<S, unknown>, name: string) {
131 return new ActivationContext(
134 return new ActivationContext(
132 this._containerLifetimeManager,
135 this._containerLifetimeManager,
133 Object.create(this._services) as typeof this._services,
136 service.hasOverrides ?
137 Object.create(this._services) as typeof this._services :
138 this._services,
134 name,
139 name,
135 service,
140 service,
136 this._cache
141 this._cache
@@ -3,13 +3,32 export interface ActivationItem {
3 service: string;
3 service: string;
4 }
4 }
5
5
6 /**
7 * Contains information about the error which occurred during service activation.
8 *
9 * Information about activation error includes original exception which has
10 * occurred, the name of the service being activated and activation stack of
11 * services.
12 */
6 export class ActivationError {
13 export class ActivationError {
14 /**
15 * Stack of services being activating
16 */
7 readonly activationStack: ActivationItem[];
17 readonly activationStack: ActivationItem[];
8
18
19 /**
20 * The name of the failed service
21 */
9 readonly service: string;
22 readonly service: string;
10
23
24 /**
25 * The exception which occurred during activation of the service
26 */
11 readonly innerException: unknown;
27 readonly innerException: unknown;
12
28
29 /**
30 * Error message
31 */
13 readonly message: string;
32 readonly message: string;
14
33
15 constructor(service: string, activationStack: ActivationItem[], innerException: unknown) {
34 constructor(service: string, activationStack: ActivationItem[], innerException: unknown) {
@@ -1,33 +1,54
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { ActivationError } from "./ActivationError";
2 import { ActivationError } from "./ActivationError";
3 import { ContainerBuilder } from "./ContainerBuilder";
3 import { ContainerBuilder } from "./ContainerBuilder";
4 import { RegistrationMap, ServiceContainer, ContainerServices, ServiceLocator, IContainerBuilder, Configurable, ContainerKeys} from "./interfaces";
4 import { DescriptorMap, IContainerBuilder, IDestroyable, ILifetimeManager, ServiceLocator } from "./interfaces";
5 import { LifetimeManager } from "./LifetimeManager";
6
5
7 export class Container<S extends Configurable<S>> implements ServiceContainer<S> {
6 export class Container<S> implements ServiceLocator<S>, IDestroyable {
8 private readonly _services: Partial<RegistrationMap<ContainerServices<S>>>;
7 private readonly _services: DescriptorMap<S>;
9
8
10 private readonly _lifetimeManager: LifetimeManager;
9 private readonly _lifetimeManager: ILifetimeManager;
11
10
12 private _disposed: boolean;
11 private _disposed: boolean;
13
12
14 constructor(services: Partial<RegistrationMap<ContainerServices<S>>>, lifetimeManager: LifetimeManager) {
13 private readonly _onDestroyed: () => void;
15 this._services = services;
14
16 this._services.container = { activate: () => this as ContainerServices<S>["container"]};
15 constructor(services: DescriptorMap<S>, lifetimeManager: ILifetimeManager, destroyed: () => void) {
17 this._services.childContainer = { activate: () => this.createChildContainer() as ContainerServices<S>["childContainer"] };
16 this._services = {
17 ...services,
18 container: {
19 configurable: false,
20 activate: () => this
21 },
22 childContainer: {
23 configurable: false,
24 activate: () => this.createChildBuilder(),
25 }
26 };
27
18 this._disposed = false;
28 this._disposed = false;
19 this._lifetimeManager = lifetimeManager;
29 this._lifetimeManager = lifetimeManager;
30 this._onDestroyed = destroyed;
31
20 }
32 }
21
33
22 createChildContainer(): IContainerBuilder<S> {
34 private _assertNotDestroyed() {
23 return new ContainerBuilder(this._services);
35 if (this._disposed)
36 throw new Error("The container is destroyed");
24 }
37 }
25
38
26 resolve<K extends keyof ContainerServices<S>>(name: K): NonNullable<ContainerServices<S>[K]>;
39 createChildBuilder(): IContainerBuilder<S, keyof S> {
27 resolve<K extends keyof ContainerServices<S>, T>(name: K, def: T): NonNullable<ContainerServices<S>[K]> | T;
40 this._assertNotDestroyed();
28 resolve<K extends keyof ContainerServices<S>, T>(name: K, def?: T) {
41
29 // TODO: add logging
42 const lifetime = this._lifetimeManager.create<IDestroyable>();
30 // trace.debug("resolve {0}", name);
43
44 return new ContainerBuilder(this._services, lifetime);
45 }
46
47 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
48 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
49 resolve<K extends keyof S, T>(name: K, def?: T) {
50 this._assertNotDestroyed();
51
31 const d = this._services[name];
52 const d = this._services[name];
32 if (d === undefined) {
53 if (d === undefined) {
33 if (arguments.length > 1)
54 if (arguments.length > 1)
@@ -35,7 +56,6 export class Container<S extends Configu
35 else
56 else
36 throw new Error(`Service '${String(name)}' isn't found`);
57 throw new Error(`Service '${String(name)}' isn't found`);
37 } else {
58 } else {
38
39 const context = new ActivationContext(this._lifetimeManager, this._services, String(name), d);
59 const context = new ActivationContext(this._lifetimeManager, this._services, String(name), d);
40 try {
60 try {
41 return d.activate(context);
61 return d.activate(context);
@@ -44,11 +64,11 export class Container<S extends Configu
44 }
64 }
45 }
65 }
46 }
66 }
47
48 destroy() {
67 destroy() {
49 if (this._disposed)
68 if (this._disposed)
50 return;
69 return;
51 this._disposed = true;
70 this._disposed = true;
52 this._lifetimeManager.destroy();
71 this._lifetimeManager.destroy();
72 (0,this._onDestroyed)();
53 }
73 }
54 }
74 }
@@ -1,41 +1,62
1 import { Container } from "./Container";
1 import { Container } from "./Container";
2 import { DescriptorBuilder } from "./DescriptorBuilder";
2 import { DescriptorBuilder } from "./DescriptorBuilder";
3 import { Configurable, ConfigurableKeys, ContainerServices, Descriptor, IContainerBuilder, IDescriptorBuilder, RegistrationMap, ServiceContainer } from "./interfaces";
3 import { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable } from "./interfaces";
4 import { LifetimeManager } from "./LifetimeManager";
4 import { emptyLifetime, LifetimeManager } from "./LifetimeManager";
5 import { isDestroyable } from "./traits";
5
6
6 export class ContainerBuilder<S extends Configurable<S>> implements IContainerBuilder<S>{
7 /**
8 * Container builder used to prepare service descriptors and create a IoC container
9 */
10 export class ContainerBuilder<S, U extends keyof S> implements
11 IContainerBuilder<S, U> {
7
12
8 private _pending = 1;
13 private _pending = 1;
9
14
10 private readonly _services: Partial<RegistrationMap<ContainerServices<S>>>;
15 private readonly _services: DescriptorMap<S>;
11
16
12 private readonly _lifetimeManager = new LifetimeManager();
17 private readonly _lifetimeManager = new LifetimeManager();
13
18
14 constructor(parentServices?: object) {
19 private readonly _lifetime: ILifetime<IDestroyable>;
15 this._services = Object.create(parentServices ? parentServices : null) as object;
20
21 constructor(parentServices?: DescriptorMap<S>, lifetime?: ILifetime<IDestroyable>) {
22 this._services = Object.create(parentServices ? parentServices : null) as DescriptorMap<S>;
23 this._lifetimeManager = new LifetimeManager();
24 this._lifetime = lifetime ?? emptyLifetime();
25 }
26 createServiceBuilder<K extends U>(name: K):
27 IDescriptorBuilder<S, S[K], Record<never, never>, U> {
28
29 return new DescriptorBuilder(this._lifetimeManager, this._register(name), this._fail);
30
16 }
31 }
17
32
18 createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<ContainerServices<S>, NonNullable<S[K]>, object, keyof S> {
33 build(): ServiceLocator<S> {
19 return new DescriptorBuilder(this._lifetimeManager, this._register(name), this._fail);
20 }
21
22 build(): ServiceContainer<S> {
23 this._assertBuilding();
34 this._assertBuilding();
24 if(!this._complete())
35 if (!this._complete())
25 throw new Error("The configuration didn't complete.");
36 throw new Error("The configuration didn't complete.");
26 return new Container(this._services, this._lifetimeManager);
37
38 const lifetime = this._lifetime;
39
40 const detach = isDestroyable(lifetime) ? () => lifetime.destroy() : () => void (0);
41
42 const container = new Container(this._services, this._lifetimeManager, detach);
43 lifetime.store(container);
44
45 return container;
27 }
46 }
28
47
29 private readonly _register = <K extends ConfigurableKeys<S>>(name: K) => (descriptor: Descriptor<S, NonNullable<S[K]>>) => {
48 private readonly _register = <K extends U>(name: K) =>
49 (descriptor: Descriptor<S, S[K]>) => {
30 this._complete();
50 this._complete();
31 this._services[name] = descriptor;
51 this._services[name] = descriptor;
32 };
52 };
33
53
34 private readonly _fail = (ex: unknown) => {
54 private readonly _fail = (ex: unknown) => {
35
55 throw ex;
36 };
56 };
37
57
38 private _assertBuilding() {
58 private _assertBuilding() {
59 if (!this._pending)
39 throw new Error("The descriptor builder is finalized");
60 throw new Error("The descriptor builder is finalized");
40 }
61 }
41
62
@@ -1,28 +1,28
1 import { RegistrationBuilder, LifetimeContainer, ConfigurableKeys, IDescriptorBuilder, Ref, Resolved, DepsMap } from "./interfaces";
1 import { BuildDescriptorFn, IDescriptorBuilder, DepsMap, Resolve, DescriptorMap } 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 } from "./DescriptorImpl";
4 import { LifetimeManager } from "./LifetimeManager";
4 import { contextLifetime, emptyLifetime, hierarchyLifetime, LifetimeManager, singletonLifetime } from "./LifetimeManager";
5 import { each, isKey, isPromise, isString, key, oid } from "./traits";
5 import { each, 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, R extends object, O extends keyof S> implements IDescriptorBuilder<S, T, R, O> {
11 export class DescriptorBuilder<S, T, R, U extends keyof S> implements IDescriptorBuilder<S, T, R, U> {
12 private readonly _lifetimeManager: LifetimeManager;
12 private readonly _lifetimeManager: LifetimeManager;
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>;
17 private readonly _refs: DepsMap<R>;
18
18
19 private _lifetime = LifetimeManager.empty<T>();
19 private _lifetime = emptyLifetime<T>();
20
20
21 private _overrides: RegistrationOverridesMap<S>;
21 private _overrides: DescriptorMap<S>;
22
22
23 private _cleanup?: (item: T) => void;
23 private _cleanup?: (item: T) => void;
24
24
25 private _factory?: (refs: R) => T;
25 private _factory?: (refs: R) => NonNullable<T>;
26
26
27 private _pending = 1;
27 private _pending = 1;
28
28
@@ -45,24 +45,20 export class DescriptorBuilder<S extends
45 this._overrides = {};
45 this._overrides = {};
46 this._refs = {};
46 this._refs = {};
47 }
47 }
48 wants<X extends { [k in Exclude<key, keyof R>]: keyof S | Ref<keyof S, boolean, unknown>; }>(refs: X):
48
49 IDescriptorBuilder<S, T, R & {
49 /** Declares dependencies to be consumed in the factory method */
50 [k in keyof X]:
50 wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X):
51 X[k] extends keyof S ? NonNullable<S[X[k]]> :
51 IDescriptorBuilder<S, T, R & { [k in keyof X]: Resolve<S, X[k]>; }, U> {
52 X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> :
53 never;
54 }, O> {
55
52
56 each(refs, (v, k) => this._refs[k] = v);
53 each(refs, (v, k) => this._refs[k] = v);
57
54
58 return this as IDescriptorBuilder<S, T, R & {
55 return this as IDescriptorBuilder<S, T, R & {
59 [k in keyof X]:
56 [k in keyof X]: Resolve<S, X[k]>;
60 X[k] extends keyof S ? NonNullable<S[X[k]]> :
57 }, U>;
61 X[k] extends Ref<infer K, infer L, infer D> ? Resolved<S, K & keyof S, L, D> :
62 never;
63 }, O>;
64 }
58 }
65 factory(f: (refs: R) => T): void {
59
60 /** Registers a factory method for the service */
61 factory(f: (refs: R) => NonNullable<T>): void {
66 this._assertBuilding();
62 this._assertBuilding();
67 this._factory = f;
63 this._factory = f;
68 this._finalize();
64 this._finalize();
@@ -79,19 +75,19 export class DescriptorBuilder<S extends
79 this._finalized = true;
75 this._finalized = true;
80 }
76 }
81
77
82 override<K extends O>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
78 override<K extends U>(name: K, builder: BuildDescriptorFn<S, NonNullable<S[K]>, U>): this;
83 override<K extends O>(services: { [k in K]: RegistrationBuilder<S, NonNullable<S[k]>> }): this;
79 override<K extends U>(services: { [k in K]: BuildDescriptorFn<S, NonNullable<S[k]>, U> }): this;
84 override<K extends O>(nameOrServices: K | { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }, builder?: RegistrationBuilder<S, NonNullable<S[K]>>): this {
80 override<K extends U>(nameOrServices: K | { [name in K]: BuildDescriptorFn<S, NonNullable<S[K]>, U> }, builder?: BuildDescriptorFn<S, NonNullable<S[K]>, U>): this {
85 this._assertBuilding();
81 this._assertBuilding();
86 const guard = (v: void | Promise<void>) => {
82 const guard = (v: void | Promise<void>) => {
87 if (isPromise(v))
83 if (isPromise(v))
88 v.catch(err => this._fail(err));
84 v.catch(err => this._fail(err));
89 };
85 };
90
86
91 if (isKey(nameOrServices)) {
87 if (typeof nameOrServices !== "object") {
92 if (builder) {
88 if (builder) {
93 this._defer();
89 this._defer();
94 const d = new DescriptorBuilder<S, NonNullable<S[K]>, object, O>(
90 const d = new DescriptorBuilder<S, NonNullable<S[K]>, object, U>(
95 this._lifetimeManager,
91 this._lifetimeManager,
96 result => {
92 result => {
97 this._overrides[nameOrServices] = result;
93 this._overrides[nameOrServices] = result;
@@ -112,7 +108,13 export class DescriptorBuilder<S extends
112 return this;
108 return this;
113 }
109 }
114
110
111 /** Specified the singleton lifetime for the service */
115 lifetime(lifetime: "singleton", typeId: string): this;
112 lifetime(lifetime: "singleton", typeId: string): this;
113 /**
114 * Specifies the lifetime for the service, either {@linkcode ILifetime<T>}
115 * object or {@linkcode ActivationType} literal.
116 * @param lifetime
117 */
116 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
118 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
117 lifetime(lifetime: ILifetime<T> | ActivationType, typeId?: string): this {
119 lifetime(lifetime: ILifetime<T> | ActivationType, typeId?: string): this {
118 this._assertBuilding();
120 this._assertBuilding();
@@ -124,13 +126,17 export class DescriptorBuilder<S extends
124 return this;
126 return this;
125 }
127 }
126
128
129 /** Registers cleanup callback, used when lifetime of the instance is managed
130 * by the container or some external mechanism
131 */
127 cleanup(cb: (item: T) => void): this {
132 cleanup(cb: (item: T) => void): this {
128 this._assertBuilding();
133 this._assertBuilding();
129 this._cleanup = cb;
134 this._cleanup = cb;
130 return this;
135 return this;
131 }
136 }
132
137
133 value(v: T): void {
138 /** Registers a value as the instance of the service */
139 value(v: NonNullable<T>): void {
134 this._assertBuilding();
140 this._assertBuilding();
135 this._cb({
141 this._cb({
136 activate() {
142 activate() {
@@ -145,19 +151,17 export class DescriptorBuilder<S extends
145 case "container":
151 case "container":
146 return this._lifetimeManager.create();
152 return this._lifetimeManager.create();
147 case "hierarchy":
153 case "hierarchy":
148 return LifetimeManager.hierarchyLifetime();
154 return hierarchyLifetime();
149 case "context":
155 case "context":
150 return LifetimeManager.contextLifetime();
156 return contextLifetime();
151 case "singleton": {
157 case "singleton": {
152 if (!typeId)
158 if (!typeId)
153 throw Error("The singleton activation requires a typeId");
159 throw Error("The singleton activation requires a typeId");
154
155 const _oid = isString(typeId) ? typeId : oid(typeId);
160 const _oid = isString(typeId) ? typeId : oid(typeId);
156
161 return singletonLifetime(_oid);
157 return LifetimeManager.singletonLifetime(_oid);
158 }
162 }
159 default:
163 default:
160 return LifetimeManager.empty();
164 return emptyLifetime();
161 }
165 }
162 }
166 }
163
167
@@ -172,7 +176,7 export class DescriptorBuilder<S extends
172
176
173 this._cb(new DescriptorImpl<S, T>({
177 this._cb(new DescriptorImpl<S, T>({
174 lifetime: this._lifetime,
178 lifetime: this._lifetime,
175 factory: this._factory,
179 factory: this._factory as (refs: Record<key, unknown>) => NonNullable<T>,
176 overrides: this._overrides,
180 overrides: this._overrides,
177 cleanup: this._cleanup
181 cleanup: this._cleanup
178 }));
182 }));
@@ -1,32 +1,32
1 import { Descriptor, ILifetime, DepsMap, Ref, IActivationContext } from "./interfaces";
1 import { Descriptor, ILifetime, DepsMap, IActivationContext, DescriptorMap } from "./interfaces";
2 import { each, isKey, key } from "./traits";
2 import { each, key } from "./traits";
3
3
4 export type RegistrationOverridesMap<S extends object> = { [k in keyof S]?: Descriptor<S, NonNullable<S[k]>> };
4 export interface DescriptorImplArgs<S, T> {
5
6 export interface DescriptorImplArgs<S extends object, T> {
7 lifetime: ILifetime<T>;
5 lifetime: ILifetime<T>;
8
6
9 factory: (refs: Record<key, never>) => T;
7 factory: (refs: Record<key, unknown>) => NonNullable<T>;
10
8
11 cleanup?: (item: T) => void;
9 cleanup?: (item: NonNullable<T>) => void;
12
10
13 overrides?: RegistrationOverridesMap<S>;
11 overrides?: DescriptorMap<S>;
14
12
15 dependencies?: DepsMap<key, keyof S>;
13 dependencies?: DepsMap<S>;
16 }
14 }
17
15
18
16
19 export class DescriptorImpl<S extends object, T> implements Descriptor<S, T> {
17 export class DescriptorImpl<S, T> implements Descriptor<S, T> {
20
18
21 private readonly _overrides?: RegistrationOverridesMap<S>;
19 private readonly _overrides?: DescriptorMap<S>;
22
20
23 private readonly _lifetime: ILifetime<T>;
21 private readonly _lifetime: ILifetime<T>;
24
22
25 private readonly _factory: (refs: Record<key, never>) => T;
23 private readonly _factory: (refs: Record<key, unknown>) => NonNullable<T>;
24
25 private readonly _cleanup?: (item: NonNullable<T>) => void;
26
26
27 private readonly _cleanup?: (item: T) => void;
27 private readonly _deps?: DepsMap<S>;
28
28
29 private readonly _deps?: DepsMap<key, keyof S>;
29 readonly hasOverrides: boolean;
30
30
31 constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs<S, T>) {
31 constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs<S, T>) {
32 this._lifetime = lifetime;
32 this._lifetime = lifetime;
@@ -37,9 +37,11 export class DescriptorImpl<S extends ob
37 this._overrides = overrides;
37 this._overrides = overrides;
38 if (dependencies)
38 if (dependencies)
39 this._deps = dependencies;
39 this._deps = dependencies;
40
41 this.hasOverrides = !!overrides;
40 }
42 }
41
43
42 activate(context: IActivationContext<S>): T {
44 activate(context: IActivationContext<S>): NonNullable<T> {
43
45
44 if (this._lifetime.has())
46 if (this._lifetime.has())
45 return this._lifetime.get();
47 return this._lifetime.get();
@@ -49,7 +51,7 export class DescriptorImpl<S extends ob
49 if (this._overrides)
51 if (this._overrides)
50 each(this._overrides, (v, k) => context.register(k, v));
52 each(this._overrides, (v, k) => context.register(k, v));
51
53
52 const resolve = <K extends keyof S, L extends boolean, D = never>({ name, lazy, ...opts }: Ref<K, L, D>) => {
54 const resolve = <K extends keyof S>({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K] | null; }) => {
53 if (lazy) {
55 if (lazy) {
54 return () => "default" in opts ? context.resolve(name, opts.default) : context.resolve(name);
56 return () => "default" in opts ? context.resolve(name, opts.default) : context.resolve(name);
55 } else {
57 } else {
@@ -61,12 +63,12 export class DescriptorImpl<S extends ob
61 Object.keys(deps)
63 Object.keys(deps)
62 .map(k => {
64 .map(k => {
63 const ref = deps[k];
65 const ref = deps[k];
64 return isKey(ref) ?
66 return typeof ref !== "object" ?
65 { [k]: resolve({ name: ref }) } :
67 { [k]: resolve({ name: ref }) } :
66 { [k]: resolve(ref) };
68 { [k]: resolve(ref) };
67 })
69 })
68 .reduce((a, p) => ({ ...a, ...p }), {} ) as Record<key, never>:
70 .reduce((a, p) => ({ ...a, ...p }), {} ):
69 {} as Record<key, never>;
71 {};
70
72
71 const instance = this._factory.call(undefined, makeRefs(this._deps));
73 const instance = this._factory.call(undefined, makeRefs(this._deps));
72
74
@@ -1,19 +1,19
1 import { DescriptorBuilder } from "./DescriptorBuilder";
1 import { ConfigurationMap, ConfigurationMapConstraint, ContainerServices, ContainerServicesConstraint, ExtractRequiredKeys, IContainerBuilder } from "./interfaces";
2 import { ConfigurableKeys, ContainerServices, RegistrationBuildersMap, ExtractRequired, IContainerBuilder } from "./interfaces";
3 import { ServiceContainer } from "./interfaces";
4 import { argumentNotNull, each, isKey } from "./traits";
2 import { argumentNotNull, each, isKey } from "./traits";
5
3
6 export class FluentConfiguration<S, Y extends ConfigurableKeys<S> = ConfigurableKeys<S>> {
4 type ContainerExtensionConstraint<X, S> = ContainerServicesConstraint<Pick<S, keyof X & keyof S>>;
7
5
8 private _builders: Partial<RegistrationBuildersMap<S>> = {};
6 export class FluentConfiguration<S extends ContainerServicesConstraint<S>, Y extends keyof S = keyof S> {
7
8 private _builders: Partial<ConfigurationMap<ContainerServices<S>, keyof S, keyof 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 Pick<S, keyof D & keyof S>>(): FluentConfiguration<S & D, Y | ConfigurableKeys<D>> {
15 declare<D extends ContainerExtensionConstraint<D,S>>(): FluentConfiguration<S & D, Y | keyof D> {
16 return this as FluentConfiguration<S & D, Y | ConfigurableKeys<D>>;
16 return this as unknown as FluentConfiguration<S & D, Y | keyof 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
@@ -22,7 +22,7 export class FluentConfiguration<S, Y ex
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 unknown as FluentConfiguration<S & P, Exclude<Y, keyof P>>;
26 }
26 }
27
27
28 /** Register the service.
28 /** Register the service.
@@ -31,13 +31,13 export class FluentConfiguration<S, Y ex
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: ConfigurationMap<ContainerServices<S>,Y, keyof 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<X extends ConfigurationMapConstraint<ContainerServices<S>, Y, keyof X>>(config: X): FluentConfiguration<S, Exclude<Y, keyof X>>;
40 register<K extends Y>(nameOrConfig: K | RegistrationBuildersMap<S, K>, builder?: RegistrationBuildersMap<S>[K]) {
40 register<K extends Y>(nameOrConfig: K | ConfigurationMap<ContainerServices<S>, K, keyof S>, builder?: ConfigurationMap<ContainerServices<S>, Y, keyof 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;
@@ -45,7 +45,7 export class FluentConfiguration<S, Y ex
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 /**
@@ -57,15 +57,16 export class FluentConfiguration<S, Y ex
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(...args: ExtractRequiredKeys<S, Y> extends never ? [] : [services: {[ k in ExtractRequiredKeys<S, Y>]: "required"}]) {
61 //done() {
61 return this;
62 return this;
62 }
63 }
63
64
64 configure<T extends IContainerBuilder<S>>(builder: T) {
65 configure<C extends IContainerBuilder<ContainerServices<S>, keyof S>>(builder: C) {
65 each(this._builders, (v, k) => {
66 each(this._builders, (v, k) => {
66 v(builder.createServiceBuilder(k));
67 v(builder.createServiceBuilder(k));
67 });
68 });
68 builder.build() as T & ServiceContainer<S>;
69 return builder.build();
69 }
70 }
70
71
71 }
72 }
@@ -1,4 +1,4
1 import { IActivationContext, IDestroyable, ILifetime } from "./interfaces";
1 import { IActivationContext, ILifetime, ILifetimeManager } from "./interfaces";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { argumentNotNull, isDestroyable } from "./traits";
3 import { argumentNotNull, isDestroyable } from "./traits";
4
4
@@ -10,7 +10,7 const safeCall = (item: () => void) => {
10 }
10 }
11 };
11 };
12
12
13 const emptyLifetime = Object.freeze({
13 const _emptyLifetime = Object.freeze({
14 has() {
14 has() {
15 return false;
15 return false;
16 },
16 },
@@ -32,7 +32,7 const emptyLifetime = Object.freeze({
32
32
33 });
33 });
34
34
35 const unknownLifetime = Object.freeze({
35 const _unknownLifetime = Object.freeze({
36 has() {
36 has() {
37 return false;
37 return false;
38 },
38 },
@@ -45,6 +45,7 const unknownLifetime = Object.freeze({
45 store() {
45 store() {
46 throw new Error("Can't store a value in the unknown lifetime object");
46 throw new Error("Can't store a value in the unknown lifetime object");
47 },
47 },
48
48 toString() {
49 toString() {
49 return `[object UnknownLifetime]`;
50 return `[object UnknownLifetime]`;
50 }
51 }
@@ -54,23 +55,23 let nextId = 0;
54
55
55 const singletons: { [K:string]: unknown} = {};
56 const singletons: { [K: string]: unknown } = {};
56
57
57 export class LifetimeManager implements IDestroyable {
58 export class LifetimeManager implements ILifetimeManager {
58 private _cleanup: (() => void)[] = [];
59 private _cleanup: (() => void)[] = [];
59 private readonly _cache: {[K: string]: unknown} = {};
60 private readonly _cache: { [K: string]: unknown } = {};
60 private _destroyed = false;
61 private _destroyed = false;
61
62
62 private readonly _pending: {[K: string]: unknown} = {};
63 private readonly _pending: { [K: string]: unknown } = {};
63
64
64 create<T>(): ILifetime<T> {
65 create<T>(): ILifetime<T> & { remove(): void; } {
65 const id = ++nextId;
66 const id = ++nextId;
66 return {
67 return {
67 has: () => id in this._cache,
68 has: () => id in this._cache,
68
69
69 get: () => {
70 get: () => {
70 const t = this._cache[id];
71 const t = this._cache[id] as T;
71 if (t === undefined)
72 if (t === undefined || t === null)
72 throw new Error(`The item with with the key ${id} isn't found`);
73 throw new Error(`The item with with the key ${id} isn't found`);
73 return t as T;
74 return t;
74 },
75 },
75
76
76 initialize: () => {
77 initialize: () => {
@@ -79,23 +80,27 export class LifetimeManager implements
79 this._pending[id] = true;
80 this._pending[id] = true;
80 },
81 },
81
82
82 store: (item: T, cleanup?: (item: T) => void) => {
83 store: (item: NonNullable<T>, cleanup?: (item: NonNullable<T>) => void) => {
83 argumentNotNull(id, "id");
84 argumentNotNull(item, "item");
85
86 if (id in this._cache)
84 if (id in this._cache)
87 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
85 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
86 if (this._destroyed)
87 throw new Error("Lifetime manager is destroyed");
88
88 delete this._pending[id];
89 delete this._pending[id];
89
90
90 this._cache[id] = item;
91 this._cache[id] = item;
91
92
92 if (this._destroyed)
93 throw new Error("Lifetime manager is destroyed");
94 if (cleanup) {
93 if (cleanup) {
95 this._cleanup.push(() => cleanup(item));
94 this._cleanup.push(() => cleanup(item));
96 } else if (isDestroyable(item)) {
95 } else if (isDestroyable(item)) {
97 this._cleanup.push(() => item.destroy());
96 this._cleanup.push(() => item.destroy());
98 }
97 }
98 },
99
100 remove: () => {
101 if (this._pending[id])
102 throw new Error(`The item '${id}' can't be removed before it has been stored`);
103 delete this._cache[id];
99 }
104 }
100 };
105 };
101 }
106 }
@@ -108,15 +113,17 export class LifetimeManager implements
108 }
113 }
109 }
114 }
110
115
111 static empty<T>(): ILifetime<T> {
112 return emptyLifetime;
113 }
116 }
114
117
115 static hierarchyLifetime<T>() {
118 export const emptyLifetime = <T>(): ILifetime<T> => {
116 let _lifetime: ILifetime<T> = unknownLifetime;
119 return _emptyLifetime;
120 };
121
122 export const hierarchyLifetime = <T>(): ILifetime<T> => {
123 let _lifetime: ILifetime<T> = _unknownLifetime;
117 return {
124 return {
118 initialize(context: IActivationContext<object>) {
125 initialize(context: IActivationContext<object>) {
119 if (_lifetime !== unknownLifetime)
126 if (_lifetime !== _unknownLifetime)
120 throw new Error("Cyclic reference activation detected");
127 throw new Error("Cyclic reference activation detected");
121
128
122 _lifetime = context.createContainerLifetime<T>();
129 _lifetime = context.createContainerLifetime<T>();
@@ -127,20 +134,20 export class LifetimeManager implements
127 has() {
134 has() {
128 return _lifetime.has();
135 return _lifetime.has();
129 },
136 },
130 store(item: T, cleanup?: (item: T) => void) {
137 store(item: NonNullable<T>, cleanup?: (item: NonNullable<T>) => void) {
131 return _lifetime.store(item, cleanup);
138 return _lifetime.store(item, cleanup);
132 },
139 },
133 toString() {
140 toString() {
134 return `[object HierarchyLifetime, has=${String(this.has())}]`;
141 return `[object HierarchyLifetime, has=${String(this.has())}]`;
135 }
142 }
136 };
143 };
137 }
144 };
138
145
139 static contextLifetime<T>() {
146 export const contextLifetime = <T>(): ILifetime<T> => {
140 let _lifetime: ILifetime<T> = unknownLifetime;
147 let _lifetime: ILifetime<T> = _unknownLifetime;
141 return {
148 return {
142 initialize(context: ActivationContext<object>) {
149 initialize(context: ActivationContext<object>) {
143 if (_lifetime !== unknownLifetime)
150 if (_lifetime !== _unknownLifetime)
144 throw new Error("Cyclic reference detected");
151 throw new Error("Cyclic reference detected");
145 _lifetime = context.createLifetime();
152 _lifetime = context.createLifetime();
146 },
153 },
@@ -150,16 +157,16 export class LifetimeManager implements
150 has() {
157 has() {
151 return _lifetime.has();
158 return _lifetime.has();
152 },
159 },
153 store(item: T) {
160 store(item: NonNullable<T>) {
154 _lifetime.store(item);
161 _lifetime.store(item);
155 },
162 },
156 toString() {
163 toString() {
157 return `[object ContextLifetime, has=${String(this.has())}]`;
164 return `[object ContextLifetime, has=${String(this.has())}]`;
158 }
165 }
159 };
166 };
160 }
167 };
161
168
162 static singletonLifetime<T>(typeId: string) {
169 export const singletonLifetime = <T>(typeId: string): ILifetime<T> => {
163 argumentNotNull(typeId, "typeId");
170 argumentNotNull(typeId, "typeId");
164 let pending = false;
171 let pending = false;
165 return {
172 return {
@@ -169,14 +176,14 export class LifetimeManager implements
169 get() {
176 get() {
170 if (!this.has())
177 if (!this.has())
171 throw new Error(`The instance ${typeId} doesn't exists`);
178 throw new Error(`The instance ${typeId} doesn't exists`);
172 return singletons[typeId] as T;
179 return singletons[typeId] as NonNullable<T>;
173 },
180 },
174 initialize() {
181 initialize() {
175 if (pending)
182 if (pending)
176 throw new Error("Cyclic reference detected");
183 throw new Error("Cyclic reference detected");
177 pending = true;
184 pending = true;
178 },
185 },
179 store(item: T) {
186 store(item: NonNullable<T>) {
180 singletons[typeId] = item;
187 singletons[typeId] = item;
181 pending = false;
188 pending = false;
182 },
189 },
@@ -184,13 +191,13 export class LifetimeManager implements
184 return `[object SingletonLifetime, has=${String(this.has())}, typeId=${typeId}]`;
191 return `[object SingletonLifetime, has=${String(this.has())}, typeId=${typeId}]`;
185 }
192 }
186 };
193 };
187 }
194 };
188
195
189 static containerLifetime<T>(container: { createLifetime<X>(): ILifetime<X>}) {
196 export const containerLifetime = <T>(container: { createLifetime<X>(): ILifetime<X> }) => {
190 let _lifetime: ILifetime<T> = unknownLifetime;
197 let _lifetime: ILifetime<T> = _unknownLifetime;
191 return {
198 return {
192 initialize() {
199 initialize() {
193 if (_lifetime !== unknownLifetime)
200 if (_lifetime !== _unknownLifetime)
194 throw new Error("Cyclic reference detected");
201 throw new Error("Cyclic reference detected");
195 _lifetime = container.createLifetime();
202 _lifetime = container.createLifetime();
196 },
203 },
@@ -200,12 +207,11 export class LifetimeManager implements
200 has() {
207 has() {
201 return _lifetime.has();
208 return _lifetime.has();
202 },
209 },
203 store(item: T) {
210 store(item: NonNullable<T>) {
204 _lifetime.store(item);
211 _lifetime.store(item);
205 },
212 },
206 toString() {
213 toString() {
207 return `[object ContainerLifetime, has=${String(_lifetime.has())}]`;
214 return `[object ContainerLifetime, has=${String(_lifetime.has())}]`;
208 }
215 }
209 };
216 };
210 }
217 };
211 }
@@ -11,8 +11,8 export interface Resolver<S> {
11 /**
11 /**
12 * Функция для разрешения зависимостей, поддерживает создание фабричных методов,
12 * Функция для разрешения зависимостей, поддерживает создание фабричных методов,
13 * отложенную активацию и значение по-умолчанию для сервисов
13 * отложенную активацию и значение по-умолчанию для сервисов
14 * @template K Ключ сервиса из {@link S}
14 * @template K Ключ сервиса из {@linkcode S}
15 * @template O Тип параметра {@link opts} используется для выведения типа
15 * @template O Тип параметра {@linkcode opts} используется для выведения типа
16 * возвращаемого значения.
16 * возвращаемого значения.
17 * @param name Ключ сервиса, который будет разрешен.
17 * @param name Ключ сервиса, который будет разрешен.
18 * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
18 * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
@@ -23,8 +23,8 export interface Resolver<S> {
23 * @returns Либо фабричный метод для получения зависимости, либо значение зависимости
23 * @returns Либо фабричный метод для получения зависимости, либо значение зависимости
24 * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
24 * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
25 */
25 */
26 <K extends keyof S, O extends { lazy: true; default?: unknown }>(name: K, opts?: O): () => (O extends { default: infer T } ? T : never) | NonNullable<S[K]>;
26 <K extends keyof S, O extends { lazy: true; }>(name: K, opts?: O): () => NonNullable<S[K]> | InferDefault<O>;
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]>;
27 <K extends keyof S, O extends { lazy?: false; }>(name: K, opts?: O): NonNullable<S[K]> | InferDefault<O>;
28 }
28 }
29
29
30 export type DepsMap<S> = {
30 export type DepsMap<S> = {
@@ -32,26 +32,52 export type DepsMap<S> = {
32 };
32 };
33
33
34 export type Refs<S> = {
34 export type Refs<S> = {
35 [k in keyof S]: Ref<k, boolean, S[k]>;
35 [k in keyof S]: Ref<k, S[k]>;
36 }[keyof S];
36 }[keyof S];
37
37
38 export type Ref<K extends key, L extends boolean, D> = { name: K, lazy?: L, default?: D | null };
38 export type Ref<K extends key, D> = {
39 /** The name of the service */
40 name: K;
41
42 /** Make a lazy reference, the resolved dependency will be a function */
43 lazy?: boolean;
44
45 /** The default value for the case where the service isn't defined.
46 * When specified the dependency becomes optional, the default value can be
47 * `null` or `undefined`
48 */
49 default?: D | null
50 };
39
51
40 export type Lazy<T, L extends boolean> = L extends true ? () => T : T;
52 export type Lazy<T, L extends boolean> = L extends true ? () => T : T;
41
53
54 /** Возвращает тип свойства `default` в типе {@link T} */
42 export type InferDefault<T> = T extends { default: infer D } ? D : never;
55 export type InferDefault<T> = T extends { default: infer D } ? D : never;
43
56
57 export type InferLazy<R> = R extends { lazy: infer L } ?
58 L extends true ? true : false :
59 false;
44 export type Resolve<S, R> =
60 export type Resolve<S, R> =
45 R extends keyof S ? NonNullable<S[R]> :
61 R extends keyof S ? NonNullable<S[R]> :
46 R extends Ref<infer K, infer L, unknown> ?
62 R extends Ref<infer K, unknown> ?
47 K extends keyof S ? Lazy<NonNullable<S[K]> | InferDefault<R>, L> :
63 K extends keyof S ?
64 Lazy<NonNullable<S[K]> | InferDefault<R>, InferLazy<R>> :
48 never :
65 never :
49 never;
66 never;
50
67
51 /**
68 /**
52 * Интерфейс для конфигурирования сервиса в контейнере
69 * Интерфейс для конфигурирования сервиса в контейнере. Конфигурирование сервиса
70 * состоит из настройки различных параметров вызовами методов {@linkcode wants},
71 * {@linkcode lifetime}, {@linkcode override}, {@linkcode cleanup}. Завершение настройки
72 * сервиса осуществляется вызовом одного из методов {@linkcode factory} либо
73 * {@linkcode value}.
74 *
75 * @template S Карта сервисов контейнера, доступных при описании дескриптора
76 * @template T Тип сервиса
77 * @template R Карта зависимостей, которая передается параметром фабрике
78 * @template U Имена пользовательских сервисов, доступных для переопределения
53 */
79 */
54 export interface IDescriptorBuilder<S, T, R, O extends keyof S> {
80 export interface IDescriptorBuilder<S, T, R, U extends keyof S> {
55
81
56 /** Указывает фабрика для создания экземпляра сервиса, фабрика передается
82 /** Указывает фабрика для создания экземпляра сервиса, фабрика передается
57 * в виде параметра. При вызове фабрике будет передан объект с зависимостями,
83 * в виде параметра. При вызове фабрике будет передан объект с зависимостями,
@@ -59,9 +85,9 export interface IDescriptorBuilder<S, T
59 *
85 *
60 * Вызов данного метода завершает конфигурирование сервиса.
86 * Вызов данного метода завершает конфигурирование сервиса.
61 *
87 *
62 * @param f Фабрика для создания экземпляра сервиса
88 * @param f Фабрика для создания экземпляра сервиса.
63 */
89 */
64 factory(f: (refs: R) => T): void;
90 factory(f: (refs: R) => NonNullable<T>): void;
65
91
66 /**
92 /**
67 * Используется для указания зависимостей, которые потребуются фабричному
93 * Используется для указания зависимостей, которые потребуются фабричному
@@ -78,10 +104,10 export interface IDescriptorBuilder<S, T
78 wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X):
104 wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X):
79 IDescriptorBuilder<S, T, R & {
105 IDescriptorBuilder<S, T, R & {
80 [k in keyof X]: Resolve<S, X[k]>;
106 [k in keyof X]: Resolve<S, X[k]>;
81 }, O>
107 }, U>
82
108
83 override<K extends O>(name: K, builder: RegistrationBuilder<S, NonNullable<S[K]>>): this;
109 override<K extends U>(name: K, builder: BuildDescriptorFn<S, S[K], U>): this;
84 override<K extends O>(services: { [name in K]: RegistrationBuilder<S, NonNullable<S[K]>> }): this;
110 override<X extends ConfigurationMapConstraint<S, U, keyof X>>(services: X): this;
85
111
86 lifetime(lifetime: "singleton", typeId: string | number | object): this;
112 lifetime(lifetime: "singleton", typeId: string | number | object): this;
87 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
113 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
@@ -96,49 +122,97 export interface IDescriptorBuilder<S, T
96 *
122 *
97 * @param v Экземпляр реализации сервиса.
123 * @param v Экземпляр реализации сервиса.
98 */
124 */
99 value(v: T): void;
125 value(v: NonNullable<T>): void;
100 }
126 }
101
127
102 export type RegistrationBuilder<S, T> = (d: IDescriptorBuilder<S, T, object, ConfigurableKeys<S>>) => void;
128 export type BuildDescriptorFn<S, T, U extends keyof S> = (d: IDescriptorBuilder<S, T, Record<never, never>, U>) => void;
103
129
104 export type RegistrationBuildersMap<S extends Configurable<S>, K extends keyof S = keyof S> = {
130 /**
105 [k in K]-?: RegistrationBuilder<ContainerServices<S>, NonNullable<S[k]>>
131 * Конфигурация контейнера, состоит из набора функций, которые выполняют конфигурацию.
132 *
133 * Все параметры конфигурации являются обязательными, если требуется ввести
134 * необязательные параметры, то нужно ограничить параметр типа {@linkcode K}
135 *
136 * @template S Сервисы доступные в контейнере
137 * @template K Сервисы участвующие в конфигурации
138 */
139 export type ConfigurationMap<S, K extends keyof S, U extends keyof S> = {
140 [k in K]-?: BuildDescriptorFn<S, S[k], U>
141 };
142
143 export type ConfigurationMapConstraint<S, U extends keyof S, X extends string | number | symbol> = {
144 [k in X]-?: k extends U ? BuildDescriptorFn<S, S[k], U> : never;
145 };
146
147 /**
148 * The type constraint useful to restrict type parameters to prevent defining
149 * the services with the {@link ContainerKeys} names.
150 *
151 * The constraint doesn't exclude using this keys but declares them as `never`
152 * which effectively will lead using this keys to the error.
153 */
154 export type ContainerServicesConstraint<S> = {
155 [k in keyof S]: k extends ContainerKeys ? never : S[k];
106 };
156 };
107
157
108 export interface Descriptor<S, T> {
158 export interface Descriptor<S, T> {
109 activate(context: IActivationContext<S>): T;
159
160 /** This flags indicates that this registration can be replaced or overridden. */
161 readonly configurable?: boolean;
162
163 /** If specified signals the activation context that a new service scope
164 * should be created to isolate service overrides.
165 */
166 readonly hasOverrides?: boolean;
167
168 activate(context: IActivationContext<S>): NonNullable<T>;
110 }
169 }
111
170
112 export interface IActivationContext<S> extends ServiceLocator<S> {
171 export interface IActivationContext<S> extends ServiceLocator<S> {
113 createLifetime<T>(): ILifetime<T>;
172 createLifetime<T>(): ILifetime<T>;
114
173
115 createContainerLifetime<T>(): ILifetime<T>;
174 createContainerLifetime<T>(): ILifetime<T>;
116 }
117
175
118 export type RegistrationMap<S, K extends keyof S = keyof S> = {
176 register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]): void;
119 [k in K]-?: Descriptor<S, S[k]>;
120 };
121
122 export interface ContainerProvided<S> {
123 container: ServiceLocator<ContainerServices<S>>;
124
125 childContainer: IContainerBuilder<S>;
126 }
177 }
127
178
128 export type Configurable<S> = { [k in keyof S]: k extends ProvidedKeys ? never : S[k]; };
179 /**
180 * Descriptors map for the specified services {@linkcode S}. All entries are
181 * optional regardless the required or optional services in the original map.
182 *
183 * @template S Сервисы контекста активации
184 * @template U Карта сервисов которые создаются дескрипторами
185 */
186 export type DescriptorMap<S> = {
187 [k in keyof S]?: Descriptor<S, S[k]>;
188 };
129
189
130 export type ProvidedKeys = keyof ContainerProvided<never>;
190 type ContainerKeys = keyof ContainerProvided<object>;
191
192 export type ContainerProvided<S extends ContainerServicesConstraint<S>> = {
193 container: ServiceLocator<ContainerServices<S>>;
194
195 childContainer: IContainerBuilder<ContainerServices<S>, Exclude<keyof S, ContainerKeys>>;
196 };
131
197
132 export type ContainerServices<S> =
133 { [k in keyof S as k extends ProvidedKeys ? never: k]: S[k] } &
134 ContainerProvided<S>;
135
198
136 export type ConfigurableKeys<S> = Exclude<keyof S, ProvidedKeys>;
199 /**
200 * Таблица сервисов, которые предоставляет контейнер.
201 *
202 * Сервисы, предоставляемые контейнером не могут быть null или undefined.
203 */
204 export type ContainerServices<S extends ContainerServicesConstraint<S>> = {
205 [k in keyof S | ContainerKeys]:
206 k extends ContainerKeys ? ContainerProvided<S>[k] :
207 k extends keyof S ? S[k] : never
208 };
137
209
138 export type ConfigurableServices<S> = Pick<S, ConfigurableKeys<S>>;
139
210
140 export type ContainerKeys<S> = keyof S | ProvidedKeys;
211 /**
141
212 * Returns the service declared in the type map {@link S}.
213 *
214 *
215 */
142 export interface ServiceLocator<S> {
216 export interface ServiceLocator<S> {
143 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
217 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
144 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
218 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
@@ -148,17 +222,11 export interface LifetimeContainer {
148 createLifetime<T>(): ILifetime<T>;
222 createLifetime<T>(): ILifetime<T>;
149 }
223 }
150
224
151 export interface ServiceContainer<S> extends
225 export interface IContainerBuilder<S, U extends keyof S> {
152 ServiceLocator<ContainerServices<S>>,
226 createServiceBuilder<K extends U>(name: K):
153 IDestroyable {
227 IDescriptorBuilder<S, S[K], Record<never, never>, U>;
154
228
155 createChildContainer(): IContainerBuilder<S>;
229 build(): ServiceLocator<S>;
156 }
157
158 export interface IContainerBuilder<S> {
159 createServiceBuilder<K extends keyof S>(name: K): IDescriptorBuilder<S, NonNullable<S[K]>, object, keyof S>;
160
161 build(): ServiceContainer<S>;
162 }
230 }
163
231
164
232
@@ -172,12 +240,19 export interface ILifetime<T> {
172 /** Проверяет, что уже создан экземпляр объекта */
240 /** Проверяет, что уже создан экземпляр объекта */
173 has(): boolean;
241 has(): boolean;
174
242
175 get(): T;
243 get(): NonNullable<T>;
244
245 initialize(context: IActivationContext<unknown>): void;
246
247 store(item: NonNullable<T>, cleanup?: (item: NonNullable<T>) => void): void;
176
248
177 initialize(context: IActivationContext<object>): void;
249 toString(): string;
250 }
178
251
179 store(item: T, cleanup?: (item: T) => void): void;
252 export interface ILifetimeManager extends IDestroyable {
253 create<T>(): ILifetime<T>;
180 }
254 }
181
255
182 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
256 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
183
257
258 export type ExtractRequiredKeys<T, K extends keyof T = keyof T> = { [p in K]-?: undefined extends T[p] ? never : p }[K]; No newline at end of file
@@ -1,7 +1,7
1 import { FluentConfiguration } from "./FluentConfiguration";
1 import { FluentConfiguration } from "./FluentConfiguration";
2 import { IDestroyable } from "./interfaces";
2 import { ContainerServices, ContainerServicesConstraint, IDestroyable } from "./interfaces";
3
3
4 export function fluent<S = {}>() {
4 export function fluent<S extends ContainerServicesConstraint<S>>() {
5 return new FluentConfiguration<S>();
5 return new FluentConfiguration<S>();
6 }
6 }
7
7
@@ -2,7 +2,7
2 import { describe, it } from "mocha";
2 import { describe, it } from "mocha";
3 import { Container } from "../Container";
3 import { Container } from "../Container";
4 import { ContainerBuilder } from "../ContainerBuilder";
4 import { ContainerBuilder } from "../ContainerBuilder";
5 import { ConfigurableKeys, ContainerProvided, ContainerServices, DepsMap, Refs, Resolver } from "../interfaces";
5 import { ContainerServices, DepsMap, IContainerBuilder, Refs, Resolver } from "../interfaces";
6 import { fluent } from "../traits";
6 import { fluent } from "../traits";
7
7
8 class Foo {
8 class Foo {
@@ -22,7 +22,9 interface Services {
22
22
23 baz: Foo;
23 baz: Foo;
24
24
25 container: string;
25 box?: Foo;
26
27 //container: string;
26 }
28 }
27
29
28 interface ServicesB {
30 interface ServicesB {
@@ -36,7 +38,7 interface ServicesB {
36
38
37 declare const resolver: Resolver<Services>;
39 declare const resolver: Resolver<Services>;
38
40
39 const foo = resolver("foo", {lazy: true});
41 const foo = resolver("foo", { lazy: true, default: null });
40
42
41 const mmap = <X extends DepsMap<Services>>(m: X) => {};
43 const mmap = <X extends DepsMap<Services>>(m: X) => { };
42
44
@@ -64,37 +66,42 interface SharedServices {
64 baz: Bar;
66 baz: Bar;
65 }
67 }
66
68
67 const config = fluent()
69 const config = fluent<Services>()
68 .declare<Services>()
69 .declare<ServicesB>()
70 .declare<ServicesB>()
70 .register({
71 .register({
71 zoo: it => {},
72 zoo: it => it.value(new Foo()),
72 bar: it => it
73 bar: it => it
73 .lifetime("context") // тип активации, время жизни
74 .lifetime("context") // тип активации, время жизни
74 .wants({
75 .wants({
75 zoo: "zoo", // зависимость
76 self: "container",
77 childContainer: "childContainer",
76 bar: "bar",
78 bar: "bar",
77
79 foo$: { name: "foo", lazy: true } // отложенная активация, фабричный метод
78 $zoo: { name: "foo", lazy: true, } // отложенная активация,
79 //фабричный метод
80 })
80 })
81 .wants({
81 .override({ // переопределение сервиса
82 zoom: "bar"
82 box: it => it.factory(() => new Foo())
83
83 })
84 })
84 .factory(({ $zoo, zoo }) => // фабрика получает объект с именованными зависимостями
85 .factory(({ foo$, bar, self, childContainer }) => // фабрика получает объект с именованными зависимостями
85 // удобно для деструктурирования
86 new Bar(foo$) // создается экземпляр сервиса
86 new Bar($zoo) // создается экземпляр сервиса
87 ),
87 ),
88 foo: it => it.factory(() => new Foo()),
88 foo: it => it.factory(() => new Foo()),
89 baz: it => it.value(new Foo())
89 baz: it => it.value(new Foo()),
90 //box: it => it.factory(() => new Foo())
90 })
91 })
91 .done({});
92 .done();
93
94 declare const container: IContainerBuilder<ContainerServices<Services>, keyof Services>;
92
95
93 declare const container: ContainerBuilder<{}>;
96 const v = container.build().resolve("foo");
97 if (v) {
98 // noop
99 }
100
94 const c2 = config.configure(container);
101 const c2 = config.configure(container);
95
102
96 c2.resolve("foo");
103 c2.resolve("foo");
97
104
98 declare const m :ContainerServices<{foo: Foo}>["container"];
105 declare const m: ContainerServices<{ foo?: Foo }>["container"];
99
106
100 m.resolve("container").resolve("container").resolve("foo"); No newline at end of file
107 m.resolve("container").resolve("container").resolve("foo");
General Comments 0
You need to be logged in to leave comments. Login now