##// END OF EJS Templates
Completely removed IoC annotations...
cin -
r135:03e32ec7c20b ioc ts support
parent child
Show More
@@ -0,0 +1,63
1 import { Descriptor, PartialServiceMap, ILifetime, ContainerKeys } from "../interfaces";
2 import { ActivationContext } from "../ActivationContext";
3 import { each } from "../../safe";
4 import { DependencyOptions, LazyDependencyOptions, Resolver } from "./interfaces";
5
6 export interface DescriptorImplArgs<S extends object, T> {
7 lifetime: ILifetime;
8
9 factory: (resolve: Resolver<S>) => T;
10
11 cleanup?: (item: T) => void;
12
13 overrides?: PartialServiceMap<S>;
14 }
15
16 export class DescriptorImpl<S extends object, T> implements Descriptor<S, T> {
17
18 private readonly _overrides?: PartialServiceMap<S>;
19
20 private readonly _lifetime: ILifetime;
21
22 private readonly _factory: (resolve: Resolver<S>) => T;
23
24 private readonly _cleanup?: (item: T) => void;
25
26 constructor(args: DescriptorImplArgs<S, T>) {
27 this._lifetime = args.lifetime;
28 this._factory = args.factory;
29 if (args.cleanup)
30 this._cleanup = args.cleanup;
31 if (args.overrides)
32 this._overrides = args.overrides;
33 }
34
35 activate(context: ActivationContext<S>): T {
36
37 if (this._lifetime.has())
38 return this._lifetime.get();
39
40 this._lifetime.initialize(context);
41
42 if (this._overrides)
43 each(this._overrides, (v, k) => context.register(k, v));
44
45 const resolve = (name: ContainerKeys<S>, opts?: DependencyOptions | LazyDependencyOptions) => {
46 if (opts && "lazy" in opts && opts.lazy) {
47 const c2 = context.clone();
48 return () => {
49 return opts.optional ? c2.resolve(name, opts.default) : c2.resolve(name);
50 };
51 } else {
52 return opts && opts.optional ? context.resolve(name, opts.default) : context.resolve(name);
53 }
54 };
55
56 const instance = this._factory.call(undefined, resolve);
57
58 this._lifetime.store(instance, this._cleanup);
59
60 return instance;
61 }
62
63 }
@@ -0,0 +1,58
1 import { Container } from "../Container";
2 import { argumentNotNull, each, isPrimitive, isPromise } from "../../safe";
3 import { DescriptorBuilder } from "./DescriptorBuilder";
4 import { RegistrationBuilder, FluentRegistrations } from "./interfaces";
5 import { Cancellation } from "../../Cancellation";
6
7 export class FluentConfiguration<S extends object, Y extends keyof S = keyof S> {
8
9 _builders: { [k in keyof S]?: RegistrationBuilder<S, S[k]> } = {};
10
11 register<K extends Y>(name: K, builder: RegistrationBuilder<S, S[K]>): FluentConfiguration<S, Exclude<Y, K>>;
12 register<K extends Y>(config: FluentRegistrations<K, S>): FluentConfiguration<S, Exclude<Y, K>>;
13 register<K extends Y>(nameOrConfig: K | FluentRegistrations<K, S>, builder?: RegistrationBuilder<S, S[K]>): FluentConfiguration<S, Exclude<Y, K>> {
14 if (isPrimitive(nameOrConfig)) {
15 argumentNotNull(builder, "builder");
16 this._builders[nameOrConfig] = builder;
17 } else {
18 each(nameOrConfig, (v, k) => this.register(k, v));
19 }
20
21 return this;
22 }
23
24 apply(target: Container<S>, ct = Cancellation.none) {
25
26 let pending = 1;
27
28 return new Promise((resolve, reject) => {
29 function guard(v: void | Promise<void>) {
30 if (isPromise(v))
31 v.catch(reject);
32 }
33
34 function complete() {
35 if (!--pending)
36 resolve();
37 }
38 each(this._builders, (v, k) => {
39 pending++;
40 const d = new DescriptorBuilder<S, any>(target,
41 result => {
42 target.register(k, result);
43 complete();
44 },
45 reject
46 );
47
48 try {
49 guard(v(d, ct));
50 } catch (e) {
51 reject(e);
52 }
53 });
54 complete();
55 });
56 }
57
58 }
@@ -0,0 +1,54
1 import { test } from "./TestTraits";
2 import { fluent } from "../di/traits";
3 import { Bar } from "../mock/Bar";
4 import { Container } from "../di/Container";
5 import { Foo } from "../mock/Foo";
6 import { Box } from "../mock/Box";
7 import { delay } from "../safe";
8
9 test("Simple fluent config", async t => {
10 const config = fluent<{ host: string; bar: Bar; foo: Foo }>()
11 .register({
12 host: it => it.value("example.com"),
13 bar: it => it.factory(resolve => new Bar({ host: resolve("host") }, "s-bar")),
14 foo: it => import("../mock/Foo").then(m => it.lifetime("container").factory(() => new m.Foo()))
15 });
16
17 const container = new Container<{ host: string; bar: Bar; foo: Foo; }>();
18 await config.apply(container);
19
20 t.equal(container.resolve("host"), "example.com", "The value should be resolved");
21 t.assert(container.resolve("bar"), "The service should de activated");
22 t.equal(container.resolve("foo"), container.resolve("foo"), "The service should be activated once");
23 });
24
25 test("Nested async configuration", async t => {
26 const container = await new Container<{
27 foo: Foo;
28 box: Box<Foo>
29 }>().fluent({
30 foo: it => delay(0).then(() => it.factory(() => new Foo())),
31 box: it => it.lifetime("context").factory($dependency => new Box($dependency("foo")))
32 });
33
34 t.assert(container.resolve("box").getValue(), "The dependency should be set");
35 t.equals(container.resolve("box").getValue(), container.resolve("box").getValue(), "The service should be activated once")
36 });
37
38 test("Bad fluent config", async t => {
39 try {
40 await new Container<{
41 foo: Foo;
42 box: Box<Foo>
43 }>().fluent({
44 foo: it => delay(0).then(() => it.factory(() => new Foo())),
45 box: it => it.lifetime("context")
46 .override("foo", () => { throw new Error("bad override"); })
47 .factory($dependency => new Box($dependency("foo")))
48 });
49 t.fail("Should throw");
50 } catch (e) {
51 t.pass("The configuration should fail");
52 t.equal(e.message, "bad override", "the error should pass");
53 }
54 });
@@ -15,6 +15,10 export interface ActivationContextInfo {
15
15
16 let nextId = 1;
16 let nextId = 1;
17
17
18 /** This class is created once per `Container.resolve` method call and used to
19 * cache dependencies and to track created instances. The activation context
20 * tracks services with `context` activation type.
21 */
18 export class ActivationContext<S extends object> {
22 export class ActivationContext<S extends object> {
19 _cache: MapOf<any>;
23 _cache: MapOf<any>;
20
24
@@ -30,6 +34,14 export class ActivationContext<S extends
30
34
31 _parent: ActivationContext<S> | undefined;
35 _parent: ActivationContext<S> | undefined;
32
36
37 /** Creates a new activation context with the specified parameters.
38 * @param container the container which starts the activation process
39 * @param services the initial service registrations
40 * @param name the name of the service being activated, this parameter is
41 * used for the debug purpose.
42 * @param service the service to activate, this parameter is used for the
43 * debug purpose.
44 */
33 constructor(container: Container<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) {
45 constructor(container: Container<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) {
34 this._name = name;
46 this._name = name;
35 this._service = service;
47 this._service = service;
@@ -39,16 +51,33 export class ActivationContext<S extends
39 this._container = container;
51 this._container = container;
40 }
52 }
41
53
54 /** the name of the current resolving dependency */
42 getName() {
55 getName() {
43 return this._name;
56 return this._name;
44 }
57 }
45
58
59 /** Returns the container for which 'resolve' method was called */
46 getContainer() {
60 getContainer() {
47 return this._container;
61 return this._container;
48 }
62 }
49
63
64 /** Resolves the specified dependency in the current context
65 * @param name The name of the dependency being resolved
66 */
50 resolve<K extends ContainerKeys<S>>(name: K): TypeOfService<S, K>;
67 resolve<K extends ContainerKeys<S>>(name: K): TypeOfService<S, K>;
68 /** Resolves the specified dependency with the specified default value if
69 * the dependency is missing.
70 *
71 * @param name The name of the dependency being resolved
72 * @param def A default value to return in case of the specified dependency
73 * is missing.
74 */
51 resolve<K extends ContainerKeys<S>, T>(name: K, def: T): TypeOfService<S, K> | T;
75 resolve<K extends ContainerKeys<S>, T>(name: K, def: T): TypeOfService<S, K> | T;
76 /** Resolves the specified dependency and returns undefined in case if the
77 * dependency is missing.
78 *
79 * @param name The name of the dependency being resolved
80 */
52 resolve<K extends ContainerKeys<S>>(name: K, def: undefined): TypeOfService<S, K> | undefined;
81 resolve<K extends ContainerKeys<S>>(name: K, def: undefined): TypeOfService<S, K> | undefined;
53 resolve<K extends ContainerKeys<S>, T>(name: K, def?: T): TypeOfService<S, K> | T | undefined {
82 resolve<K extends ContainerKeys<S>, T>(name: K, def?: T): TypeOfService<S, K> | T | undefined {
54 const d = this._services[name];
83 const d = this._services[name];
@@ -92,6 +121,7 export class ActivationContext<S extends
92 }
121 }
93 };
122 };
94 }
123 }
124
95 activate<T>(d: Descriptor<S, T>, name: string) {
125 activate<T>(d: Descriptor<S, T>, name: string) {
96 if (trace.isLogEnabled())
126 if (trace.isLogEnabled())
97 trace.log(`enter ${name} ${d}`);
127 trace.log(`enter ${name} ${d}`);
@@ -1,7 +1,7
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, ContainerProvided, ServiceLocator, ContainerServiceMap, ContainerKeys, TypeOfService, ILifetimeManager } from "./interfaces";
4 import { ServiceMap, Descriptor, PartialServiceMap, ServiceLocator, ContainerServiceMap, ContainerKeys, TypeOfService } 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";
@@ -9,13 +9,15 import { MapOf, IDestroyable } from "../
9 import { isDescriptor } from "./traits";
9 import { isDescriptor } from "./traits";
10 import { LifetimeManager } from "./LifetimeManager";
10 import { LifetimeManager } from "./LifetimeManager";
11 import { each } from "../safe";
11 import { each } from "../safe";
12 import { FluentRegistrations } from "./fluent/interfaces";
13 import { FluentConfiguration } from "./fluent/FluentConfiguration";
12
14
13 const trace = TraceSource.get("@implab/core/di/ActivationContext");
15 const trace = TraceSource.get("@implab/core/di/ActivationContext");
14
16
15 export class Container<S extends object = any> implements ServiceLocator<S>, IDestroyable {
17 export class Container<S extends object = any> implements ServiceLocator<S>, IDestroyable {
16 readonly _services: ContainerServiceMap<S>;
18 readonly _services: ContainerServiceMap<S>;
17
19
18 readonly _lifetimeManager: ILifetimeManager;
20 readonly _lifetimeManager: LifetimeManager;
19
21
20 readonly _cleanup: (() => void)[];
22 readonly _cleanup: (() => void)[];
21
23
@@ -126,6 +128,11 export class Container<S extends object
126 }
128 }
127 }
129 }
128
130
131 async fluent<K extends keyof S>(config: FluentRegistrations<K, S>, ct = Cancellation.none): Promise<this> {
132 await new FluentConfiguration<S>().register(config).apply(this, ct);
133 return this;
134 }
135
129 createChildContainer<S2 extends object = S>(): Container<S & S2> {
136 createChildContainer<S2 extends object = S>(): Container<S & S2> {
130 return new Container<S & S2>(this as any);
137 return new Container<S & S2>(this as any);
131 }
138 }
@@ -1,6 +1,6
1 import { IDestroyable, MapOf } from "../interfaces";
1 import { IDestroyable, MapOf } from "../interfaces";
2 import { argumentNotNull, isDestroyable } from "../safe";
2 import { argumentNotNull, isDestroyable, primitive, isNull, argumentNotEmptyString } from "../safe";
3 import { ILifetimeManager, ILifetime } from "./interfaces";
3 import { ILifetime } from "./interfaces";
4 import { ActivationContext } from "./ActivationContext";
4 import { ActivationContext } from "./ActivationContext";
5 import { Container } from "./Container";
5 import { Container } from "./Container";
6
6
@@ -12,7 +12,7 function safeCall(item: () => void) {
12 }
12 }
13 }
13 }
14
14
15 const emptyLifetime: ILifetime = {
15 const emptyLifetime: ILifetime = Object.freeze({
16 has() {
16 has() {
17 return false;
17 return false;
18 },
18 },
@@ -29,11 +29,11 const emptyLifetime: ILifetime = {
29 // does nothing
29 // does nothing
30 }
30 }
31
31
32 };
32 });
33
33
34 const unknownLifetime: ILifetime = {
34 const unknownLifetime: ILifetime = Object.freeze({
35 has() {
35 has() {
36 throw new Error("The lifetime is unknown");
36 return false;
37 },
37 },
38 initialize() {
38 initialize() {
39 throw new Error("Can't call initialize on the unknown lifetime object");
39 throw new Error("Can't call initialize on the unknown lifetime object");
@@ -44,11 +44,13 const unknownLifetime: ILifetime = {
44 store() {
44 store() {
45 throw new Error("Can't store a value in the unknown lifetime object");
45 throw new Error("Can't store a value in the unknown lifetime object");
46 }
46 }
47 }
47 });
48
48
49 let nextId = 0;
49 let nextId = 0;
50
50
51 export class LifetimeManager implements IDestroyable, ILifetimeManager {
51 const singletons: { [k in keyof any]: any; } = {};
52
53 export class LifetimeManager implements IDestroyable {
52 private _cleanup: (() => void)[] = [];
54 private _cleanup: (() => void)[] = [];
53 private _cache: MapOf<any> = {};
55 private _cache: MapOf<any> = {};
54 private _destroyed = false;
56 private _destroyed = false;
@@ -116,7 +118,7 export class LifetimeManager implements
116 if (_lifetime !== unknownLifetime)
118 if (_lifetime !== unknownLifetime)
117 throw new Error("Cyclic reference activation detected");
119 throw new Error("Cyclic reference activation detected");
118
120
119 _lifetime = context.getContainer().getLifetimeManager().create(context);
121 _lifetime = context.getContainer().getLifetimeManager().create();
120 },
122 },
121 get() {
123 get() {
122 return _lifetime.get();
124 return _lifetime.get();
@@ -151,7 +153,27 export class LifetimeManager implements
151 }
153 }
152
154
153 static singletonLifetime(typeId: string): ILifetime {
155 static singletonLifetime(typeId: string): ILifetime {
154 return emptyLifetime;
156 argumentNotEmptyString(typeId, "typeId");
157 let pending = false;
158 return {
159 has() {
160 return typeId in singletons;
161 },
162 get() {
163 if (!this.has())
164 throw new Error(`The instance ${typeId} doesn't exists`);
165 return singletons[typeId];
166 },
167 initialize() {
168 if (pending)
169 throw new Error("Cyclic reference detected");
170 pending = true;
171 },
172 store(item: any) {
173 singletons[typeId] = item;
174 pending = false;
175 }
176 };
155 }
177 }
156
178
157 static containerLifetime(container: Container<any>) {
179 static containerLifetime(container: Container<any>) {
@@ -160,7 +182,7 export class LifetimeManager implements
160 initialize(context: ActivationContext<any>) {
182 initialize(context: ActivationContext<any>) {
161 if (_lifetime !== unknownLifetime)
183 if (_lifetime !== unknownLifetime)
162 throw new Error("Cyclic reference detected");
184 throw new Error("Cyclic reference detected");
163 _lifetime = container.getLifetimeManager().create(context);
185 _lifetime = container.getLifetimeManager().create();
164 },
186 },
165 get() {
187 get() {
166 return _lifetime.get();
188 return _lifetime.get();
@@ -1,5 +1,5
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ServiceMap, PartialServiceMap, ILifetimeManager, ILifetime } from "./interfaces";
2 import { Descriptor, ServiceMap, PartialServiceMap, ILifetime } from "./interfaces";
3 import { isPrimitive, keys, isNull } from "../safe";
3 import { isPrimitive, keys, isNull } from "../safe";
4 import { TraceSource } from "../log/TraceSource";
4 import { TraceSource } from "../log/TraceSource";
5 import { isDescriptor } from "./traits";
5 import { isDescriptor } from "./traits";
@@ -1,46 +1,94
1 import { Resolver, LazyDependencyOptions, DependencyOptions } from "./interfaces";
1 import { Resolver, RegistrationBuilder } from "./interfaces";
2 import { Container } from "../Container";
2 import { Container } from "../Container";
3 import { Descriptor, ILifetime, ContainerKeys } from "../interfaces";
3 import { Descriptor, ILifetime, ActivationType, PartialServiceMap } from "../interfaces";
4 import { ActivationContext } from "../ActivationContext";
4 import { DescriptorImpl } from "./DescriptorImpl";
5 import { LifetimeManager } from "../LifetimeManager";
6 import { isString, each, isPrimitive, isPromise, oid } from "../../safe";
7
8 export class DescriptorBuilder<S extends object, T> {
9 private readonly _container: Container<S>;
10 private readonly _cb: (d: Descriptor<S, T>) => void;
11
12 private readonly _eb: (err: any) => void;
5
13
6 export class DescriptorBuilder<T, S extends object> {
14 private _lifetime = LifetimeManager.empty();
7 readonly _container: Container<S>;
15
8 readonly _cb: (d: Descriptor<S, T>) => void;
16 private _overrides?: PartialServiceMap<S>;
17
18 private _cleanup?: (item: T) => void;
9
19
10 constructor(container: Container<S>, cb: (d: Descriptor<S, T>) => void) {
20 private _factory?: (resolve: Resolver<S>) => T;
21
22 private _pending = 1;
23
24 private _failed = false;
25
26 constructor(container: Container<S>, cb: (d: Descriptor<S, T>) => void, eb: (err: any) => void) {
11 this._container = container;
27 this._container = container;
12 this._cb = cb;
28 this._cb = cb;
29 this._eb = eb;
30 }
31
32 build<T2>(): DescriptorBuilder<S, T2> {
33 this._defer();
34 return new DescriptorBuilder<S, T2>(this._container, () => this._complete(), err => this._fail(err));
13 }
35 }
14
36
15 factory(f: (resolve: Resolver<S>, activate: (lifetime: ILifetime, factory: () => any, cleanup?: (item: any) => void) => any) => T): void {
37 override<K extends keyof S>(name: K, builder: RegistrationBuilder<S, S[K]>): this;
16 this._cb({
38 override<K extends keyof S>(services: { [name in K]: RegistrationBuilder<S, S[K]> }): this;
17 activate(context: ActivationContext<S>) {
39 override<K extends keyof S>(nameOrServices: K | { [name in K]: RegistrationBuilder<S, S[K]> }, builder?: RegistrationBuilder<S, S[K]>): this {
18 const resolve = (name: ContainerKeys<S>, opts?: DependencyOptions | LazyDependencyOptions) => {
40 const overrides: PartialServiceMap<S> = this._overrides ?
19 if (opts && "lazy" in opts && opts.lazy) {
41 this._overrides :
20 const c2 = context.clone();
42 (this._overrides = {});
21 return () => {
43
22 return opts.optional ? c2.resolve(name, opts.default) : c2.resolve(name);
44 const guard = (v: void | Promise<void>) => {
23 };
45 if (isPromise(v))
24 } else {
46 v.catch(err => this._fail(err));
25 return opts && opts.optional ? context.resolve(name, opts.default) : context.resolve(name);
47 };
26 }
48
27 };
49 if (isPrimitive(nameOrServices)) {
50 if (builder) {
51 this._defer();
52 const d = new DescriptorBuilder<S, S[K]>(
53 this._container,
54 result => {
55 overrides[nameOrServices] = result;
56 this._complete();
57 },
58 err => this._fail(err)
59 );
28
60
29 const activate = (lifetime: ILifetime, factory: () => any, cleanup?: (item: any) => void) => {
61 try {
30 if (lifetime.has()) {
62 guard(builder(d));
31 return lifetime.get();
63 } catch (err) {
32 } else {
64 this._fail(err);
33 lifetime.initialize(context);
65 }
34 const instance = factory();
66 }
35 lifetime.store(instance, cleanup);
67 } else {
36 return instance;
68 each(nameOrServices, (v, k) => this.override(k, v));
37 }
69 }
70 return this;
71 }
38
72
39 };
73 lifetime(lifetime: "singleton", typeId: string): this;
74 lifetime(lifetime: ILifetime | Exclude<ActivationType, "singleton">): this;
75 lifetime(lifetime: ILifetime | ActivationType, typeId?: string): this {
76 if (isString(lifetime)) {
77 this._lifetime = this._resolveLifetime(lifetime, typeId);
78 } else {
79 this._lifetime = lifetime;
80 }
81 return this;
82 }
40
83
41 return f(resolve, activate);
84 cleanup(cb: (item: T) => void): this {
42 }
85 this._cleanup = cb;
43 });
86 return this;
87 }
88
89 factory(f: (resolve: Resolver<S>) => T): void {
90 this._factory = f;
91 this._complete();
44 }
92 }
45
93
46 value(v: T): void {
94 value(v: T): void {
@@ -51,4 +99,49 export class DescriptorBuilder<T, S exte
51 });
99 });
52 }
100 }
53
101
102 _resolveLifetime(activation: ActivationType, typeId?: string | object) {
103 switch (activation) {
104 case "container":
105 return LifetimeManager.containerLifetime(this._container);
106 case "hierarchy":
107 return LifetimeManager.hierarchyLifetime();
108 case "context":
109 return LifetimeManager.contextLifetime();
110 case "singleton":
111 if (!typeId)
112 throw Error("The singleton activation requires a typeId");
113
114 const _oid = isString(typeId) ? typeId : oid(typeId);
115
116 return LifetimeManager.singletonLifetime(_oid);
117 default:
118 return LifetimeManager.empty();
119 }
120 }
121
122 _defer() {
123 this._pending++;
124 }
125
126 _complete() {
127 if (--this._pending === 0) {
128 if (!this._factory)
129 throw new Error("The factory must be specified");
130
131 this._cb(new DescriptorImpl<S, T>({
132 lifetime: this._lifetime,
133 factory: this._factory,
134 overrides: this._overrides,
135 cleanup: this._cleanup
136 }));
137 }
138 }
139
140 _fail(err: any) {
141 if (!this._failed) {
142 this._failed = true;
143 this._eb.call(undefined, err);
144 }
145 }
146
54 }
147 }
@@ -1,6 +1,6
1 import { primitive } from "../../safe";
1 import { primitive } from "../../safe";
2 import { AnnotationBuilder } from "../Annotations";
2 import { TypeOfService, ContainerKeys, ActivationType, ILifetime } from "../interfaces";
3 import { ILifetime, TypeOfService, ContainerKeys } from "../interfaces";
3 import { ICancellation } from "../../interfaces";
4
4
5 export interface DependencyOptions {
5 export interface DependencyOptions {
6 optional?: boolean;
6 optional?: boolean;
@@ -22,10 +22,6 export type ExtractDependency<D, S> = D
22 export type WalkDependencies<D, S> = D extends primitive ? D :
22 export type WalkDependencies<D, S> = D extends primitive ? D :
23 { [K in keyof D]: ExtractDependency<D[K], S> };
23 { [K in keyof D]: ExtractDependency<D[K], S> };
24
24
25 export type ServiceModule<T, S extends object, M extends keyof any = "service"> = {
26 [m in M]: AnnotationBuilder<T, S>;
27 };
28
29 export type InferReferenceType<S extends object, K extends ContainerKeys<S>, O> = O extends { default: infer X } ? (TypeOfService<S, K> | X) :
25 export type InferReferenceType<S extends object, K extends ContainerKeys<S>, O> = O extends { default: infer X } ? (TypeOfService<S, K> | X) :
30 O extends { optional: true } ? (TypeOfService<S, K> | undefined) :
26 O extends { optional: true } ? (TypeOfService<S, K> | undefined) :
31 TypeOfService<S, K>;
27 TypeOfService<S, K>;
@@ -35,14 +31,22 export interface Resolver<S extends obje
35 <K extends ContainerKeys<S>, O extends DependencyOptions>(this: void, name: K, opts?: O): InferReferenceType<S, K, O>;
31 <K extends ContainerKeys<S>, O extends DependencyOptions>(this: void, name: K, opts?: O): InferReferenceType<S, K, O>;
36 }
32 }
37
33
38 export interface DescriptorBuilder<T, S extends object> {
34 export interface DescriptorBuilder<S extends object, T> {
39 service(service: AnnotationBuilder<T, S> | ServiceModule<T, S>): void;
35 factory(f: (resolve: Resolver<S>) => T): void;
36
37 build<T2>(): DescriptorBuilder<S, T2>;
40
38
41 factory(f: (resolve: Resolver<S>, activate: <T2>(lifetime: ILifetime, factory: () => T2, cleanup?: (item: T2) => void) => T2) => T): void;
39 override<K extends keyof S>(name: K, builder: RegistrationBuilder<S, S[K]>): this;
40 override<K extends keyof S>(services: { [name in K]: RegistrationBuilder<S, S[K]> }): this;
41
42 lifetime(lifetime: "singleton", typeId: any): this;
43 lifetime(lifetime: ILifetime | Exclude<ActivationType, "singleton">): this;
44
45 cleanup(cb: (item: T) => void): this;
42
46
43 value(v: T): void;
47 value(v: T): void;
44 }
48 }
45
49
46 export interface Configuration<S extends object, Y extends keyof S = keyof S> {
50 export type RegistrationBuilder<S extends object, T> = (d: DescriptorBuilder<S, T>, ct?: ICancellation) => void | Promise<void>;
47 register<K extends Y>(name: K, builder: (d: DescriptorBuilder<S[K], S>) => void): Configuration<S, Exclude<Y, K>>;
51
48 }
52 export type FluentRegistrations<K extends keyof S, S extends object> = { [k in K]: RegistrationBuilder<S, S[k]> };
@@ -37,10 +37,6 export type ContainerRegistered<S extend
37
37
38 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
38 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
39
39
40 export interface ILifetimeManager {
41 create(context: ActivationContext<any>): ILifetime;
42 }
43
44 /**
40 /**
45 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
41 * Интерфейс для управления жизнью экземпляра объекта. Каждая регистрация имеет
46 * свой собственный объект `ILifetime`, который создается при первой активации
42 * свой собственный объект `ILifetime`, который создается при первой активации
@@ -1,12 +1,11
1 import { isPrimitive } from "../safe";
1 import { isPrimitive } from "../safe";
2 import { Descriptor } from "./interfaces";
2 import { Descriptor } from "./interfaces";
3 import { Configuration } from "./fluent/Configuration";
3 import { FluentConfiguration } from "./fluent/FluentConfiguration";
4
5 export function isDescriptor(x: any): x is Descriptor {
4 export function isDescriptor(x: any): x is Descriptor {
6 return (!isPrimitive(x)) &&
5 return (!isPrimitive(x)) &&
7 (x.activate instanceof Function);
6 (x.activate instanceof Function);
8 }
7 }
9
8
10 export function configure<S extends object>() {
9 export function fluent<S extends object>() {
11 return new Configuration<S>();
10 return new FluentConfiguration<S>();
12 }
11 }
@@ -1,4 +1,4
1 import { ICancellable, Constructor, IDestroyable } from "./interfaces";
1 import { ICancellable, Constructor, IDestroyable, PromiseOrValue } from "./interfaces";
2 import { Cancellation } from "./Cancellation";
2 import { Cancellation } from "./Cancellation";
3
3
4 let _nextOid = 0;
4 let _nextOid = 0;
@@ -6,6 +6,8 const _oid = typeof Symbol === "function
6 Symbol("__implab__oid__") :
6 Symbol("__implab__oid__") :
7 "__implab__oid__";
7 "__implab__oid__";
8
8
9 export function oid(instance: null | undefined): undefined;
10 export function oid(instance: NonNullable<any>): string;
9 export function oid(instance: any): string | undefined {
11 export function oid(instance: any): string | undefined {
10 if (isNull(instance))
12 if (isNull(instance))
11 return undefined;
13 return undefined;
@@ -1,16 +1,19
1 import { Services } from "./services";
1 import { Services } from "./services";
2 import { configure } from "../di/traits";
2 import { fluent } from "../di/traits";
3 import { LifetimeManager } from "../di/LifetimeManager";
3
4 export default fluent<Services>().register({
5 host: it => it.value("example.com"),
4
6
5 export const config = configure<Services>()
7 bar2: it => Promise.all([import("./Foo"), import("./Bar")])
6 .register("host", s => s.value("example.com"))
8 .then(([{ Foo }, { Bar }]) => it
7 .register("bar2", bar2 => Promise.all([import("./Foo"), import("./Bar")])
9 .lifetime("container")
8 .then(([{ Foo }, { Bar }]) => {
10 .override({
9 const lifetime = LifetimeManager.hierarchyLifetime();
11 host: it2 => it2.value("simple.org"),
10
12 foo: it2 => it2.value(new Foo())
11 bar2.factory((resolve, activate) => {
13 })
14 .factory(resolve => {
12 const bar = new Bar({
15 const bar = new Bar({
13 foo: activate(lifetime, () => new Foo()),
16 foo: new Foo(),
14 nested: {
17 nested: {
15 lazy: resolve("foo", { lazy: true })
18 lazy: resolve("foo", { lazy: true })
16 },
19 },
@@ -18,6 +21,6 export const config = configure<Services
18 }, "some text");
21 }, "some text");
19 bar.setName(resolve("host"));
22 bar.setName(resolve("host"));
20 return bar;
23 return bar;
21 });
24 })
22 })
25 )
23 );
26 });
@@ -6,7 +6,6 import { ValueDescriptor } from "../di/V
6 import { Foo } from "../mock/Foo";
6 import { Foo } from "../mock/Foo";
7 import { Bar } from "../mock/Bar";
7 import { Bar } from "../mock/Bar";
8 import { isNull } from "../safe";
8 import { isNull } from "../safe";
9 import { Box } from "ts/mock/Box";
10
9
11 test("Container register/resolve tests", async t => {
10 test("Container register/resolve tests", async t => {
12 const container = new Container<{
11 const container = new Container<{
@@ -1,8 +1,6
1 {
1 {
2 "extends": "../tsconfig",
2 "extends": "../tsconfig",
3 "compilerOptions": {
3 "compilerOptions": {
4 //"rootDir": "ts",
5 "baseUrl": ".",
6 "rootDirs": [
4 "rootDirs": [
7 "ts",
5 "ts",
8 "../main/ts"
6 "../main/ts"
@@ -6,5 +6,6 define([
6 "./ObservableTests",
6 "./ObservableTests",
7 "./ContainerTests",
7 "./ContainerTests",
8 "./SafeTests",
8 "./SafeTests",
9 "./TextTests"
9 "./TextTests",
10 "./FluentContainerTests"
10 ]); No newline at end of file
11 ]);
1 NO CONTENT: file was removed
NO CONTENT: file was removed
1 NO CONTENT: file was removed
NO CONTENT: file was removed
General Comments 0
You need to be logged in to leave comments. Login now