##// END OF EJS Templates
WIP lifetime, service descriptors
cin -
r14:3f8a82c8ce73 default
parent child
Show More
@@ -1,141 +1,153
1 import { ActivationError } from "./ActivationError";
1 import { ActivationError } from "./ActivationError";
2 import { Descriptor, ILifetime, IActivationContext, DescriptorMap, ILifetimeManager } from "./interfaces";
2 import { Descriptor, ILifetime, IActivationContext, DescriptorMap, ILifetimeManager, ILifetimeSlot } from "./interfaces";
3 import { argumentNotNull, prototype } from "./traits";
3 import { argumentNotNull, prototype } from "./traits";
4
4
5 export interface ActivationContextInfo {
5 export interface ActivationContextInfo {
6 name: string;
6 name: string;
7
7
8 service: string;
8 service: string;
9
9
10 }
10 }
11
11
12 let nextId = 1;
12 let nextId = 1;
13
13
14 /** This object is created once per `Container.resolve` method call and used to
14 /** This object is created once per `Container.resolve` method call and used to
15 * cache dependencies and to track created instances. The activation context
15 * cache dependencies and to track created instances. The activation context
16 * tracks services with `context` activation type.
16 * tracks services with `context` activation type.
17 *
17 *
18 * @template S The service map used in the activation context, services from
18 * @template S The service map used in the activation context, services from
19 * this map are available to resolution.
19 * this map are available to resolution.
20 * @template U A set of keys from the service map which can be overridden in
20 * @template U A set of keys from the service map which can be overridden in
21 * this activation context.
21 * this activation context.
22 */
22 */
23 export class ActivationContext<S> implements IActivationContext<S> {
23 export class ActivationContext<S> implements IActivationContext<S> {
24 private readonly _cache: Record<string, unknown>;
24 private readonly _cache: Record<string, unknown>;
25
25
26 private readonly _services: DescriptorMap<S>;
26 private readonly _services: DescriptorMap<S>;
27
27
28 private readonly _name: string;
28 private readonly _name: string;
29
29
30 private readonly _service: Descriptor<S, unknown>;
30 private readonly _service: Descriptor<S, unknown>;
31
31
32 private readonly _lifetimeManagers: ILifetimeManager[];
32 private readonly _lifetimeManagers: ILifetimeManager[];
33
33
34 private readonly _parent: ActivationContext<S> | undefined;
34 private readonly _parent: ActivationContext<S> | undefined;
35
35
36 /** Creates a new activation context with the specified parameters.
36 /** Creates a new activation context with the specified parameters.
37 * @param containerLifetimeManager the container which starts the activation process
37 * @param containerLifetimeManager the container which starts the activation process
38 * @param services the initial service registrations
38 * @param services the initial service registrations
39 * @param name the name of the service being activated, this parameter is
39 * @param name the name of the service being activated, this parameter is
40 * used for the debug purpose.
40 * used for the debug purpose.
41 * @param service the service to activate, this parameter is used for the
41 * @param service the service to activate, this parameter is used for the
42 * debug purpose.
42 * debug purpose.
43 */
43 */
44 constructor(lifetimeManagers: ILifetimeManager[], services: DescriptorMap<S>, name: string, service: Descriptor<S, unknown>, cache = {}) {
44 constructor(lifetimeManagers: ILifetimeManager[], services: DescriptorMap<S>, name: string, service: Descriptor<S, unknown>, cache = {}) {
45 this._name = name;
45 this._name = name;
46 this._service = service;
46 this._service = service;
47 this._cache = cache;
47 this._cache = cache;
48 this._services = services;
48 this._services = services;
49 this._lifetimeManagers = lifetimeManagers;
49 this._lifetimeManagers = lifetimeManagers;
50 }
50 }
51
51
52 /** the name of the current resolving dependency */
52 /** the name of the current resolving dependency */
53 getName() {
53 getName() {
54 return this._name;
54 return this._name;
55 }
55 }
56
56
57 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
57 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
58 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
58 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
59 resolve<K extends keyof S, T>(name: K, def?: T): NonNullable<S[K]> | T | undefined {
59 resolve<K extends keyof S, T>(name: K, def?: T): NonNullable<S[K]> | T | undefined {
60 const d = this._services[name];
60 const d = this._services[name];
61
61
62 if (d !== undefined) {
62 if (d !== undefined) {
63 return this.activate(d, name.toString());
63 return this.activate(d, name.toString());
64 } else {
64 } else {
65 if (arguments.length > 1)
65 if (arguments.length > 1)
66 return def;
66 return def;
67 else
67 else
68 throw new Error(`Service ${String(name)} not found`);
68 throw new Error(`Service ${String(name)} not found`);
69 }
69 }
70 }
70 }
71
71
72 /**
72 /**
73 * registers services local to the the activation context
73 * registers services local to the the activation context
74 *
74 *
75 * @name{string} the name of the service
75 * @name{string} the name of the service
76 * @service{string} the service descriptor to register
76 * @service{string} the service descriptor to register
77 */
77 */
78 register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]) {
78 register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]) {
79 argumentNotNull(name, "name");
79 argumentNotNull(name, "name");
80
80
81 const d = this._services[name];
81 const d = this._services[name];
82 if (d !== undefined && !d.configurable)
82 if (d !== undefined && !d.configurable)
83 throw new Error(`Service ${String(name)} can't be overridden`);
83 throw new Error(`Service ${String(name)} can't be overridden`);
84
84
85 this._services[name] = service;
85 this._services[name] = service;
86 }
86 }
87
87
88 ownerSlot<T>(slotId: string | number): ILifetimeSlot<T> {
89 return this._lifetimeManagers[this._service.level].slot(slotId);
90 }
91
92 containerSlot<T>(slotId: string | number): ILifetimeSlot<T> {
93 return this._lifetimeManagers[this._lifetimeManagers.length - 1].slot(slotId);
94 }
95
96 contextSlot<T>(slotId: string | number): ILifetimeSlot<T> {
97
98 }
99
88 createLifetime<T>(): ILifetime<T> {
100 createLifetime<T>(): ILifetime<T> {
89 const id = nextId++;
101 const id = nextId++;
90 return {
102 return {
91 initialize() { },
103 initialize() { },
92 has: () => id in this._cache,
104 has: () => id in this._cache,
93 get: () => {
105 get: () => {
94 const v = this._cache[id] as T;
106 const v = this._cache[id] as T;
95 if (v === undefined || v === null)
107 if (v === undefined || v === null)
96 throw new Error("The value isn't present in the activation context");
108 throw new Error("The value isn't present in the activation context");
97 return v;
109 return v;
98 },
110 },
99 store: item => {
111 store: item => {
100 this._cache[id] = item;
112 this._cache[id] = item;
101 }
113 }
102 };
114 };
103 }
115 }
104
116
105 activate<T>(d: Descriptor<S, T>, name: string) {
117 activate<T>(d: Descriptor<S, T>, name: string) {
106 // TODO: add logging
118 // TODO: add logging
107 // if (trace.isLogEnabled())
119 // if (trace.isLogEnabled())
108 // trace.log("enter {0} {1}", name, d);
120 // trace.log("enter {0} {1}", name, d);
109
121
110 const ctx = new ActivationContext(
122 const ctx = new ActivationContext(
111 this._containerLifetimeManager,
123 this._containerLifetimeManager,
112 d.hasOverrides ? prototype(this._services) : this._services,
124 d.hasOverrides ? prototype(this._services) : this._services,
113 name,
125 name,
114 d,
126 d,
115 this._cache
127 this._cache
116 );
128 );
117
129
118 const v = d.activate(ctx);
130 const v = d.activate(ctx);
119
131
120 // if (trace.isLogEnabled())
132 // if (trace.isLogEnabled())
121 // trace.log(`leave ${name}`);
133 // trace.log(`leave ${name}`);
122
134
123 return v;
135 return v;
124 }
136 }
125
137
126 getStack(): ActivationContextInfo[] {
138 getStack(): ActivationContextInfo[] {
127 const stack = [{
139 const stack = [{
128 name: this._name,
140 name: this._name,
129 service: this._service.toString()
141 service: this._service.toString()
130 }];
142 }];
131
143
132 return this._parent ?
144 return this._parent ?
133 stack.concat(this._parent.getStack()) :
145 stack.concat(this._parent.getStack()) :
134 stack;
146 stack;
135 }
147 }
136
148
137 fail(innerException: unknown): never {
149 fail(innerException: unknown): never {
138 throw new ActivationError(this._name, this.getStack(), innerException);
150 throw new ActivationError(this._name, this.getStack(), innerException);
139 }
151 }
140
152
141 }
153 }
@@ -1,71 +1,66
1 import { Container } from "./Container";
1 import { Container } from "./Container";
2 import { DescriptorBuilder } from "./DescriptorBuilder";
2 import { DescriptorBuilder } from "./DescriptorBuilder";
3 import { containerSelfDescriptor } from "./DescriptorImpl";
3 import { containerSelfDescriptor } from "./DescriptorImpl";
4 import { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable, ContainerServices, ContainerServicesConstraint } from "./interfaces";
4 import { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable, ContainerServices, ContainerServicesConstraint } from "./interfaces";
5 import { emptyLifetime, LifetimeManager } from "./LifetimeManager";
5 import { emptyLifetime, LifetimeManager } from "./LifetimeManager";
6 import { isDestroyable, prototype } from "./traits";
6 import { isDestroyable, prototype } from "./traits";
7
7
8 /**
8 /**
9 * Container builder used to prepare service descriptors and create a IoC container
9 * Container builder used to prepare service descriptors and create a IoC container
10 */
10 */
11 export class ContainerBuilder<S extends ContainerServicesConstraint<S>, U extends keyof S> implements
11 export class ContainerBuilder<S, U extends keyof S> implements
12 IContainerBuilder<S, U> {
12 IContainerBuilder<S, U> {
13
13
14 private _pending = 1;
14 private _pending = 1;
15
15
16 private readonly _services: DescriptorMap<ContainerServices<S>>;
16 private readonly _services: DescriptorMap<ContainerServices<S>>;
17
17
18 private readonly _lifetimeManager = new LifetimeManager();
18 private readonly _lifetimeManager = new LifetimeManager();
19
19
20 private readonly _lifetime: ILifetime<IDestroyable>;
20 private readonly _lifetime: ILifetime<IDestroyable>;
21
21
22 constructor(parentServices: DescriptorMap<S> | null = null, lifetime?: ILifetime<IDestroyable>) {
22 constructor(parentServices: DescriptorMap<S> | null = null, lifetime?: ILifetime<IDestroyable>) {
23 this._services = {
23 this._services = { ...parentServices }; // create a copy
24 ...parentServices,
25 container: containerSelfDescriptor as any,
26 childContainer: containerSelfDescriptor as any
27 };
28 this._lifetimeManager = new LifetimeManager();
29 this._lifetime = lifetime ?? emptyLifetime();
24 this._lifetime = lifetime ?? emptyLifetime();
30 }
25 }
31 createServiceBuilder<K extends U>(name: K):
26 createServiceBuilder<K extends U>(name: K):
32 IDescriptorBuilder<S, S[K], Record<never, never>, U> {
27 IDescriptorBuilder<S, S[K], Record<never, never>, U> {
33
28
34 return new DescriptorBuilder(this._lifetimeManager, this._register(name), this._fail);
29 return new DescriptorBuilder(this._lifetimeManager, this._register(name), this._fail);
35
30
36 }
31 }
37
32
38 build(): ServiceLocator<S> {
33 build(): ServiceLocator<S> {
39 this._assertBuilding();
34 this._assertBuilding();
40 if (!this._complete())
35 if (!this._complete())
41 throw new Error("The configuration didn't complete.");
36 throw new Error("The configuration didn't complete.");
42
37
43 const {remove, store} = this._lifetime(null);
38 const {remove, store} = this._lifetime(null);
44
39
45 const container = new Container(this._services, this._lifetimeManager, remove);
40 const container = new Container(this._services, this._lifetimeManager, remove);
46
41
47 store(container);
42 store(container);
48
43
49 return container;
44 return container;
50 }
45 }
51
46
52 private readonly _register = <K extends U>(name: K) =>
47 private readonly _register = <K extends U>(name: K) =>
53 (descriptor: Descriptor<S, S[K]>) => {
48 (descriptor: Descriptor<S, S[K]>) => {
54 this._complete();
49 this._complete();
55 this._services[name] = descriptor;
50 this._services[name] = descriptor;
56 };
51 };
57
52
58 private readonly _fail = (ex: unknown) => {
53 private readonly _fail = (ex: unknown) => {
59 throw ex;
54 throw ex;
60 };
55 };
61
56
62 private _assertBuilding() {
57 private _assertBuilding() {
63 if (!this._pending)
58 if (!this._pending)
64 throw new Error("The descriptor builder is finalized");
59 throw new Error("The descriptor builder is finalized");
65 }
60 }
66
61
67 private _complete() {
62 private _complete() {
68 return !(--this._pending);
63 return !(--this._pending);
69 }
64 }
70
65
71 } No newline at end of file
66 }
@@ -1,193 +1,189
1 import { BuildDescriptorFn, IDescriptorBuilder, DepsMap, Resolve, DescriptorMap } 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 } from "./DescriptorImpl";
3 import { DescriptorImpl } from "./DescriptorImpl";
4 import { contextLifetime, emptyLifetime, hierarchyLifetime, LifetimeManager, singletonLifetime } from "./LifetimeManager";
4 import { containerLifetime, contextLifetime, emptyLifetime, hierarchyLifetime, LifetimeManager, singletonLifetime } from "./LifetimeManager";
5 import { each, 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, T, R, U extends keyof S> implements IDescriptorBuilder<S, T, R, U> {
11 export class DescriptorBuilder<S, T, R, U extends keyof S> implements IDescriptorBuilder<S, T, R, U> {
12 private readonly _lifetimeManager: LifetimeManager;
13 private readonly _cb: (d: Descriptor<S, T>) => void;
12 private readonly _cb: (d: Descriptor<S, T>) => void;
14
13
15 private readonly _eb: (err: unknown) => void;
14 private readonly _eb: (err: unknown) => void;
16
15
17 private readonly _refs: DepsMap<R>;
16 private readonly _refs: DepsMap<R>;
18
17
19 private _lifetime = emptyLifetime<T>();
18 private _lifetime: ILifetime<T> = emptyLifetime<T>();
20
19
21 private _overrides: DescriptorMap<S>;
20 private _overrides: DescriptorMap<S>;
22
21
23 private _cleanup?: (item: T) => void;
22 private _cleanup?: (item: T) => void;
24
23
25 private _factory?: (refs: R) => NonNullable<T>;
24 private _factory?: (refs: R) => NonNullable<T>;
26
25
27 private _pending = 1;
26 private _pending = 1;
28
27
29 private _failed = false;
28 private _failed = false;
30
29
31 private _finalized = false;
30 private _finalized = false;
32
31
33 /**
32 /**
34 * Creates new DescriptorBuilder. Accepts a lifetime container for resolving "container"
33 * Creates new DescriptorBuilder. Accepts a lifetime container for resolving "container"
35 * lifetime.
34 * lifetime.
36 *
35 *
37 * @param lifetimeManager The lifetime container is the container where the service is to be registered.
38 * @param cb The callback to receive the built service descriptor
36 * @param cb The callback to receive the built service descriptor
39 * @param eb The callback to receive the error due
37 * @param eb The callback to receive the error due
40 */
38 */
41 constructor(lifetimeManager: LifetimeManager, cb: (d: Descriptor<S, T>) => void, eb: (err: unknown) => void) {
39 constructor(cb: (d: Descriptor<S, T>) => void, eb: (err: unknown) => void) {
42 this._lifetimeManager = lifetimeManager;
43 this._cb = cb;
40 this._cb = cb;
44 this._eb = eb;
41 this._eb = eb;
45 this._overrides = {};
42 this._overrides = {};
46 this._refs = {};
43 this._refs = {};
47 }
44 }
48
45
49 /** Declares dependencies to be consumed in the factory method */
46 /** Declares dependencies to be consumed in the factory method */
50 wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X):
47 wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X):
51 IDescriptorBuilder<S, T, R & { [k in keyof X]: Resolve<S, X[k]>; }, U> {
48 IDescriptorBuilder<S, T, R & { [k in keyof X]: Resolve<S, X[k]>; }, U> {
52
49
53 each(refs, (v, k) => this._refs[k] = v);
50 each(refs, (v, k) => this._refs[k] = v);
54
51
55 return this as IDescriptorBuilder<S, T, R & {
52 return this as IDescriptorBuilder<S, T, R & {
56 [k in keyof X]: Resolve<S, X[k]>;
53 [k in keyof X]: Resolve<S, X[k]>;
57 }, U>;
54 }, U>;
58 }
55 }
59
56
60 /** Registers a factory method for the service */
57 /** Registers a factory method for the service */
61 factory(f: (refs: R) => NonNullable<T>): void {
58 factory(f: (refs: R) => NonNullable<T>): void {
62 this._assertBuilding();
59 this._assertBuilding();
63 this._factory = f;
60 this._factory = f;
64 this._finalize();
61 this._finalize();
65 this._complete();
62 this._complete();
66 }
63 }
67
64
68
65
69 private _assertBuilding() {
66 private _assertBuilding() {
70 if (this._finalized)
67 if (this._finalized)
71 throw new Error("The descriptor builder is finalized");
68 throw new Error("The descriptor builder is finalized");
72 }
69 }
73
70
74 private _finalize() {
71 private _finalize() {
75 this._finalized = true;
72 this._finalized = true;
76 }
73 }
77
74
78 override<K extends U>(name: K, builder: BuildDescriptorFn<S, NonNullable<S[K]>, U>): this;
75 override<K extends U>(name: K, builder: BuildDescriptorFn<S, NonNullable<S[K]>, U>): this;
79 override<K extends U>(services: { [k in K]: BuildDescriptorFn<S, NonNullable<S[k]>, U> }): this;
76 override<K extends U>(services: { [k in K]: BuildDescriptorFn<S, NonNullable<S[k]>, U> }): 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 {
77 override<K extends U>(nameOrServices: K | { [name in K]: BuildDescriptorFn<S, NonNullable<S[K]>, U> }, builder?: BuildDescriptorFn<S, NonNullable<S[K]>, U>): this {
81 this._assertBuilding();
78 this._assertBuilding();
82 const guard = (v: void | Promise<void>) => {
79 const guard = (v: void | Promise<void>) => {
83 if (isPromise(v))
80 if (isPromise(v))
84 v.catch(err => this._fail(err));
81 v.catch(err => this._fail(err));
85 };
82 };
86
83
87 if (typeof nameOrServices !== "object") {
84 if (typeof nameOrServices !== "object") {
88 if (builder) {
85 if (builder) {
89 this._defer();
86 this._defer();
90 const d = new DescriptorBuilder<S, NonNullable<S[K]>, object, U>(
87 const d = new DescriptorBuilder<S, NonNullable<S[K]>, object, U>(
91 this._lifetimeManager,
92 result => {
88 result => {
93 this._overrides[nameOrServices] = result;
89 this._overrides[nameOrServices] = result;
94 this._complete();
90 this._complete();
95 },
91 },
96 err => this._fail(err)
92 err => this._fail(err)
97 );
93 );
98
94
99 try {
95 try {
100 guard(builder(d));
96 guard(builder(d));
101 } catch (err) {
97 } catch (err) {
102 this._fail(err);
98 this._fail(err);
103 }
99 }
104 }
100 }
105 } else {
101 } else {
106 each(nameOrServices, (v, k) => this.override(k, v));
102 each(nameOrServices, (v, k) => this.override(k, v));
107 }
103 }
108 return this;
104 return this;
109 }
105 }
110
106
111 /** Specified the singleton lifetime for the service */
107 /** Specified the singleton lifetime for the service */
112 lifetime(lifetime: "singleton", typeId: string): this;
108 lifetime(lifetime: "singleton", typeId: string): this;
113 /**
109 /**
114 * Specifies the lifetime for the service, either {@linkcode ILifetime<T>}
110 * Specifies the lifetime for the service, either {@linkcode ILifetime<T>}
115 * object or {@linkcode ActivationType} literal.
111 * object or {@linkcode ActivationType} literal.
116 * @param lifetime
112 * @param lifetime
117 */
113 */
118 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
114 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
119 lifetime(lifetime: ILifetime<T> | ActivationType, typeId?: string): this {
115 lifetime(lifetime: ILifetime<T> | ActivationType, typeId?: string): this {
120 this._assertBuilding();
116 this._assertBuilding();
121 if (isString(lifetime)) {
117 if (isString(lifetime)) {
122 this._lifetime = this._resolveLifetime(lifetime, typeId);
118 this._lifetime = this._resolveLifetime(lifetime, typeId);
123 } else {
119 } else {
124 this._lifetime = lifetime;
120 this._lifetime = lifetime;
125 }
121 }
126 return this;
122 return this;
127 }
123 }
128
124
129 /** Registers cleanup callback, used when lifetime of the instance is managed
125 /** Registers cleanup callback, used when lifetime of the instance is managed
130 * by the container or some external mechanism
126 * by the container or some external mechanism
131 */
127 */
132 cleanup(cb: (item: T) => void): this {
128 cleanup(cb: (item: T) => void): this {
133 this._assertBuilding();
129 this._assertBuilding();
134 this._cleanup = cb;
130 this._cleanup = cb;
135 return this;
131 return this;
136 }
132 }
137
133
138 /** Registers a value as the instance of the service */
134 /** Registers a value as the instance of the service */
139 value(v: NonNullable<T>): void {
135 value(v: NonNullable<T>): void {
140 this._assertBuilding();
136 this._assertBuilding();
141 this._cb({
137 this._cb({
142 activate() {
138 activate() {
143 return v;
139 return v;
144 }
140 }
145 });
141 });
146 this._finalize();
142 this._finalize();
147 }
143 }
148
144
149 _resolveLifetime<T>(activation: ActivationType, typeId?: string | object): ILifetime<T> {
145 _resolveLifetime<T>(activation: ActivationType, typeId?: string | object): ILifetime<T> {
150 switch (activation) {
146 switch (activation) {
151 case "container":
147 case "container":
152 return this._lifetimeManager.create();
148 return containerLifetime();
153 case "hierarchy":
149 case "hierarchy":
154 return hierarchyLifetime();
150 return hierarchyLifetime();
155 case "context":
151 case "context":
156 return contextLifetime();
152 return contextLifetime();
157 case "singleton": {
153 case "singleton": {
158 if (!typeId)
154 if (!typeId)
159 throw Error("The singleton activation requires a typeId");
155 throw Error("The singleton activation requires a typeId");
160 const _oid = isString(typeId) ? typeId : oid(typeId);
156 const _oid = isString(typeId) ? typeId : oid(typeId);
161 return singletonLifetime(_oid);
157 return singletonLifetime(_oid);
162 }
158 }
163 default:
159 default:
164 return emptyLifetime();
160 return emptyLifetime();
165 }
161 }
166 }
162 }
167
163
168 _defer() {
164 _defer() {
169 this._pending++;
165 this._pending++;
170 }
166 }
171
167
172 _complete() {
168 _complete() {
173 if (--this._pending === 0) {
169 if (--this._pending === 0) {
174 if (!this._factory)
170 if (!this._factory)
175 throw new Error("The factory must be specified");
171 throw new Error("The factory must be specified");
176
172
177 this._cb(new DescriptorImpl<S, T>({
173 this._cb(new DescriptorImpl<S, T>({
178 lifetime: this._lifetime,
174 lifetime: this._lifetime,
179 factory: this._factory as (refs: Record<key, unknown>) => NonNullable<T>,
175 factory: this._factory as (refs: Record<key, unknown>) => NonNullable<T>,
180 overrides: this._overrides,
176 overrides: this._overrides,
181 cleanup: this._cleanup
177 cleanup: this._cleanup
182 }));
178 }));
183 }
179 }
184 }
180 }
185
181
186 _fail(err: unknown) {
182 _fail(err: unknown) {
187 if (!this._failed) {
183 if (!this._failed) {
188 this._failed = true;
184 this._failed = true;
189 this._eb.call(undefined, err);
185 this._eb.call(undefined, err);
190 }
186 }
191 }
187 }
192
188
193 }
189 }
@@ -1,104 +1,105
1 import { Descriptor, ILifetime, DepsMap, IActivationContext, DescriptorMap } from "./interfaces";
1 import { Descriptor, ILifetime, DepsMap, IActivationContext, DescriptorMap } from "./interfaces";
2 import { each, key } from "./traits";
2 import { each, key } from "./traits";
3
3
4 export interface DescriptorImplArgs<S, T> {
4 export interface DescriptorImplArgs<S, T> {
5 lifetime: ILifetime<T>;
6
5
7 factory: (refs: Record<key, unknown>) => NonNullable<T>;
6 readonly lifetime: ILifetime<NonNullable<T>>;
7
8 readonly factory: (refs: Record<key, unknown>) => NonNullable<T>;
8
9
9 cleanup?: (item: NonNullable<T>) => void;
10 readonly cleanup?: (item: NonNullable<T>) => void;
10
11
11 overrides?: DescriptorMap<S>;
12 readonly overrides?: DescriptorMap<S>;
12
13
13 dependencies?: DepsMap<S>;
14 readonly dependencies?: DepsMap<S>;
14 }
15 }
15
16
16 export const containerSelfDescriptor = <S>() => Object.freeze({
17 export const containerSelfDescriptor = <S>() => Object.freeze({
17 level: 0,
18 level: 0,
18 activate(context: IActivationContext<S>) {
19 activate(context: IActivationContext<S>) {
19 return context.createChildContainer();
20 return context.createChildContainer();
20 }
21 }
21 });
22 });
22
23
23
24
24 export class DescriptorImpl<S, T> implements Descriptor<S, T> {
25 export class DescriptorImpl<S, T> implements Descriptor<S, T> {
25
26
26 private readonly _overrides?: DescriptorMap<S>;
27 private readonly _overrides?: DescriptorMap<S>;
27
28
28 private readonly _lifetime: ILifetime<T>;
29 private readonly _lifetime: ILifetime<NonNullable<T>>;
29
30
30 private readonly _factory: (refs: Record<key, unknown>) => NonNullable<T>;
31 private readonly _factory: (refs: Record<key, unknown>) => NonNullable<T>;
31
32
32 private readonly _cleanup?: (item: NonNullable<T>) => void;
33 private readonly _cleanup?: (item: NonNullable<T>) => void;
33
34
34 private readonly _deps?: DepsMap<S>;
35 private readonly _deps?: DepsMap<S>;
35
36
36 readonly hasOverrides: boolean;
37 readonly hasOverrides: boolean;
37
38
38 constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs<S, T>) {
39 constructor({ lifetime, factory, cleanup, overrides, dependencies }: DescriptorImplArgs<S, T>) {
39 this._lifetime = lifetime;
40 this._lifetime = lifetime;
40 this._factory = factory;
41 this._factory = factory;
41 if (cleanup)
42 if (cleanup)
42 this._cleanup = cleanup;
43 this._cleanup = cleanup;
43 if (overrides)
44 if (overrides)
44 this._overrides = overrides;
45 this._overrides = overrides;
45 if (dependencies)
46 if (dependencies)
46 this._deps = dependencies;
47 this._deps = dependencies;
47
48
48 this.hasOverrides = !!overrides;
49 this.hasOverrides = !!overrides;
49 }
50 }
50
51
51 activate(context: IActivationContext<S>): NonNullable<T> {
52 activate(context: IActivationContext<S>): NonNullable<T> {
52
53
53 const { has, get, initialize, store } = this._lifetime(context);
54 const { has, get, initialize, store } = this._lifetime(context);
54
55
55 if (has())
56 if (has())
56 return get();
57 return get();
57
58
58 initialize();
59 initialize();
59
60
60 if (this._overrides)
61 if (this._overrides)
61 each(this._overrides, (v, k) => context.register(k, v));
62 each(this._overrides, (v, k) => context.register(k, v));
62
63
63 const resolve = <K extends keyof S>({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K] | null; }) => {
64 const resolve = <K extends keyof S>({ name, lazy, ...opts }: { name: K; lazy?: boolean; default?: S[K] | null; }) => {
64 if (lazy) {
65 if (lazy) {
65 return "default" in opts ?
66 return "default" in opts ?
66 () => context.resolve(name, opts.default) :
67 () => context.resolve(name, opts.default) :
67 () => context.resolve(name);
68 () => context.resolve(name);
68 } else {
69 } else {
69 return "default" in opts ?
70 return "default" in opts ?
70 context.resolve(name, opts.default) :
71 context.resolve(name, opts.default) :
71 context.resolve(name);
72 context.resolve(name);
72 }
73 }
73 };
74 };
74
75
75 const deps = this._deps;
76 const deps = this._deps;
76
77
77 const refs = deps ?
78 const refs = deps ?
78 Object.keys(deps)
79 Object.keys(deps)
79 .map(k => {
80 .map(k => {
80 const ref = deps[k];
81 const ref = deps[k];
81 return typeof ref !== "object" ?
82 return typeof ref !== "object" ?
82 { [k]: resolve({ name: ref }) } :
83 { [k]: resolve({ name: ref }) } :
83 { [k]: resolve(ref) };
84 { [k]: resolve(ref) };
84 })
85 })
85 .reduce((a, p) => ({ ...a, ...p }), {}) :
86 .reduce((a, p) => ({ ...a, ...p }), {}) :
86 {};
87 {};
87
88
88 try {
89 try {
89 // call the factory method
90 // call the factory method
90 const instance = (0,this._factory)(refs);
91 const instance = (0, this._factory)(refs);
91
92
92 // store the instance
93 // store the instance
93 store(instance, this._cleanup);
94 store(instance, this._cleanup);
94 return instance;
95 return instance;
95 } catch(err) {
96 } catch (err) {
96 context.fail(err);
97 context.fail(err);
97 }
98 }
98 }
99 }
99
100
100
101
101 toString() {
102 toString() {
102 return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`;
103 return `[object DescriptorImpl, lifetime=${String(this._lifetime)}]`;
103 }
104 }
104 }
105 }
@@ -1,277 +1,273
1 import { ContainerBuilder } from "./ContainerBuilder";
1 import { ContainerBuilder } from "./ContainerBuilder";
2 import { key } from "./traits";
2 import { key } from "./traits";
3
3
4 export interface IDestroyable {
4 export interface IDestroyable {
5 destroy(): void;
5 destroy(): void;
6 }
6 }
7
7
8 /**
8 /**
9 * @template S Карта доступных зависимостей
9 * @template S Карта доступных зависимостей
10 */
10 */
11 export interface Resolver<S> {
11 export interface Resolver<S> {
12 /**
12 /**
13 * Функция для разрешения зависимостей, поддерживает создание фабричных методов,
13 * Функция для разрешения зависимостей, поддерживает создание фабричных методов,
14 * отложенную активацию и значение по-умолчанию для сервисов
14 * отложенную активацию и значение по-умолчанию для сервисов
15 * @template K Ключ сервиса из {@linkcode S}
15 * @template K Ключ сервиса из {@linkcode S}
16 * @template O Тип параметра {@linkcode opts} используется для выведения типа
16 * @template O Тип параметра {@linkcode opts} используется для выведения типа
17 * возвращаемого значения.
17 * возвращаемого значения.
18 * @param name Ключ сервиса, который будет разрешен.
18 * @param name Ключ сервиса, который будет разрешен.
19 * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
19 * @param {boolean=} opts.lazy Признак того, что требуется отложенная активация,
20 * будет возвращен фабричный метод для получения зависимости. Если не указан,
20 * будет возвращен фабричный метод для получения зависимости. Если не указан,
21 * то считается `false`.
21 * то считается `false`.
22 * @param {any=} opts.default Значение по умолчанию, если в контейнере указанный
22 * @param {any=} opts.default Значение по умолчанию, если в контейнере указанный
23 * сервис не зарегистрирован
23 * сервис не зарегистрирован
24 * @returns Либо фабричный метод для получения зависимости, либо значение зависимости
24 * @returns Либо фабричный метод для получения зависимости, либо значение зависимости
25 * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
25 * @throws Error Если зависимость не найдена и не предоставлено значение по-умолчанию
26 */
26 */
27 <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: true; }>(name: K, opts?: O): () => NonNullable<S[K]> | InferDefault<O>;
28 <K extends keyof S, O extends { lazy?: false; }>(name: K, opts?: O): NonNullable<S[K]> | InferDefault<O>;
28 <K extends keyof S, O extends { lazy?: false; }>(name: K, opts?: O): NonNullable<S[K]> | InferDefault<O>;
29 }
29 }
30
30
31 export type DepsMap<S> = {
31 export type DepsMap<S> = {
32 [k in key]: Refs<S> | keyof S;
32 [k in key]: Refs<S> | keyof S;
33 };
33 };
34
34
35 export type Refs<S> = {
35 export type Refs<S> = {
36 [k in keyof S]: Ref<k, S[k]>;
36 [k in keyof S]: Ref<k, S[k]>;
37 }[keyof S];
37 }[keyof S];
38
38
39 export type Ref<K extends key, D> = {
39 export type Ref<K extends key, D> = {
40 /** The name of the service */
40 /** The name of the service */
41 name: K;
41 name: K;
42
42
43 /** Make a lazy reference, the resolved dependency will be a function */
43 /** Make a lazy reference, the resolved dependency will be a function */
44 lazy?: boolean;
44 lazy?: boolean;
45
45
46 /** The default value for the case where the service isn't defined.
46 /** The default value for the case where the service isn't defined.
47 * When specified the dependency becomes optional, the default value can be
47 * When specified the dependency becomes optional, the default value can be
48 * `null` or `undefined`
48 * `null` or `undefined`
49 */
49 */
50 default?: D | null
50 default?: D | null
51 };
51 };
52
52
53 export type Lazy<T, L extends boolean> = L extends true ? () => T : T;
53 export type Lazy<T, L extends boolean> = L extends true ? () => T : T;
54
54
55 /** Возвращает тип свойства `default` в типе {@link T} */
55 /** Возвращает тип свойства `default` в типе {@link T} */
56 export type InferDefault<T> = T extends { default: infer D } ? D : never;
56 export type InferDefault<T> = T extends { default: infer D } ? D : never;
57
57
58 export type InferLazy<R> = R extends { lazy: infer L } ?
58 export type InferLazy<R> = R extends { lazy: infer L } ?
59 L extends true ? true : false :
59 L extends true ? true : false :
60 false;
60 false;
61 export type Resolve<S, R> =
61 export type Resolve<S, R> =
62 R extends keyof S ? NonNullable<S[R]> :
62 R extends keyof S ? NonNullable<S[R]> :
63 R extends Ref<infer K, unknown> ?
63 R extends Ref<infer K, unknown> ?
64 K extends keyof S ?
64 K extends keyof S ?
65 Lazy<NonNullable<S[K]> | InferDefault<R>, InferLazy<R>> :
65 Lazy<NonNullable<S[K]> | InferDefault<R>, InferLazy<R>> :
66 never :
66 never :
67 never;
67 never;
68
68
69 /**
69 /**
70 * Интерфейс для конфигурирования сервиса в контейнере. Конфигурирование сервиса
70 * Интерфейс для конфигурирования сервиса в контейнере. Конфигурирование сервиса
71 * состоит из настройки различных параметров вызовами методов {@linkcode wants},
71 * состоит из настройки различных параметров вызовами методов {@linkcode wants},
72 * {@linkcode lifetime}, {@linkcode override}, {@linkcode cleanup}. Завершение настройки
72 * {@linkcode lifetime}, {@linkcode override}, {@linkcode cleanup}. Завершение настройки
73 * сервиса осуществляется вызовом одного из методов {@linkcode factory} либо
73 * сервиса осуществляется вызовом одного из методов {@linkcode factory} либо
74 * {@linkcode value}.
74 * {@linkcode value}.
75 *
75 *
76 * @template S Карта сервисов контейнера, доступных при описании дескриптора
76 * @template S Карта сервисов контейнера, доступных при описании дескриптора
77 * @template T Тип сервиса
77 * @template T Тип сервиса
78 * @template R Карта зависимостей, которая передается параметром фабрике
78 * @template R Карта зависимостей, которая передается параметром фабрике
79 * @template U Имена пользовательских сервисов, доступных для переопределения
79 * @template U Имена пользовательских сервисов, доступных для переопределения
80 */
80 */
81 export interface IDescriptorBuilder<S, T, R, U extends keyof S> {
81 export interface IDescriptorBuilder<S, T, R, U extends keyof S> {
82
82
83 /** Указывает фабрика для создания экземпляра сервиса, фабрика передается
83 /** Указывает фабрика для создания экземпляра сервиса, фабрика передается
84 * в виде параметра. При вызове фабрике будет передан объект с зависимостями,
84 * в виде параметра. При вызове фабрике будет передан объект с зависимостями,
85 * которые были предварительно указаны вызовами метода `wants(...)`
85 * которые были предварительно указаны вызовами метода `wants(...)`
86 *
86 *
87 * Вызов данного метода завершает конфигурирование сервиса.
87 * Вызов данного метода завершает конфигурирование сервиса.
88 *
88 *
89 * @param f Фабрика для создания экземпляра сервиса.
89 * @param f Фабрика для создания экземпляра сервиса.
90 */
90 */
91 factory(f: (refs: R) => NonNullable<T>): void;
91 factory(f: (refs: R) => NonNullable<T>): void;
92
92
93 /**
93 /**
94 * Используется для указания зависимостей, которые потребуются фабричному
94 * Используется для указания зависимостей, которые потребуются фабричному
95 * методу при создании нового экземпляра сервиса. Данный метод может быть
95 * методу при создании нового экземпляра сервиса. Данный метод может быть
96 * вызван несколько раз подряд, при этом вызовы этого метода имеют
96 * вызван несколько раз подряд, при этом вызовы этого метода имеют
97 * кумулятивный эффект.
97 * кумулятивный эффект.
98 *
98 *
99 * @template X Тип объекта с зависимостями, которые требуется получить при
99 * @template X Тип объекта с зависимостями, которые требуется получить при
100 * создании экземпляра фабрики при помощи фабричного метода.
100 * создании экземпляра фабрики при помощи фабричного метода.
101 * @param refs Объект с описанием зависимостей
101 * @param refs Объект с описанием зависимостей
102 * @returns Возвращает дескриптор сервиса, в котором указаны необходимые
102 * @returns Возвращает дескриптор сервиса, в котором указаны необходимые
103 * зависимости
103 * зависимости
104 */
104 */
105 wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X):
105 wants<X extends DepsMap<S> & Record<keyof R & keyof X, never>>(refs: X):
106 IDescriptorBuilder<S, T, R & {
106 IDescriptorBuilder<S, T, R & {
107 [k in keyof X]: Resolve<S, X[k]>;
107 [k in keyof X]: Resolve<S, X[k]>;
108 }, U>
108 }, U>
109
109
110 override<K extends U>(name: K, builder: BuildDescriptorFn<S, S[K], U>): this;
110 override<K extends U>(name: K, builder: BuildDescriptorFn<S, S[K], U>): this;
111 override<X extends ConfigurationMapConstraint<S, U, keyof X>>(services: X): this;
111 override<X extends ConfigurationMapConstraint<S, U, keyof X>>(services: X): this;
112
112
113 lifetime(lifetime: "singleton", typeId: string | number | object): this;
113 lifetime(lifetime: "singleton", typeId: string | number | object): this;
114 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
114 lifetime(lifetime: ILifetime<T> | Exclude<ActivationType, "singleton">): this;
115
115
116 /** Указывает функцию для освобождения экземпляра сервиса для случаев, когда
116 /** Указывает функцию для освобождения экземпляра сервиса для случаев, когда
117 * время жизни привязано к контейнеру.
117 * время жизни привязано к контейнеру.
118 */
118 */
119 cleanup(cb: (item: T) => void): this;
119 cleanup(cb: (item: T) => void): this;
120
120
121 /**
121 /**
122 * Регистрирует в контейнере постоянное значение в качестве реализации сервиса.
122 * Регистрирует в контейнере постоянное значение в качестве реализации сервиса.
123 *
123 *
124 * @param v Экземпляр реализации сервиса.
124 * @param v Экземпляр реализации сервиса.
125 */
125 */
126 value(v: NonNullable<T>): void;
126 value(v: NonNullable<T>): void;
127 }
127 }
128
128
129 export type BuildDescriptorFn<S, T, U extends keyof S> = (d: IDescriptorBuilder<S, T, Record<never, never>, U>) => void;
129 export type BuildDescriptorFn<S, T, U extends keyof S> = (d: IDescriptorBuilder<S, T, Record<never, never>, U>) => void;
130
130
131 /**
131 /**
132 * Конфигурация контейнера, состоит из набора функций, которые выполняют конфигурацию.
132 * Конфигурация контейнера, состоит из набора функций, которые выполняют конфигурацию.
133 *
133 *
134 * Все параметры конфигурации являются обязательными, если требуется ввести
134 * Все параметры конфигурации являются обязательными, если требуется ввести
135 * необязательные параметры, то нужно ограничить параметр типа {@linkcode K}
135 * необязательные параметры, то нужно ограничить параметр типа {@linkcode K}
136 *
136 *
137 * @template S Сервисы доступные в контейнере
137 * @template S Сервисы доступные в контейнере
138 * @template K Сервисы участвующие в конфигурации
138 * @template K Сервисы участвующие в конфигурации
139 */
139 */
140 export type ConfigurationMap<S, K extends keyof S, U extends keyof S> = {
140 export type ConfigurationMap<S, K extends keyof S, U extends keyof S> = {
141 [k in K]-?: BuildDescriptorFn<S, S[k], U>
141 [k in K]-?: BuildDescriptorFn<S, S[k], U>
142 };
142 };
143
143
144 export type ConfigurationMapConstraint<S, U extends keyof S, X extends string | number | symbol> = {
144 export type ConfigurationMapConstraint<S, U extends keyof S, X extends string | number | symbol> = {
145 [k in X]-?: k extends U ? BuildDescriptorFn<S, S[k], U> : never;
145 [k in X]-?: k extends U ? BuildDescriptorFn<S, S[k], U> : never;
146 };
146 };
147
147
148 /**
148 /**
149 * The type constraint useful to restrict type parameters to prevent defining
149 * The type constraint useful to restrict type parameters to prevent defining
150 * the services with the {@link ContainerKeys} names.
150 * the services with the {@link ContainerKeys} names.
151 *
151 *
152 * The constraint doesn't exclude using this keys but declares them as `never`
152 * The constraint doesn't exclude using this keys but declares them as `never`
153 * which effectively will lead using this keys to the error.
153 * which effectively will lead using this keys to the error.
154 */
154 */
155 export type ContainerServicesConstraint<S> = {
155 export type ContainerServicesConstraint<S> = {
156 [k in keyof S]: k extends ContainerKeys ? never : S[k];
156 [k in keyof S]: k extends ContainerKeys ? never : S[k];
157 };
157 };
158
158
159 export interface Descriptor<S, T> {
159 export interface Descriptor<S, T> {
160
160
161 /** The level of the service in the containers chain.
162 */
163 readonly level: number;
164
165 /** This flags indicates that this registration can be replaced or overridden. */
161 /** This flags indicates that this registration can be replaced or overridden. */
166 readonly configurable?: boolean;
162 readonly configurable?: boolean;
167
163
168 /** If specified signals the activation context that a new service scope
164 /** If specified signals the activation context that a new service scope
169 * should be created to isolate service overrides.
165 * should be created to isolate service overrides.
170 */
166 */
171 readonly hasOverrides?: boolean;
167 readonly hasOverrides?: boolean;
172
168
173 activate(context: IActivationContext<S>): NonNullable<T>;
169 activate(context: IActivationContext<S>): NonNullable<T>;
174 }
170 }
175
171
176 /** The context used to initialize lifetime instance {@linkcode ILifetime} */
172 /** The context used to initialize lifetime instance {@linkcode ILifetime} */
177 export interface ILifetimeContext {
173 export interface ILifetimeContext {
178
174
179 ownerSlot<T>(slotId: string | number): ILifetimeSlot<T>;
175 ownerSlot<T>(slotId: string | number): ILifetimeSlot<T>;
180
176
181 contextSlot<T>(slotId: string | number): ILifetimeSlot<T>;
177 contextSlot<T>(slotId: string | number): ILifetimeSlot<T>;
182
178
183 containerSlot<T>(slotId: string | number): ILifetimeSlot<T>;
179 containerSlot<T>(slotId: string | number): ILifetimeSlot<T>;
184
180
185 }
181 }
186
182
187 export interface IActivationContext<S> extends ILifetimeContext, ServiceLocator<S> {
183 export interface IActivationContext<S> extends ILifetimeContext, ServiceLocator<S> {
188
184
189 register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]): void;
185 register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]): void;
190
186
191 fail(error: unknown): never;
187 fail(error: unknown): never;
192
188
193 selfContainer(): ServiceLocator<S>;
189 selfContainer(): ServiceLocator<S>;
194
190
195 createChildContainer(): IContainerBuilder<S, Exclude<keyof S, ContainerKeys>>;
191 createChildContainer(): IContainerBuilder<S, Exclude<keyof S, ContainerKeys>>;
196 }
192 }
197
193
198 /**
194 /**
199 * Descriptors map for the specified services {@linkcode S}. All entries are
195 * Descriptors map for the specified services {@linkcode S}. All entries are
200 * optional regardless the required or optional services in the original map.
196 * optional regardless the required or optional services in the original map.
201 *
197 *
202 * @template S Сервисы контекста активации
198 * @template S Сервисы контекста активации
203 * @template U Карта сервисов которые создаются дескрипторами
199 * @template U Карта сервисов которые создаются дескрипторами
204 */
200 */
205 export type DescriptorMap<S> = {
201 export type DescriptorMap<S> = {
206 [k in keyof S]?: Descriptor<S, S[k]>;
202 [k in keyof S]?: Descriptor<S, S[k]>;
207 };
203 };
208
204
209 type ContainerKeys = keyof ContainerProvided<object>;
205 type ContainerKeys = keyof ContainerProvided<object>;
210
206
211 export type ContainerProvided<S, U extends keyof S = keyof S> = {
207 export type ContainerProvided<S, U extends keyof S = keyof S> = {
212 container: ServiceLocator<ContainerProvided<S>>;
208 container: ServiceLocator<ContainerProvided<S>>;
213
209
214 childContainer: IContainerBuilder<S, U>;
210 childContainer: IContainerBuilder<S, U>;
215 };
211 };
216
212
217
213
218 /**
214 /**
219 * Таблица сервисов, которые предоставляет контейнер.
215 * Таблица сервисов, которые предоставляет контейнер.
220 *
216 *
221 * Сервисы, предоставляемые контейнером не могут быть null или undefined.
217 * Сервисы, предоставляемые контейнером не могут быть null или undefined.
222 */
218 */
223 export type ContainerServices<S> = {
219 export type ContainerServices<S> = {
224 [k in (keyof S) | ContainerKeys]:
220 [k in (keyof S) | ContainerKeys]:
225 k extends ContainerKeys ? ContainerProvided<S>[k] :
221 k extends ContainerKeys ? ContainerProvided<S>[k] :
226 k extends keyof S ? S[k] : never
222 k extends keyof S ? S[k] : never
227 };
223 };
228
224
229
225
230 /**
226 /**
231 * Returns the service declared in the type map {@link S}.
227 * Returns the service declared in the type map {@link S}.
232 *
228 *
233 *
229 *
234 */
230 */
235 export interface ServiceLocator<S> {
231 export interface ServiceLocator<S> {
236 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
232 resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
237 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
233 resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
238 }
234 }
239
235
240
236
241 export interface IContainerBuilder<S, U extends keyof S> {
237 export interface IContainerBuilder<S, U extends keyof S> {
242 createServiceBuilder<K extends U>(name: K):
238 createServiceBuilder<K extends U>(name: K):
243 IDescriptorBuilder<S, S[K], Record<never, never>, U>;
239 IDescriptorBuilder<S, S[K], Record<never, never>, U>;
244
240
245 build(): ServiceLocator<S>;
241 build(): ServiceLocator<S>;
246 }
242 }
247
243
248
244
249 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
245 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
250
246
251 /**
247 /**
252 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
248 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
253 * свой собственный объект `ILifetime`, который создается при первой активации
249 * свой собственный объект `ILifetime`, который создается при первой активации
254 */
250 */
255 export type ILifetime<T> = (context: ILifetimeContext) => ILifetimeSlot<NonNullable<T>>;
251 export type ILifetime<T> = (context: ILifetimeContext) => ILifetimeSlot<T>;
256
252
257 export interface ILifetimeSlot<T> {
253 export interface ILifetimeSlot<T> {
258 readonly has: () => boolean;
254 readonly has: () => boolean;
259
255
260 readonly get: () => T;
256 readonly get: () => T;
261
257
262 readonly initialize: () => void;
258 readonly initialize: () => void;
263
259
264 readonly store: (item: T, cleanup?: (item: T) => void) => void;
260 readonly store: (item: T, cleanup?: (item: T) => void) => void;
265
261
266 readonly remove: () => void;
262 readonly remove: () => void;
267
263
268 readonly cleanup: () => void;
264 readonly cleanup: () => void;
269 }
265 }
270
266
271 export interface ILifetimeManager extends IDestroyable {
267 export interface ILifetimeManager extends IDestroyable {
272 slot<T>(id: string | number): ILifetimeSlot<T>;
268 slot<T>(id: string | number): ILifetimeSlot<T>;
273 }
269 }
274
270
275 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
271 export type ExtractRequired<T, K extends keyof T = keyof T> = { [p in K as (undefined extends T[p] ? never : p)]-?: T[p] };
276
272
277 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
273 export type ExtractRequiredKeys<T, K extends keyof T = keyof T> = { [p in K]-?: undefined extends T[p] ? never : p }[K];
General Comments 0
You need to be logged in to leave comments. Login now