| @@ -29,7 +29,7 export class ActivationContext<S> implem | |||
|
|
29 | 29 | |
|
|
30 | 30 | private readonly _service: Descriptor<S, unknown>; |
|
|
31 | 31 | |
|
|
32 |
private readonly _ |
|
|
|
32 | private readonly _lifetimeManagers: ILifetimeManager[]; | |
|
|
33 | 33 | |
|
|
34 | 34 | private readonly _parent: ActivationContext<S> | undefined; |
|
|
35 | 35 | |
| @@ -41,12 +41,12 export class ActivationContext<S> implem | |||
|
|
41 | 41 | * @param service the service to activate, this parameter is used for the |
|
|
42 | 42 | * debug purpose. |
|
|
43 | 43 | */ |
|
|
44 |
constructor( |
|
|
|
44 | constructor(lifetimeManagers: ILifetimeManager[], services: DescriptorMap<S>, name: string, service: Descriptor<S, unknown>, cache = {}) { | |
|
|
45 | 45 | this._name = name; |
|
|
46 | 46 | this._service = service; |
|
|
47 | 47 | this._cache = cache; |
|
|
48 | 48 | this._services = services; |
|
|
49 |
this._ |
|
|
|
49 | this._lifetimeManagers = lifetimeManagers; | |
|
|
50 | 50 | } |
|
|
51 | 51 | |
|
|
52 | 52 | /** the name of the current resolving dependency */ |
| @@ -54,10 +54,6 export class ActivationContext<S> implem | |||
|
|
54 | 54 | return this._name; |
|
|
55 | 55 | } |
|
|
56 | 56 | |
|
|
57 | createContainerLifetime<T>() { | |
|
|
58 | return this._containerLifetimeManager.create<T>(); | |
|
|
59 | } | |
|
|
60 | ||
|
|
61 | 57 | resolve<K extends keyof S>(name: K): NonNullable<S[K]>; |
|
|
62 | 58 | resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T; |
|
|
63 | 59 | resolve<K extends keyof S, T>(name: K, def?: T): NonNullable<S[K]> | T | undefined { |
| @@ -1,32 +1,22 | |||
|
|
1 | 1 | import { ActivationContext } from "./ActivationContext"; |
|
|
2 | 2 | import { ActivationError } from "./ActivationError"; |
|
|
3 | 3 | import { ContainerBuilder } from "./ContainerBuilder"; |
|
|
4 | import { LifetimeManager } from "./LifetimeManager"; | |
|
|
4 | 5 | import { DescriptorMap, IContainerBuilder, IDestroyable, ILifetimeManager, ServiceLocator } from "./interfaces"; |
|
|
5 | 6 | |
|
|
6 | 7 | export class Container<S> implements ServiceLocator<S>, IDestroyable { |
|
|
7 | 8 | private readonly _services: DescriptorMap<S>; |
|
|
8 | 9 | |
|
|
9 | private readonly _lifetimeManager: ILifetimeManager; | |
|
|
10 | private readonly _lifetimeManagers: ILifetimeManager[]; | |
|
|
10 | 11 | |
|
|
11 | 12 | private _disposed: boolean; |
|
|
12 | 13 | |
|
|
13 | 14 | private readonly _onDestroyed: () => void; |
|
|
14 | 15 | |
|
|
15 | constructor(services: DescriptorMap<S>, lifetimeManager: ILifetimeManager, destroyed: () => void) { | |
|
|
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 | ||
|
|
16 | constructor(services: DescriptorMap<S>, lifetimeManagers: ILifetimeManager[], destroyed: () => void) { | |
|
|
17 | this._services = services; | |
|
|
28 | 18 | this._disposed = false; |
|
|
29 | this._lifetimeManager = lifetimeManager; | |
|
|
19 | this._lifetimeManagers = lifetimeManagers.concat(new LifetimeManager()); | |
|
|
30 | 20 | this._onDestroyed = destroyed; |
|
|
31 | 21 | |
|
|
32 | 22 | } |
| @@ -56,7 +46,7 export class Container<S> implements Ser | |||
|
|
56 | 46 | else |
|
|
57 | 47 | throw new Error(`Service '${String(name)}' isn't found`); |
|
|
58 | 48 | } else { |
|
|
59 | const context = new ActivationContext(this._lifetimeManager, this._services, String(name), d); | |
|
|
49 | const context = new ActivationContext(this._lifetimeManagers, this._services, String(name), d); | |
|
|
60 | 50 | try { |
|
|
61 | 51 | return d.activate(context); |
|
|
62 | 52 | } catch (error) { |
| @@ -1,25 +1,30 | |||
|
|
1 | 1 | import { Container } from "./Container"; |
|
|
2 | 2 | import { DescriptorBuilder } from "./DescriptorBuilder"; |
|
|
3 | import { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable } from "./interfaces"; | |
|
|
3 | import { containerSelfDescriptor } from "./DescriptorImpl"; | |
|
|
4 | import { Descriptor, IContainerBuilder, IDescriptorBuilder, DescriptorMap, ServiceLocator, ILifetime, IDestroyable, ContainerServices, ContainerServicesConstraint } from "./interfaces"; | |
|
|
4 | 5 | import { emptyLifetime, LifetimeManager } from "./LifetimeManager"; |
|
|
5 | 6 | import { isDestroyable, prototype } from "./traits"; |
|
|
6 | 7 | |
|
|
7 | 8 | /** |
|
|
8 | 9 | * Container builder used to prepare service descriptors and create a IoC container |
|
|
9 | 10 | */ |
|
|
10 | export class ContainerBuilder<S, U extends keyof S> implements | |
|
|
11 | export class ContainerBuilder<S extends ContainerServicesConstraint<S>, U extends keyof S> implements | |
|
|
11 | 12 | IContainerBuilder<S, U> { |
|
|
12 | 13 | |
|
|
13 | 14 | private _pending = 1; |
|
|
14 | 15 | |
|
|
15 | private readonly _services: DescriptorMap<S>; | |
|
|
16 | private readonly _services: DescriptorMap<ContainerServices<S>>; | |
|
|
16 | 17 | |
|
|
17 | 18 | private readonly _lifetimeManager = new LifetimeManager(); |
|
|
18 | 19 | |
|
|
19 | 20 | private readonly _lifetime: ILifetime<IDestroyable>; |
|
|
20 | 21 | |
|
|
21 | 22 | constructor(parentServices: DescriptorMap<S> | null = null, lifetime?: ILifetime<IDestroyable>) { |
|
|
22 |
this._services = |
|
|
|
23 | this._services = { | |
|
|
24 | ...parentServices, | |
|
|
25 | container: containerSelfDescriptor as any, | |
|
|
26 | childContainer: containerSelfDescriptor as any | |
|
|
27 | }; | |
|
|
23 | 28 | this._lifetimeManager = new LifetimeManager(); |
|
|
24 | 29 | this._lifetime = lifetime ?? emptyLifetime(); |
|
|
25 | 30 | } |
| @@ -13,6 +13,13 export interface DescriptorImplArgs<S, T | |||
|
|
13 | 13 | dependencies?: DepsMap<S>; |
|
|
14 | 14 | } |
|
|
15 | 15 | |
|
|
16 | export const containerSelfDescriptor = <S>() => Object.freeze({ | |
|
|
17 | level: 0, | |
|
|
18 | activate(context: IActivationContext<S>) { | |
|
|
19 | return context.createChildContainer(); | |
|
|
20 | } | |
|
|
21 | }); | |
|
|
22 | ||
|
|
16 | 23 | |
|
|
17 | 24 | export class DescriptorImpl<S, T> implements Descriptor<S, T> { |
|
|
18 | 25 | |
| @@ -29,12 +29,10 const _emptySlot = Object.freeze({ | |||
|
|
29 | 29 | cleanup: noop, |
|
|
30 | 30 | }); |
|
|
31 | 31 | |
|
|
32 |
const _destroy = (item: |
|
|
|
32 | const _destroy = (item: unknown) => () => isDestroyable(item) && item.destroy() ; | |
|
|
33 | 33 | |
|
|
34 | 34 | const _makeCleanup = <T>(value: T, cleanup?: (item: T) => void) => |
|
|
35 | cleanup ? () => cleanup(value) : | |
|
|
36 | isDestroyable(value) ? () => _destroy(value) : | |
|
|
37 | noop; | |
|
|
35 | cleanup ? () => cleanup(value) : _destroy(value); | |
|
|
38 | 36 | |
|
|
39 | 37 | const newSlot = <T>(put: (item: ILifetimeSlot<T>) => void, remove: () => void): ILifetimeSlot<T> => ({ |
|
|
40 | 38 | has: () => false, |
| @@ -119,6 +117,12 export const emptyLifetime = <T>() => () | |||
|
|
119 | 117 | |
|
|
120 | 118 | let nextId = 1; |
|
|
121 | 119 | |
|
|
120 | export const containerLifetime = <T>() => { | |
|
|
121 | const slotId = nextId ++; | |
|
|
122 | return (context: ILifetimeContext) => | |
|
|
123 | context.ownerSlot<T>(slotId); | |
|
|
124 | }; | |
|
|
125 | ||
|
|
122 | 126 | export const hierarchyLifetime = <T>() => { |
|
|
123 | 127 | const slotId = nextId++; |
|
|
124 | 128 | return (context: ILifetimeContext) => |
| @@ -1,3 +1,4 | |||
|
|
1 | import { ContainerBuilder } from "./ContainerBuilder"; | |
|
|
1 | 2 | import { key } from "./traits"; |
|
|
2 | 3 | |
|
|
3 | 4 | export interface IDestroyable { |
| @@ -157,6 +158,10 export type ContainerServicesConstraint< | |||
|
|
157 | 158 | |
|
|
158 | 159 | export interface Descriptor<S, T> { |
|
|
159 | 160 | |
|
|
161 | /** The level of the service in the containers chain. | |
|
|
162 | */ | |
|
|
163 | readonly level: number; | |
|
|
164 | ||
|
|
160 | 165 | /** This flags indicates that this registration can be replaced or overridden. */ |
|
|
161 | 166 | readonly configurable?: boolean; |
|
|
162 | 167 | |
| @@ -170,6 +175,9 export interface Descriptor<S, T> { | |||
|
|
170 | 175 | |
|
|
171 | 176 | /** The context used to initialize lifetime instance {@linkcode ILifetime} */ |
|
|
172 | 177 | export interface ILifetimeContext { |
|
|
178 | ||
|
|
179 | ownerSlot<T>(slotId: string | number): ILifetimeSlot<T>; | |
|
|
180 | ||
|
|
173 | 181 | contextSlot<T>(slotId: string | number): ILifetimeSlot<T>; |
|
|
174 | 182 | |
|
|
175 | 183 | containerSlot<T>(slotId: string | number): ILifetimeSlot<T>; |
| @@ -181,6 +189,10 export interface IActivationContext<S> e | |||
|
|
181 | 189 | register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]): void; |
|
|
182 | 190 | |
|
|
183 | 191 | fail(error: unknown): never; |
|
|
192 | ||
|
|
193 | selfContainer(): ServiceLocator<S>; | |
|
|
194 | ||
|
|
195 | createChildContainer(): IContainerBuilder<S, Exclude<keyof S, ContainerKeys>>; | |
|
|
184 | 196 | } |
|
|
185 | 197 | |
|
|
186 | 198 | /** |
| @@ -196,10 +208,10 export type DescriptorMap<S> = { | |||
|
|
196 | 208 | |
|
|
197 | 209 | type ContainerKeys = keyof ContainerProvided<object>; |
|
|
198 | 210 | |
|
|
199 |
export type ContainerProvided<S extends |
|
|
|
200 |
container: ServiceLocator<Container |
|
|
|
211 | export type ContainerProvided<S, U extends keyof S = keyof S> = { | |
|
|
212 | container: ServiceLocator<ContainerProvided<S>>; | |
|
|
201 | 213 | |
|
|
202 |
childContainer: IContainerBuilder< |
|
|
|
214 | childContainer: IContainerBuilder<S, U>; | |
|
|
203 | 215 | }; |
|
|
204 | 216 | |
|
|
205 | 217 | |
| @@ -208,8 +220,8 export type ContainerProvided<S extends | |||
|
|
208 | 220 | * |
|
|
209 | 221 | * Сервисы, предоставляемые контейнером не могут быть null или undefined. |
|
|
210 | 222 | */ |
|
|
211 |
export type ContainerServices<S |
|
|
|
212 | [k in keyof S | ContainerKeys]: | |
|
|
223 | export type ContainerServices<S> = { | |
|
|
224 | [k in (keyof S) | ContainerKeys]: | |
|
|
213 | 225 | k extends ContainerKeys ? ContainerProvided<S>[k] : |
|
|
214 | 226 | k extends keyof S ? S[k] : never |
|
|
215 | 227 | }; |
| @@ -2,7 +2,7 | |||
|
|
2 | 2 | import { describe, it } from "mocha"; |
|
|
3 | 3 | import { Container } from "../Container"; |
|
|
4 | 4 | import { ContainerBuilder } from "../ContainerBuilder"; |
|
|
5 | import { ContainerServices, DepsMap, IContainerBuilder, Refs, Resolver } from "../interfaces"; | |
|
|
5 | import { ContainerServices, DepsMap, IContainerBuilder, Refs, Resolver, ServiceLocator } from "../interfaces"; | |
|
|
6 | 6 | import { fluent } from "../traits"; |
|
|
7 | 7 | |
|
|
8 | 8 | class Foo { |
| @@ -48,9 +48,9 if (refs && refs.name === "foo") { | |||
|
|
48 | 48 | refs.default; |
|
|
49 | 49 | } |
|
|
50 | 50 | |
|
|
51 | declare const x: ContainerServices<Services>; | |
|
|
51 | declare const x: ServiceLocator<ContainerServices<Services>>; | |
|
|
52 | 52 | |
|
|
53 | x.container.resolve("container"); | |
|
|
53 | x.resolve("container").resolve("container"); | |
|
|
54 | 54 | |
|
|
55 | 55 | |
|
|
56 | 56 | mmap({ |
General Comments 0
You need to be logged in to leave comments.
Login now
