| @@ -1,7 +1,6 | |||
|
|
1 | 1 | import { TraceSource } from "../log/TraceSource"; |
|
|
2 | 2 | import { argumentNotEmptyString } from "../safe"; |
|
|
3 | import { Descriptor, ContainerServiceMap, ContainerKeys, TypeOfService, ILifetime } from "./interfaces"; | |
|
|
4 | import { Container } from "./Container"; | |
|
|
3 | import { Descriptor, ContainerServiceMap, ContainerKeys, TypeOfService, ILifetime, ServiceContainer } from "./interfaces"; | |
|
|
5 | 4 | import { MapOf } from "../interfaces"; |
|
|
6 | 5 | |
|
|
7 | 6 | const trace = TraceSource.get("@implab/core/di/ActivationContext"); |
| @@ -30,7 +29,7 export class ActivationContext<S extends | |||
|
|
30 | 29 | |
|
|
31 | 30 | _service: Descriptor<S, any>; |
|
|
32 | 31 | |
|
|
33 | _container: Container<S>; | |
|
|
32 | _container: ServiceContainer<S>; | |
|
|
34 | 33 | |
|
|
35 | 34 | _parent: ActivationContext<S> | undefined; |
|
|
36 | 35 | |
| @@ -42,7 +41,7 export class ActivationContext<S extends | |||
|
|
42 | 41 | * @param service the service to activate, this parameter is used for the |
|
|
43 | 42 | * debug purpose. |
|
|
44 | 43 | */ |
|
|
45 | constructor(container: Container<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) { | |
|
|
44 | constructor(container: ServiceContainer<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) { | |
|
|
46 | 45 | this._name = name; |
|
|
47 | 46 | this._service = service; |
|
|
48 | 47 | this._visited = {}; |
| @@ -3,13 +3,12 import { | |||
|
|
3 | 3 | ActivationType, |
|
|
4 | 4 | ContainerKeys, |
|
|
5 | 5 | TypeOfService, |
|
|
6 | ILifetime | |
|
|
6 | ILifetime, ServiceContainer | |
|
|
7 | 7 | } from "./interfaces"; |
|
|
8 | 8 | |
|
|
9 | 9 | import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe"; |
|
|
10 | 10 | import { AggregateDescriptor } from "./AggregateDescriptor"; |
|
|
11 | 11 | import { ValueDescriptor } from "./ValueDescriptor"; |
|
|
12 | import { Container } from "./Container"; | |
|
|
13 | 12 | import { ReferenceDescriptor } from "./ReferenceDescriptor"; |
|
|
14 | 13 | import { TypeServiceDescriptor } from "./TypeServiceDescriptor"; |
|
|
15 | 14 | import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor"; |
| @@ -149,7 +148,7 export class Configuration<S extends obj | |||
|
|
149 | 148 | |
|
|
150 | 149 | _hasInnerDescriptors = false; |
|
|
151 | 150 | |
|
|
152 | readonly _container: Container<S>; | |
|
|
151 | readonly _container: ServiceContainer<S>; | |
|
|
153 | 152 | |
|
|
154 | 153 | _path: Array<string>; |
|
|
155 | 154 | |
| @@ -157,7 +156,7 export class Configuration<S extends obj | |||
|
|
157 | 156 | |
|
|
158 | 157 | _require: ModuleResolver | undefined; |
|
|
159 | 158 | |
|
|
160 | constructor(container: Container<S>) { | |
|
|
159 | constructor(container: ServiceContainer<S>) { | |
|
|
161 | 160 | argumentNotNull(container, "container"); |
|
|
162 | 161 | this._container = container; |
|
|
163 | 162 | this._path = []; |
| @@ -1,11 +1,11 | |||
|
|
1 | 1 | import { ActivationContext } from "./ActivationContext"; |
|
|
2 | 2 | import { ValueDescriptor } from "./ValueDescriptor"; |
|
|
3 | 3 | import { ActivationError } from "./ActivationError"; |
|
|
4 |
import { ServiceMap, Descriptor, PartialServiceMap, |
|
|
|
4 | import { ServiceMap, Descriptor, PartialServiceMap, ContainerServiceMap, ContainerKeys, TypeOfService, ServiceContainer } from "./interfaces"; | |
|
|
5 | 5 | import { TraceSource } from "../log/TraceSource"; |
|
|
6 | 6 | import { Configuration, RegistrationMap } from "./Configuration"; |
|
|
7 | 7 | import { Cancellation } from "../Cancellation"; |
|
|
8 |
import { IDestroyable |
|
|
|
8 | import { IDestroyable, ICancellation } from "../interfaces"; | |
|
|
9 | 9 | import { isDescriptor } from "./traits"; |
|
|
10 | 10 | import { LifetimeManager } from "./LifetimeManager"; |
|
|
11 | 11 | import { each, isString } from "../safe"; |
| @@ -14,7 +14,7 import { FluentConfiguration } from "./f | |||
|
|
14 | 14 | |
|
|
15 | 15 | const trace = TraceSource.get("@implab/core/di/ActivationContext"); |
|
|
16 | 16 | |
|
|
17 |
export class Container<S extends object = any> implements Service |
|
|
|
17 | export class Container<S extends object = any> implements ServiceContainer<S>, IDestroyable { | |
|
|
18 | 18 | readonly _services: ContainerServiceMap<S>; |
|
|
19 | 19 | |
|
|
20 | 20 | readonly _lifetimeManager: LifetimeManager; |
| @@ -91,6 +91,7 export class Container<S extends object | |||
|
|
91 | 91 | return this; |
|
|
92 | 92 | } |
|
|
93 | 93 | |
|
|
94 | /** @deprecated use getLifetimeManager() */ | |
|
|
94 | 95 | onDispose(callback: () => void) { |
|
|
95 | 96 | if (!(callback instanceof Function)) |
|
|
96 | 97 | throw new Error("The callback must be a function"); |
| @@ -134,13 +135,13 export class Container<S extends object | |||
|
|
134 | 135 | } |
|
|
135 | 136 | } |
|
|
136 | 137 | |
|
|
137 | applyConfig<S2 extends object>(config: Promise<{ default: ContainerConfiguration<S2>; }>, ct?: ICancellation): Promise<Container<S & S2>>; | |
|
|
138 | applyConfig<S2 extends object, P extends string>(config: Promise<{ [p in P]: ContainerConfiguration<S2>; }>, prop: P, ct?: ICancellation): Promise<Container<S & S2>>; | |
|
|
138 | applyConfig<S2 extends object>(config: Promise<{ default: ContainerConfiguration<S2>; }>, ct?: ICancellation): Promise<ServiceContainer<S & S2>>; | |
|
|
139 | applyConfig<S2 extends object, P extends string>(config: Promise<{ [p in P]: ContainerConfiguration<S2>; }>, prop: P, ct?: ICancellation): Promise<ServiceContainer<S & S2>>; | |
|
|
139 | 140 | async applyConfig<S2 extends object, P extends string>( |
|
|
140 | 141 | config: Promise<{ [p in P | "default"]: ContainerConfiguration<S2>; }>, |
|
|
141 | 142 | propOrCt?: P | ICancellation, |
|
|
142 | 143 | ct?: ICancellation |
|
|
143 | ): Promise<Container<S & S2>> { | |
|
|
144 | ): Promise<ServiceContainer<S & S2>> { | |
|
|
144 | 145 | const mod = await config; |
|
|
145 | 146 | |
|
|
146 | 147 | let _ct: ICancellation; |
| @@ -1,8 +1,7 | |||
|
|
1 | 1 | import { IDestroyable, MapOf } from "../interfaces"; |
|
|
2 |
import { argumentNotNull, isDestroyable, |
|
|
|
3 | import { ILifetime } from "./interfaces"; | |
|
|
2 | import { argumentNotNull, isDestroyable, argumentNotEmptyString } from "../safe"; | |
|
|
3 | import { ILifetime, ServiceContainer } from "./interfaces"; | |
|
|
4 | 4 | import { ActivationContext } from "./ActivationContext"; |
|
|
5 | import { Container } from "./Container"; | |
|
|
6 | 5 | |
|
|
7 | 6 | function safeCall(item: () => void) { |
|
|
8 | 7 | try { |
| @@ -48,7 +47,7 const unknownLifetime: ILifetime = Objec | |||
|
|
48 | 47 | |
|
|
49 | 48 | let nextId = 0; |
|
|
50 | 49 | |
|
|
51 |
const singletons: |
|
|
|
50 | const singletons: any = {}; | |
|
|
52 | 51 | |
|
|
53 | 52 | export class LifetimeManager implements IDestroyable { |
|
|
54 | 53 | private _cleanup: (() => void)[] = []; |
| @@ -176,7 +175,7 export class LifetimeManager implements | |||
|
|
176 | 175 | }; |
|
|
177 | 176 | } |
|
|
178 | 177 | |
|
|
179 | static containerLifetime(container: Container<any>) { | |
|
|
178 | static containerLifetime(container: ServiceContainer<any>) { | |
|
|
180 | 179 | let _lifetime = unknownLifetime; |
|
|
181 | 180 | return { |
|
|
182 | 181 | initialize(context: ActivationContext<any>) { |
| @@ -1,12 +1,11 | |||
|
|
1 | 1 | import { Resolver, RegistrationBuilder } from "./interfaces"; |
|
|
2 | import { Container } from "../Container"; | |
|
|
3 | import { Descriptor, ILifetime, ActivationType, PartialServiceMap } from "../interfaces"; | |
|
|
2 | import { Descriptor, ILifetime, ActivationType, PartialServiceMap, ServiceContainer } from "../interfaces"; | |
|
|
4 | 3 | import { DescriptorImpl } from "./DescriptorImpl"; |
|
|
5 | 4 | import { LifetimeManager } from "../LifetimeManager"; |
|
|
6 | 5 | import { isString, each, isPrimitive, isPromise, oid } from "../../safe"; |
|
|
7 | 6 | |
|
|
8 | 7 | export class DescriptorBuilder<S extends object, T> { |
|
|
9 | private readonly _container: Container<S>; | |
|
|
8 | private readonly _container: ServiceContainer<S>; | |
|
|
10 | 9 | private readonly _cb: (d: Descriptor<S, T>) => void; |
|
|
11 | 10 | |
|
|
12 | 11 | private readonly _eb: (err: any) => void; |
| @@ -23,7 +22,7 export class DescriptorBuilder<S extends | |||
|
|
23 | 22 | |
|
|
24 | 23 | private _failed = false; |
|
|
25 | 24 | |
|
|
26 | constructor(container: Container<S>, cb: (d: Descriptor<S, T>) => void, eb: (err: any) => void) { | |
|
|
25 | constructor(container: ServiceContainer<S>, cb: (d: Descriptor<S, T>) => void, eb: (err: any) => void) { | |
|
|
27 | 26 | this._container = container; |
|
|
28 | 27 | this._cb = cb; |
|
|
29 | 28 | this._eb = eb; |
| @@ -1,8 +1,8 | |||
|
|
1 | import { Container } from "../Container"; | |
|
|
2 | 1 |
|
|
|
3 | 2 | import { DescriptorBuilder } from "./DescriptorBuilder"; |
|
|
4 | 3 | import { RegistrationBuilder, FluentRegistrations, ContainerConfiguration } from "./interfaces"; |
|
|
5 | 4 | import { Cancellation } from "../../Cancellation"; |
|
|
5 | import { ServiceContainer } from "../interfaces"; | |
|
|
6 | 6 | |
|
|
7 | 7 | export class FluentConfiguration<S extends object, Y extends keyof S = keyof S> { |
|
|
8 | 8 | |
| @@ -29,13 +29,13 export class FluentConfiguration<S exten | |||
|
|
29 | 29 | return this.register(config); |
|
|
30 | 30 | } |
|
|
31 | 31 | |
|
|
32 |
apply<S |
|
|
|
32 | apply<S2 extends object>(target: ServiceContainer<S2>, ct = Cancellation.none) { | |
|
|
33 | 33 | |
|
|
34 | 34 | let pending = 1; |
|
|
35 | 35 | |
|
|
36 |
const _t2 = target as unknown as Container<S |
|
|
|
36 | const _t2 = target as unknown as ServiceContainer<S2 & S>; | |
|
|
37 | 37 | |
|
|
38 |
return new Promise<Container<S |
|
|
|
38 | return new Promise<ServiceContainer<S2 & S>>((resolve, reject) => { | |
|
|
39 | 39 | function guard(v: void | Promise<void>) { |
|
|
40 | 40 | if (isPromise(v)) |
|
|
41 | 41 | v.catch(reject); |
| @@ -47,7 +47,7 export class FluentConfiguration<S exten | |||
|
|
47 | 47 | } |
|
|
48 | 48 | each(this._builders, (v, k) => { |
|
|
49 | 49 | pending++; |
|
|
50 |
const d = new DescriptorBuilder<S |
|
|
|
50 | const d = new DescriptorBuilder<S2 & S, any>(_t2, | |
|
|
51 | 51 | result => { |
|
|
52 | 52 | _t2.register(k, result); |
|
|
53 | 53 | complete(); |
| @@ -1,7 +1,6 | |||
|
|
1 | 1 | import { primitive } from "../../safe"; |
|
|
2 | import { TypeOfService, ContainerKeys, ActivationType, ILifetime } from "../interfaces"; | |
|
|
2 | import { TypeOfService, ContainerKeys, ActivationType, ILifetime, ServiceContainer } from "../interfaces"; | |
|
|
3 | 3 | import { ICancellation } from "../../interfaces"; |
|
|
4 | import { Container } from "../Container"; | |
|
|
5 | 4 | |
|
|
6 | 5 | export interface DependencyOptions { |
|
|
7 | 6 | optional?: boolean; |
| @@ -49,7 +48,7 export interface DescriptorBuilder<S ext | |||
|
|
49 | 48 | } |
|
|
50 | 49 | |
|
|
51 | 50 | export interface ContainerConfiguration<S extends object> { |
|
|
52 | apply<S2 extends object>(target: Container<S2>, ct: ICancellation): Promise<Container<S2 & S>>; | |
|
|
51 | apply<S2 extends object>(target: ServiceContainer<S2>, ct?: ICancellation): Promise<ServiceContainer<S2 & S>>; | |
|
|
53 | 52 | } |
|
|
54 | 53 | |
|
|
55 | 54 | export type RegistrationBuilder<S extends object, T> = (d: DescriptorBuilder<S, T>, ct?: ICancellation) => void | Promise<void>; |
| @@ -1,4 +1,5 | |||
|
|
1 | 1 | import { ActivationContext } from "./ActivationContext"; |
|
|
2 | import { LifetimeManager } from "./LifetimeManager"; | |
|
|
2 | 3 | |
|
|
3 | 4 | export interface Descriptor<S extends object = any, T = any> { |
|
|
4 | 5 | activate(context: ActivationContext<S>): T; |
| @@ -26,6 +27,14 export interface ServiceLocator<S extend | |||
|
|
26 | 27 | resolve<K extends ContainerKeys<S>>(name: K, def?: TypeOfService<S, K>): TypeOfService<S, K>; |
|
|
27 | 28 | } |
|
|
28 | 29 | |
|
|
30 | export interface ServiceContainer<S extends object> extends ServiceLocator<S> { | |
|
|
31 | getLifetimeManager(): LifetimeManager; | |
|
|
32 | register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>): this; | |
|
|
33 | register(services: PartialServiceMap<S>): this; | |
|
|
34 | ||
|
|
35 | createChildContainer(): ServiceContainer<S>; | |
|
|
36 | } | |
|
|
37 | ||
|
|
29 | 38 | export interface ContainerProvided<S extends object> { |
|
|
30 | 39 | container: ServiceLocator<S>; |
|
|
31 | 40 | } |
| @@ -20,3 +20,7 export interface ChildServices extends S | |||
|
|
20 | 20 | |
|
|
21 | 21 | bar: Bar; |
|
|
22 | 22 | } |
|
|
23 | ||
|
|
24 | export interface FooServices { | |
|
|
25 | foo: Foo; | |
|
|
26 | } | |
| @@ -5,7 +5,8 import { Container } from "../di/Contain | |||
|
|
5 | 5 | import { Foo } from "../mock/Foo"; |
|
|
6 | 6 | import { Box } from "../mock/Box"; |
|
|
7 | 7 | import { delay } from "../safe"; |
|
|
8 | import { Services } from "../mock/services"; | |
|
|
8 | import { FooServices, Services } from "../mock/services"; | |
|
|
9 | import { ContainerConfiguration } from "../di/fluent/interfaces"; | |
|
|
9 | 10 | |
|
|
10 | 11 | test("Simple fluent config", async t => { |
|
|
11 | 12 | const config = fluent<{ host: string; bar: Bar; foo: Foo }>() |
| @@ -67,3 +68,13 test("Container applyConfig", async t => | |||
|
|
67 | 68 | |
|
|
68 | 69 | t.assert(container.resolve("host"), "Should resolve simple value"); |
|
|
69 | 70 | }); |
|
|
71 | ||
|
|
72 | test("Child container config", async t => { | |
|
|
73 | const container = await new Container<{}>().applyConfig(import("../mock/config")); | |
|
|
74 | ||
|
|
75 | const fooServices: ContainerConfiguration<FooServices> = (await import("../mock/config2")).default; | |
|
|
76 | ||
|
|
77 | const child = await fooServices.apply(container.createChildContainer()); | |
|
|
78 | ||
|
|
79 | t.assert(child.resolve("foo"), "foo should be resolved"); | |
|
|
80 | }); | |
General Comments 0
You need to be logged in to leave comments.
Login now
