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