##// END OF EJS Templates
working on fluent configuration
cin -
r132:0866c6259285 ioc ts support
parent child
Show More
@@ -0,0 +1,17
1 import { Container } from "../Container";
2 import { ExtractDependency, ServiceRecordBuilder } from "./interfaces";
3
4 export class ConfigBuilder<S extends object, Y extends keyof S = keyof S> {
5 register<K extends Y>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void | Promise<void>): ConfigBuilder<S, Exclude<Y, K>>;
6 register<K extends Y, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): ConfigBuilder<S, Exclude<Y, K>>;
7 register<K extends Y>(name: K, value: S[K], raw: true): ConfigBuilder<S, Exclude<Y, K>>;
8 register<K extends Y>(name: K, value: any, raw = false): ConfigBuilder<S, Exclude<Y, K>> {
9
10 return this;
11 }
12
13 apply(container: Container<S>): PromiseLike<void> {
14
15 return Promise.resolve();
16 }
17 }
@@ -0,0 +1,17
1 import { RegistrationBuilder } from "./RegistrationBuilder";
2
3 export class ConstructorBuilder<C extends new (...args: any[]) => any, S extends object>
4 extends RegistrationBuilder<InstanceType<C>, S> {
5
6 $type: C;
7
8 _params: any;
9
10 constructor(target: C, params: any) {
11 super();
12 this.$type = target;
13
14 this._params = params;
15 }
16
17 }
@@ -0,0 +1,14
1 import { RegistrationBuilder } from "./RegistrationBuilder";
2
3 export class FactoryBuilder<F extends (...args: any[]) => any, S extends object> extends RegistrationBuilder<ReturnType<F>, S> {
4 $factory: F;
5
6 _params: any;
7
8 constructor(target: F, params: any) {
9 super();
10
11 this.$factory = target;
12 this._params = params;
13 }
14 }
1 NO CONTENT: new file 100644
NO CONTENT: new file 100644
@@ -1,123 +1,130
1 import { TraceSource } from "../log/TraceSource";
1 import { TraceSource } from "../log/TraceSource";
2 import { argumentNotNull, argumentNotEmptyString } from "../safe";
2 import { argumentNotNull, argumentNotEmptyString } from "../safe";
3 import { Descriptor, ContainerServiceMap, ContainerKeys, ContainerResolve } from "./interfaces";
3 import { Descriptor, ContainerServiceMap, ContainerKeys, ContainerResolve } from "./interfaces";
4 import { Container } from "./Container";
4 import { Container } from "./Container";
5 import { MapOf } from "../interfaces";
5 import { MapOf } from "../interfaces";
6
6
7 const trace = TraceSource.get("@implab/core/di/ActivationContext");
7 const trace = TraceSource.get("@implab/core/di/ActivationContext");
8
8
9 export interface ActivationContextInfo {
9 export interface ActivationContextInfo {
10 name: string;
10 name: string;
11
11
12 service: string;
12 service: string;
13
13
14 }
14 }
15
15
16 export class ActivationContext<S extends object> {
16 export class ActivationContext<S extends object> {
17 _cache: MapOf<any>;
17 _cache: MapOf<any>;
18
18
19 _services: ContainerServiceMap<S>;
19 _services: ContainerServiceMap<S>;
20
20
21 _visited: MapOf<any>;
21 _visited: MapOf<any>;
22
22
23 _name: string;
23 _name: string;
24
24
25 _service: Descriptor<S, any>;
25 _service: Descriptor<S, any>;
26
26
27 _container: Container<S>;
27 _container: Container<S>;
28
28
29 _parent: ActivationContext<S> | undefined;
29 _parent: ActivationContext<S> | undefined;
30
30
31 constructor(container: Container<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) {
31 constructor(container: Container<S>, services: ContainerServiceMap<S>, name: string, service: Descriptor<S, any>) {
32 this._name = name;
32 this._name = name;
33 this._service = service;
33 this._service = service;
34 this._visited = {};
34 this._visited = {};
35 this._cache = {};
35 this._cache = {};
36 this._services = services;
36 this._services = services;
37 this._container = container;
37 this._container = container;
38 }
38 }
39
39
40 getName() {
40 getName() {
41 return this._name;
41 return this._name;
42 }
42 }
43
43
44 getContainer() {
44 getContainer() {
45 return this._container;
45 return this._container;
46 }
46 }
47
47
48 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>) {
48 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>) {
49 const d = this._services[name];
49 const d = this._services[name];
50
50
51 if (d !== undefined) {
51 if (d !== undefined) {
52 return this.activate(d, name.toString());
52 return this.activate(d, name.toString());
53 } else {
53 } else {
54 if (def !== undefined && def !== null)
54 if (def !== undefined && def !== null)
55 return def;
55 return def;
56 else
56 else
57 throw new Error(`Service ${name} not found`);
57 throw new Error(`Service ${name} not found`);
58 }
58 }
59 }
59 }
60
60
61 /**
61 /**
62 * registers services local to the the activation context
62 * registers services local to the the activation context
63 *
63 *
64 * @name{string} the name of the service
64 * @name{string} the name of the service
65 * @service{string} the service descriptor to register
65 * @service{string} the service descriptor to register
66 */
66 */
67 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>) {
67 register<K extends keyof S>(name: K, service: Descriptor<S, S[K]>) {
68 argumentNotEmptyString(name, "name");
68 argumentNotEmptyString(name, "name");
69
69
70 this._services[name] = service as any;
70 this._services[name] = service as any;
71 }
71 }
72
72
73 has(id: string) {
73 has(id: string) {
74 return id in this._cache;
74 return id in this._cache;
75 }
75 }
76
76
77 get<T>(id: string) {
77 get<T>(id: string) {
78 return this._cache[id];
78 return this._cache[id];
79 }
79 }
80
80
81 store(id: string, value: any) {
81 store(id: string, value: any) {
82 return (this._cache[id] = value);
82 return (this._cache[id] = value);
83 }
83 }
84
84
85 activate<T>(d: Descriptor<S, T>, name: string) {
85 activate<T>(d: Descriptor<S, T>, name: string) {
86 if (trace.isLogEnabled())
86 if (trace.isLogEnabled())
87 trace.log(`enter ${name} ${d}`);
87 trace.log(`enter ${name} ${d}`);
88
88
89 const ctx = this.enter(d, name);
89 const ctx = this.enter(d, name);
90 const v = d.activate(ctx);
90 const v = d.activate(ctx);
91
91
92 if (trace.isLogEnabled())
92 if (trace.isLogEnabled())
93 trace.log(`leave ${name}`);
93 trace.log(`leave ${name}`);
94
94
95 return v;
95 return v;
96 }
96 }
97
97
98 visit(id: string) {
98 visit(id: string) {
99 const count = this._visited[id] || 0;
99 const count = this._visited[id] || 0;
100 this._visited[id] = count + 1;
100 this._visited[id] = count + 1;
101 return count;
101 return count;
102 }
102 }
103
103
104 getStack(): ActivationContextInfo[] {
104 getStack(): ActivationContextInfo[] {
105 const stack = [{
105 const stack = [{
106 name: this._name,
106 name: this._name,
107 service: this._service.toString()
107 service: this._service.toString()
108 }];
108 }];
109
109
110 return this._parent ?
110 return this._parent ?
111 stack.concat(this._parent.getStack()) :
111 stack.concat(this._parent.getStack()) :
112 stack;
112 stack;
113 }
113 }
114
114
115 private enter(service: Descriptor<S, any>, name: string): this {
115 private enter(service: Descriptor<S, any>, name: string): this {
116 const clone = Object.create(this);
116 const clone = Object.create(this);
117 clone._name = name;
117 clone._name = name;
118 clone._services = Object.create(this._services);
118 clone._services = Object.create(this._services);
119 clone._parent = this;
119 clone._parent = this;
120 clone._service = service;
120 clone._service = service;
121 return clone;
121 return clone;
122 }
122 }
123
124 /** Creates a clone for the current context, used to protect it from modifications */
125 clone(): this {
126 const clone = Object.create(this);
127 clone._services = Object.create(this._services);
128 return clone;
129 }
123 }
130 }
@@ -1,25 +1,30
1 import { TypeRegistration } from "./Configuration";
1 import { TypeRegistration } from "./Configuration";
2 import { ExtractDependency } from "./fluent/interfaces";
2 import { ExtractDependency } from "./fluent/interfaces";
3 import { RegistrationBuilder } from "./fluent/RegistrationBuilder";
3
4
4 export class AnnotaionBuilder<T, S extends object> {
5 export class AnnotaionBuilder<T, S extends object> {
5 wire<P extends any[]>(...args: P) {
6 wire<P extends any[]>(...args: P) {
6 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
7 return <C extends new (...args: ExtractDependency<P, S>) => T>(constructor: C) => {
7
8
8 };
9 };
9 }
10 }
10
11
11 inject<P extends any[]>(...args: P) {
12 inject<P extends any[]>(...args: P) {
12 return <X extends { [m in M]: (...args: any) => any }, M extends keyof (T | X)>(
13 return <X extends { [m in M]: (...args: any) => any }, M extends keyof (T | X)>(
13 target: X,
14 target: X,
14 memberName: M,
15 memberName: M,
15 descriptor: TypedPropertyDescriptor< T[M] extends ((...args: ExtractDependency<P, S>) => any) ? any : never >
16 descriptor: TypedPropertyDescriptor< T[M] extends ((...args: ExtractDependency<P, S>) => any) ? any : never >
16 ) => {
17 ) => {
17
18
18 };
19 };
19 }
20 }
20
21
21 getDescriptor(): TypeRegistration<new () => T, S> {
22 getDescriptor(): TypeRegistration<new () => T, S> {
22 throw new Error();
23 throw new Error();
23 }
24 }
24
25
26 getRegistrationBuilder(): RegistrationBuilder<T, S> {
27 throw new Error();
28 }
29
25 }
30 }
@@ -1,415 +1,428
1 import {
1 import {
2 PartialServiceMap,
2 PartialServiceMap,
3 ActivationType,
3 ActivationType,
4 ContainerKeys,
4 ContainerKeys,
5 ContainerResolve
5 ContainerResolve
6 } from "./interfaces";
6 } from "./interfaces";
7
7
8 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
8 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get, primitive } from "../safe";
9 import { AggregateDescriptor } from "./AggregateDescriptor";
9 import { AggregateDescriptor } from "./AggregateDescriptor";
10 import { ValueDescriptor } from "./ValueDescriptor";
10 import { ValueDescriptor } from "./ValueDescriptor";
11 import { Container } from "./Container";
11 import { Container } from "./Container";
12 import { ReferenceDescriptor } from "./ReferenceDescriptor";
12 import { ReferenceDescriptor } from "./ReferenceDescriptor";
13 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
13 import { TypeServiceDescriptor } from "./TypeServiceDescriptor";
14 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
14 import { FactoryServiceDescriptor } from "./FactoryServiceDescriptor";
15 import { TraceSource } from "../log/TraceSource";
15 import { TraceSource } from "../log/TraceSource";
16 import { ConfigError } from "./ConfigError";
16 import { ConfigError } from "./ConfigError";
17 import { Cancellation } from "../Cancellation";
17 import { Cancellation } from "../Cancellation";
18 import { makeResolver } from "./ResolverHelper";
18 import { makeResolver } from "./ResolverHelper";
19 import { ICancellation } from "../interfaces";
19 import { ICancellation } from "../interfaces";
20 import { isDescriptor } from "./traits";
20 import { isDescriptor } from "./traits";
21 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
21 import { LazyReferenceDescriptor } from "./LazyReferenceDescriptor";
22 import { LifetimeManager } from "./LifetimeManager";
22
23
23 export interface RegistrationScope<S extends object> {
24 export interface RegistrationScope<S extends object> {
24
25
25 /** сервисы, которые регистрируются в контексте активации и таким образом
26 /** сервисы, которые регистрируются в контексте активации и таким образом
26 * могут переопределять ранее зарегистрированные сервисы. за это свойство
27 * могут переопределять ранее зарегистрированные сервисы. за это свойство
27 * нужно платить, кроме того порядок активации будет влиять на результат
28 * нужно платить, кроме того порядок активации будет влиять на результат
28 * разрешения зависимостей.
29 * разрешения зависимостей.
29 */
30 */
30 services?: RegistrationMap<S>;
31 services?: RegistrationMap<S>;
31 }
32 }
32
33
33 /**
34 /**
34 * Базовый интефейс конфигурации сервисов
35 * Базовый интефейс конфигурации сервисов
35 */
36 */
36 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
37 export interface ServiceRegistration<T, S extends object> extends RegistrationScope<S> {
37
38
38 activation?: ActivationType;
39 activation?: ActivationType;
39
40
40 params?: any;
41 params?: any;
41
42
42 inject?: object | object[];
43 inject?: object | object[];
43
44
44 cleanup?: ((instance: T) => void) | string;
45 cleanup?: ((instance: T) => void) | string;
45 }
46 }
46
47
47 export interface TypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
48 export interface TypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
48 $type: string | C;
49 $type: string | C;
49 params?: Registration<ConstructorParameters<C>, S>;
50 params?: Registration<ConstructorParameters<C>, S>;
50 }
51 }
51
52
52 export interface StrictTypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
53 export interface StrictTypeRegistration<C extends new (...args: any[]) => any, S extends object> extends ServiceRegistration<InstanceType<C>, S> {
53 $type: C;
54 $type: C;
54 params?: Registration<ConstructorParameters<C>, S>;
55 params?: Registration<ConstructorParameters<C>, S>;
55 }
56 }
56
57
57 export interface FactoryRegistration<F extends (...args: any[]) => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
58 export interface FactoryRegistration<F extends (...args: any[]) => any, S extends object> extends ServiceRegistration<ReturnType<F>, S> {
58 $factory: string | F;
59 $factory: string | F;
59 }
60 }
60
61
61 export interface ValueRegistration<T> {
62 export interface ValueRegistration<T> {
62 $value: T;
63 $value: T;
63 parse?: boolean;
64 parse?: boolean;
64 }
65 }
65
66
66 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
67 export interface DependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends RegistrationScope<S> {
67 $dependency: K;
68 $dependency: K;
68 lazy?: boolean;
69 lazy?: boolean;
69 optional?: boolean;
70 optional?: boolean;
70 default?: ContainerResolve<S, K>;
71 default?: ContainerResolve<S, K>;
71 }
72 }
72
73
73 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
74 export interface LazyDependencyRegistration<S extends object, K extends ContainerKeys<S> = ContainerKeys<S>> extends DependencyRegistration<S, K> {
74 lazy: true;
75 lazy: true;
75 }
76 }
76
77
77 type OfType<K extends keyof S, S, T> = Extract<{ [k in K]: T}, S>;
78
79 export type Registration<T, S extends object> = T extends primitive ? T :
78 export type Registration<T, S extends object> = T extends primitive ? T :
80 (
79 (
81 T |
80 T |
82 { [k in keyof T]: Registration<T[k], S> } |
81 { [k in keyof T]: Registration<T[k], S> } |
83 TypeRegistration<new (...args: any[]) => T, S> |
82 TypeRegistration<new (...args: any[]) => T, S> |
84 FactoryRegistration<(...args: any[]) => T, S> |
83 FactoryRegistration<(...args: any[]) => T, S> |
85 ValueRegistration<any> |
84 ValueRegistration<any> |
86 DependencyRegistration<S, keyof S>
85 DependencyRegistration<S, keyof S>
87 );
86 );
88
87
89 export type RegistrationMap<S extends object> = {
88 export type RegistrationMap<S extends object> = {
90 [k in keyof S]?: Registration<S[k], S>;
89 [k in keyof S]?: Registration<S[k], S>;
91 };
90 };
92
91
93 const _activationTypes: { [k in ActivationType]: number; } = {
92 const _activationTypes: { [k in ActivationType]: number; } = {
94 singleton: 1,
93 singleton: 1,
95 container: 2,
94 container: 2,
96 hierarchy: 3,
95 hierarchy: 3,
97 context: 4,
96 context: 4,
98 call: 5
97 call: 5
99 };
98 };
100
99
101 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
100 export function isTypeRegistration(x: any): x is TypeRegistration<new () => any, any> {
102 return (!isPrimitive(x)) && ("$type" in x);
101 return (!isPrimitive(x)) && ("$type" in x);
103 }
102 }
104
103
105 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
104 export function isFactoryRegistration(x: any): x is FactoryRegistration<() => any, any> {
106 return (!isPrimitive(x)) && ("$factory" in x);
105 return (!isPrimitive(x)) && ("$factory" in x);
107 }
106 }
108
107
109 export function isValueRegistration(x: any): x is ValueRegistration<any> {
108 export function isValueRegistration(x: any): x is ValueRegistration<any> {
110 return (!isPrimitive(x)) && ("$value" in x);
109 return (!isPrimitive(x)) && ("$value" in x);
111 }
110 }
112
111
113 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
112 export function isDependencyRegistration<S extends object>(x: any): x is DependencyRegistration<S, keyof S> {
114 return (!isPrimitive(x)) && ("$dependency" in x);
113 return (!isPrimitive(x)) && ("$dependency" in x);
115 }
114 }
116
115
117 export function isActivationType(x: string): x is ActivationType {
116 export function isActivationType(x: string): x is ActivationType {
118 return typeof x === "string" && x in _activationTypes;
117 return typeof x === "string" && x in _activationTypes;
119 }
118 }
120
119
121 const trace = TraceSource.get("@implab/core/di/Configuration");
120 const trace = TraceSource.get("@implab/core/di/Configuration");
122 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
121 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
123 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
122 async function mapAll(data: any, map?: (v: any, k: string) => any): Promise<any>;
124 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
123 async function mapAll(data: any, map?: (v: any, k: any) => any): Promise<any> {
125 if (data instanceof Array) {
124 if (data instanceof Array) {
126 return Promise.all(map ? data.map(map) : data);
125 return Promise.all(map ? data.map(map) : data);
127 } else {
126 } else {
128 const keys = Object.keys(data);
127 const keys = Object.keys(data);
129
128
130 const o: any = {};
129 const o: any = {};
131
130
132 await Promise.all(keys.map(async k => {
131 await Promise.all(keys.map(async k => {
133 const v = map ? map(data[k], k) : data[k];
132 const v = map ? map(data[k], k) : data[k];
134 o[k] = isPromise(v) ? await v : v;
133 o[k] = isPromise(v) ? await v : v;
135 }));
134 }));
136
135
137 return o;
136 return o;
138 }
137 }
139 }
138 }
140
139
141 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
140 export type ModuleResolver = (moduleName: string, ct?: ICancellation) => any;
142
141
143 export class Configuration<S extends object> {
142 export class Configuration<S extends object> {
144
143
145 _hasInnerDescriptors = false;
144 _hasInnerDescriptors = false;
146
145
147 readonly _container: Container<S>;
146 readonly _container: Container<S>;
148
147
149 _path: Array<string>;
148 _path: Array<string>;
150
149
151 _configName: string | undefined;
150 _configName: string | undefined;
152
151
153 _require: ModuleResolver | undefined;
152 _require: ModuleResolver | undefined;
154
153
155 constructor(container: Container<S>) {
154 constructor(container: Container<S>) {
156 argumentNotNull(container, "container");
155 argumentNotNull(container, "container");
157 this._container = container;
156 this._container = container;
158 this._path = [];
157 this._path = [];
159 }
158 }
160
159
161 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
160 async loadConfiguration(moduleName: string, contextRequire?: any, ct = Cancellation.none) {
162 argumentNotEmptyString(moduleName, "moduleName");
161 argumentNotEmptyString(moduleName, "moduleName");
163
162
164 trace.log(
163 trace.log(
165 "loadConfiguration moduleName={0}, contextRequire={1}",
164 "loadConfiguration moduleName={0}, contextRequire={1}",
166 moduleName,
165 moduleName,
167 contextRequire ? typeof (contextRequire) : "<nil>"
166 contextRequire ? typeof (contextRequire) : "<nil>"
168 );
167 );
169
168
170 this._configName = moduleName;
169 this._configName = moduleName;
171
170
172 const r = await makeResolver(undefined, contextRequire);
171 const r = await makeResolver(undefined, contextRequire);
173
172
174 const config = await r(moduleName, ct);
173 const config = await r(moduleName, ct);
175
174
176 await this._applyConfiguration(
175 await this._applyConfiguration(
177 config,
176 config,
178 await makeResolver(moduleName, contextRequire),
177 await makeResolver(moduleName, contextRequire),
179 ct
178 ct
180 );
179 );
181 }
180 }
182
181
183 async applyConfiguration(data: RegistrationMap<S>, contextRequire?: any, ct = Cancellation.none) {
182 async applyConfiguration(data: RegistrationMap<S>, contextRequire?: any, ct = Cancellation.none) {
184 argumentNotNull(data, "data");
183 argumentNotNull(data, "data");
185
184
186 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
185 await this._applyConfiguration(data, await makeResolver(void (0), contextRequire), ct);
187 }
186 }
188
187
189 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
188 async _applyConfiguration(data: RegistrationMap<S>, resolver?: ModuleResolver, ct = Cancellation.none) {
190 trace.log("applyConfiguration");
189 trace.log("applyConfiguration");
191
190
192 this._configName = "$";
191 this._configName = "$";
193
192
194 if (resolver)
193 if (resolver)
195 this._require = resolver;
194 this._require = resolver;
196
195
197 let services: PartialServiceMap<S>;
196 let services: PartialServiceMap<S>;
198
197
199 try {
198 try {
200 services = await this._visitRegistrations(data, "$");
199 services = await this._visitRegistrations(data, "$");
201 } catch (e) {
200 } catch (e) {
202 throw this._makeError(e);
201 throw this._makeError(e);
203 }
202 }
204
203
205 this._container.register(services);
204 this._container.register(services);
206 }
205 }
207
206
208 _makeError(inner: any) {
207 _makeError(inner: any) {
209 const e = new ConfigError("Failed to load configuration", inner);
208 const e = new ConfigError("Failed to load configuration", inner);
210 e.configName = this._configName || "<inline>";
209 e.configName = this._configName || "<inline>";
211 e.path = this._makePath();
210 e.path = this._makePath();
212 return e;
211 return e;
213 }
212 }
214
213
215 _makePath() {
214 _makePath() {
216 return this._path
215 return this._path
217 .reduce(
216 .reduce(
218 (prev, cur) => typeof cur === "number" ?
217 (prev, cur) => typeof cur === "number" ?
219 `${prev}[${cur}]` :
218 `${prev}[${cur}]` :
220 `${prev}.${cur}`
219 `${prev}.${cur}`
221 )
220 )
222 .toString();
221 .toString();
223 }
222 }
224
223
225 async _resolveType(moduleName: string, localName: string) {
224 async _resolveType(moduleName: string, localName: string) {
226 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
225 trace.log("resolveType moduleName={0}, localName={1}", moduleName, localName);
227 try {
226 try {
228 const m = await this._loadModule(moduleName);
227 const m = await this._loadModule(moduleName);
229 return localName ? get(localName, m) : m;
228 return localName ? get(localName, m) : m;
230 } catch (e) {
229 } catch (e) {
231 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
230 trace.error("Failed to resolve type moduleName={0}, localName={1}", moduleName, localName);
232 throw e;
231 throw e;
233 }
232 }
234 }
233 }
235
234
236 _loadModule(moduleName: string) {
235 _loadModule(moduleName: string) {
237 trace.debug("loadModule {0}", moduleName);
236 trace.debug("loadModule {0}", moduleName);
238 if (!this._require)
237 if (!this._require)
239 throw new Error("Module loader isn't specified");
238 throw new Error("Module loader isn't specified");
240
239
241 return this._require(moduleName);
240 return this._require(moduleName);
242 }
241 }
243
242
244 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
243 async _visitRegistrations(data: RegistrationMap<S>, name: string) {
245 this._enter(name);
244 this._enter(name);
246
245
247 if (data.constructor &&
246 if (data.constructor &&
248 data.constructor.prototype !== Object.prototype)
247 data.constructor.prototype !== Object.prototype)
249 throw new Error("Configuration must be a simple object");
248 throw new Error("Configuration must be a simple object");
250
249
251 const services = await mapAll(data, async (v, k) => {
250 const services = await mapAll(data, async (v, k) => {
252 const d = await this._visit(v, k.toString());
251 const d = await this._visit(v, k.toString());
253 return isDescriptor(d) ? d : new AggregateDescriptor(d);
252 return isDescriptor(d) ? d : new AggregateDescriptor(d);
254 }) as PartialServiceMap<S>;
253 }) as PartialServiceMap<S>;
255
254
256 this._leave();
255 this._leave();
257
256
258 return services;
257 return services;
259 }
258 }
260
259
261 _enter(name: string) {
260 _enter(name: string) {
262 this._path.push(name.toString());
261 this._path.push(name.toString());
263 trace.debug(">{0}", name);
262 trace.debug(">{0}", name);
264 }
263 }
265
264
266 _leave() {
265 _leave() {
267 const name = this._path.pop();
266 const name = this._path.pop();
268 trace.debug("<{0}", name);
267 trace.debug("<{0}", name);
269 }
268 }
270
269
271 async _visit(data: any, name: string): Promise<any> {
270 async _visit(data: any, name: string): Promise<any> {
272 if (isPrimitive(data) || isDescriptor(data))
271 if (isPrimitive(data) || isDescriptor(data))
273 return data;
272 return data;
274
273
275 if (isDependencyRegistration<S>(data)) {
274 if (isDependencyRegistration<S>(data)) {
276 return this._visitDependencyRegistration(data, name);
275 return this._visitDependencyRegistration(data, name);
277 } else if (isValueRegistration(data)) {
276 } else if (isValueRegistration(data)) {
278 return this._visitValueRegistration(data, name);
277 return this._visitValueRegistration(data, name);
279 } else if (isTypeRegistration(data)) {
278 } else if (isTypeRegistration(data)) {
280 return this._visitTypeRegistration(data, name);
279 return this._visitTypeRegistration(data, name);
281 } else if (isFactoryRegistration(data)) {
280 } else if (isFactoryRegistration(data)) {
282 return this._visitFactoryRegistration(data, name);
281 return this._visitFactoryRegistration(data, name);
283 } else if (data instanceof Array) {
282 } else if (data instanceof Array) {
284 return this._visitArray(data, name);
283 return this._visitArray(data, name);
285 }
284 }
286
285
287 return this._visitObject(data, name);
286 return this._visitObject(data, name);
288 }
287 }
289
288
290 async _visitObject(data: any, name: string) {
289 async _visitObject(data: any, name: string) {
291 if (data.constructor &&
290 if (data.constructor &&
292 data.constructor.prototype !== Object.prototype)
291 data.constructor.prototype !== Object.prototype)
293 return new ValueDescriptor(data);
292 return new ValueDescriptor(data);
294
293
295 this._enter(name);
294 this._enter(name);
296
295
297 const v = await mapAll(data, delegate(this, "_visit"));
296 const v = await mapAll(data, delegate(this, "_visit"));
298
297
299 // TODO: handle inline descriptors properly
298 // TODO: handle inline descriptors properly
300 // const ex = {
299 // const ex = {
301 // activate(ctx) {
300 // activate(ctx) {
302 // const value = ctx.activate(this.prop, "prop");
301 // const value = ctx.activate(this.prop, "prop");
303 // // some code
302 // // some code
304 // },
303 // },
305 // // will be turned to ReferenceDescriptor
304 // // will be turned to ReferenceDescriptor
306 // prop: { $dependency: "depName" }
305 // prop: { $dependency: "depName" }
307 // };
306 // };
308
307
309 this._leave();
308 this._leave();
310 return v;
309 return v;
311 }
310 }
312
311
313 async _visitArray(data: any[], name: string) {
312 async _visitArray(data: any[], name: string) {
314 if (data.constructor &&
313 if (data.constructor &&
315 data.constructor.prototype !== Array.prototype)
314 data.constructor.prototype !== Array.prototype)
316 return new ValueDescriptor(data);
315 return new ValueDescriptor(data);
317
316
318 this._enter(name);
317 this._enter(name);
319
318
320 const v = await mapAll(data, delegate(this, "_visit"));
319 const v = await mapAll(data, delegate(this, "_visit"));
321 this._leave();
320 this._leave();
322
321
323 return v;
322 return v;
324 }
323 }
325
324
326 _makeServiceParams(data: ServiceRegistration<any, S>) {
325 _makeServiceParams(data: ServiceRegistration<any, S>) {
327 const opts: any = {
326 const opts: any = {
328 owner: this._container
329 };
327 };
330 if (data.services)
328 if (data.services)
331 opts.services = this._visitRegistrations(data.services, "services");
329 opts.services = this._visitRegistrations(data.services, "services");
332
330
333 if (data.inject) {
331 if (data.inject) {
334 this._enter("inject");
332 this._enter("inject");
335 opts.inject = mapAll(
333 opts.inject = mapAll(
336 data.inject instanceof Array ?
334 data.inject instanceof Array ?
337 data.inject :
335 data.inject :
338 [data.inject],
336 [data.inject],
339 delegate(this, "_visitObject")
337 delegate(this, "_visitObject")
340 );
338 );
341 this._leave();
339 this._leave();
342 }
340 }
343
341
344 if ("params" in data)
342 if ("params" in data)
345 opts.params = data.params instanceof Array ?
343 opts.params = data.params instanceof Array ?
346 this._visitArray(data.params, "params") :
344 this._visitArray(data.params, "params") :
347 this._visit(data.params, "params");
345 this._visit(data.params, "params");
348
346
349 if (data.activation) {
347 if (data.activation) {
350 opts.activation = data.activation;
348 opts.activation = this._getLifetimeManager(data.activation);
351 }
349 }
352
350
353 if (data.cleanup)
351 if (data.cleanup)
354 opts.cleanup = data.cleanup;
352 opts.cleanup = data.cleanup;
355
353
356 return opts;
354 return opts;
357 }
355 }
358
356
359 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
357 async _visitValueRegistration<T>(data: ValueRegistration<T>, name: string) {
360 this._enter(name);
358 this._enter(name);
361 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
359 const d = data.parse ? new AggregateDescriptor(data.$value) : new ValueDescriptor(data.$value);
362 this._leave();
360 this._leave();
363 return d;
361 return d;
364 }
362 }
365
363
366 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
364 async _visitDependencyRegistration<K extends keyof S>(data: DependencyRegistration<S, K>, name: string) {
367 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
365 argumentNotEmptyString(data && data.$dependency, "data.$dependency");
368 this._enter(name);
366 this._enter(name);
369 const options = {
367 const options = {
370 name: data.$dependency,
368 name: data.$dependency,
371 optional: data.optional,
369 optional: data.optional,
372 default: data.default,
370 default: data.default,
373 services: data.services && await this._visitRegistrations(data.services, "services")
371 services: data.services && await this._visitRegistrations(data.services, "services")
374 };
372 };
375 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
373 const d = data.lazy ? new LazyReferenceDescriptor<S, K>(options) : new ReferenceDescriptor<S, K>(options);
376 this._leave();
374 this._leave();
377 return d;
375 return d;
378 }
376 }
379
377
380 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
378 async _visitTypeRegistration(data: TypeRegistration<new () => any, S>, name: string) {
381 argumentNotNull(data.$type, "data.$type");
379 argumentNotNull(data.$type, "data.$type");
382 this._enter(name);
380 this._enter(name);
383
381
384 const opts = this._makeServiceParams(data);
382 const opts = this._makeServiceParams(data);
385 if (data.$type instanceof Function) {
383 if (data.$type instanceof Function) {
386 opts.type = data.$type;
384 opts.type = data.$type;
387 } else {
385 } else {
388 const [moduleName, typeName] = data.$type.split(":", 2);
386 const [moduleName, typeName] = data.$type.split(":", 2);
389 opts.type = this._resolveType(moduleName, typeName);
387 opts.type = this._resolveType(moduleName, typeName);
390 }
388 }
391
389
392 const d = new TypeServiceDescriptor<S, any, any[]>(
390 const d = new TypeServiceDescriptor<S, any, any[]>(
393 await mapAll(opts)
391 await mapAll(opts)
394 );
392 );
395
393
396 this._leave();
394 this._leave();
397
395
398 return d;
396 return d;
399 }
397 }
400
398
401 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
399 async _visitFactoryRegistration(data: FactoryRegistration<() => any, S>, name: string) {
402 argumentOfType(data.$factory, Function, "data.$factory");
400 argumentOfType(data.$factory, Function, "data.$factory");
403 this._enter(name);
401 this._enter(name);
404
402
405 const opts = this._makeServiceParams(data);
403 const opts = this._makeServiceParams(data);
406 opts.factory = data.$factory;
404 opts.factory = data.$factory;
407
405
408 const d = new FactoryServiceDescriptor<S, any, any[]>(
406 const d = new FactoryServiceDescriptor<S, any, any[]>(
409 await mapAll(opts)
407 await mapAll(opts)
410 );
408 );
411
409
412 this._leave();
410 this._leave();
413 return d;
411 return d;
414 }
412 }
413
414 _getLifetimeManager(activation: ActivationType) {
415 switch (activation) {
416 case "container":
417 return this._container.getLifetimeManager();
418 case "hierarchy":
419 return LifetimeManager.hierarchyLifetime;
420 case "context":
421 return LifetimeManager.contextLifetime;
422 case "singleton":
423 return LifetimeManager.singletonLifetime;
424 default:
425 return LifetimeManager.empty;
426 }
427 }
415 }
428 }
@@ -1,84 +1,84
1 import { argumentNotEmptyString, each } from "../safe";
1 import { argumentNotEmptyString, each } from "../safe";
2 import { ActivationContext } from "./ActivationContext";
2 import { ActivationContext } from "./ActivationContext";
3 import { Descriptor, PartialServiceMap, ContainerResolve, ContainerKeys } from "./interfaces";
3 import { Descriptor, PartialServiceMap, ContainerResolve, ContainerKeys } from "./interfaces";
4 import { ActivationError } from "./ActivationError";
4 import { ActivationError } from "./ActivationError";
5
5
6 export interface ReferenceDescriptorParams<S extends object, K extends ContainerKeys<S>> {
6 export interface ReferenceDescriptorParams<S extends object, K extends ContainerKeys<S>> {
7 name: K;
7 name: K;
8 optional?: boolean;
8 optional?: boolean;
9 default?: ContainerResolve<S, K>;
9 default?: ContainerResolve<S, K>;
10 services?: PartialServiceMap<S>;
10 services?: PartialServiceMap<S>;
11 }
11 }
12
12
13 export class LazyReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
13 export class LazyReferenceDescriptor<S extends object = any, K extends ContainerKeys<S> = ContainerKeys<S>>
14 implements Descriptor<S, ((args?: PartialServiceMap<S>) => ContainerResolve<S, K>)> {
14 implements Descriptor<S, ((args?: PartialServiceMap<S>) => ContainerResolve<S, K>)> {
15
15
16 _name: K;
16 _name: K;
17
17
18 _optional = false;
18 _optional = false;
19
19
20 _default: ContainerResolve<S, K> | undefined;
20 _default: ContainerResolve<S, K> | undefined;
21
21
22 _services: PartialServiceMap<S>;
22 _services: PartialServiceMap<S>;
23
23
24 constructor(opts: ReferenceDescriptorParams<S, K>) {
24 constructor(opts: ReferenceDescriptorParams<S, K>) {
25 argumentNotEmptyString(opts && opts.name, "opts.name");
25 argumentNotEmptyString(opts && opts.name, "opts.name");
26 this._name = opts.name;
26 this._name = opts.name;
27 this._optional = !!opts.optional;
27 this._optional = !!opts.optional;
28 this._default = opts.default;
28 this._default = opts.default;
29
29
30 this._services = (opts.services || {}) as PartialServiceMap<S>;
30 this._services = (opts.services || {}) as PartialServiceMap<S>;
31 }
31 }
32
32
33 activate(context: ActivationContext<S>) {
33 activate(context: ActivationContext<S>) {
34 // добавляем сервисы
34 // добавляем сервисы
35 if (this._services) {
35 if (this._services) {
36 each(this._services, (v, k) => context.register(k, v));
36 each(this._services, (v, k) => context.register(k, v));
37 }
37 }
38
38
39 const saved = context.clone();
39 const saved = context.clone();
40
40
41 return (cfg?: PartialServiceMap<S>) => {
41 return (cfg?: PartialServiceMap<S>) => {
42 // защищаем контекст на случай исключения в процессе
42 // защищаем контекст на случай исключения в процессе
43 // активации
43 // активации
44 const ct = saved.clone();
44 const ct = cfg ? saved.clone() : saved;
45 try {
45 try {
46 if (cfg) {
46 if (cfg) {
47 each(cfg, (v, k) => ct.register(k, v));
47 each(cfg, (v, k) => ct.register(k, v));
48 }
48 }
49
49
50 return this._optional ? ct.resolve(this._name, this._default) : ct
50 return this._optional ? ct.resolve(this._name, this._default) : ct
51 .resolve(this._name);
51 .resolve(this._name);
52 } catch (error) {
52 } catch (error) {
53 throw new ActivationError(this._name.toString(), ct.getStack(), error);
53 throw new ActivationError(this._name.toString(), ct.getStack(), error);
54 }
54 }
55 };
55 };
56
56
57 }
57 }
58
58
59 toString() {
59 toString() {
60 const opts = [];
60 const opts = [];
61 if (this._optional)
61 if (this._optional)
62 opts.push("optional");
62 opts.push("optional");
63
63
64 opts.push("lazy");
64 opts.push("lazy");
65
65
66 const parts = [
66 const parts = [
67 "@ref "
67 "@ref "
68 ];
68 ];
69 if (opts.length) {
69 if (opts.length) {
70 parts.push("{");
70 parts.push("{");
71 parts.push(opts.join());
71 parts.push(opts.join());
72 parts.push("} ");
72 parts.push("} ");
73 }
73 }
74
74
75 parts.push(this._name.toString());
75 parts.push(this._name.toString());
76
76
77 if (this._default !== undefined && this._default !== null) {
77 if (this._default !== undefined && this._default !== null) {
78 parts.push(" = ");
78 parts.push(" = ");
79 parts.push(String(this._default));
79 parts.push(String(this._default));
80 }
80 }
81
81
82 return parts.join("");
82 return parts.join("");
83 }
83 }
84 }
84 }
@@ -1,141 +1,142
1 import { IDestroyable, MapOf } from "../interfaces";
1 import { IDestroyable, MapOf } from "../interfaces";
2 import { argumentNotNull, isDestroyable } from "../safe";
2 import { argumentNotNull, isDestroyable } from "../safe";
3 import { ILifetimeManager, ILifetime } from "./interfaces";
3 import { ILifetimeManager, ILifetime } from "./interfaces";
4 import { ActivationContext } from "./ActivationContext";
4 import { ActivationContext } from "./ActivationContext";
5
5
6 function safeCall(item: () => void) {
6 function safeCall(item: () => void) {
7 try {
7 try {
8 item();
8 item();
9 } catch {
9 } catch {
10 // silence!
10 // silence!
11 }
11 }
12 }
12 }
13
13
14 const emptyLifetime: ILifetime = {
14 const emptyLifetime: ILifetime = {
15 has() {
15 has() {
16 return false;
16 return false;
17 },
17 },
18
18
19 enter() {
19 enter() {
20
20
21 },
21 },
22
22
23 get() {
23 get() {
24 throw new Error("The specified item isn't registered with this lifetime manager");
24 throw new Error("The specified item isn't registered with this lifetime manager");
25 },
25 },
26
26
27 store() {
27 store() {
28 // does nothing
28 // does nothing
29 }
29 }
30
30
31 };
31 };
32
32
33 export class LifetimeManager implements IDestroyable, ILifetimeManager {
33 export class LifetimeManager implements IDestroyable, ILifetimeManager {
34 private _cleanup: (() => void)[] = [];
34 private _cleanup: (() => void)[] = [];
35 private _cache: MapOf<any> = {};
35 private _cache: MapOf<any> = {};
36 private _destroyed = false;
36 private _destroyed = false;
37
37
38 private _pending: MapOf<boolean> = {};
39
38 initialize(id: string): ILifetime {
40 initialize(id: string): ILifetime {
39 const self = this;
41 const self = this;
40 let pending = false;
41 return {
42 return {
42 has() {
43 has() {
43 return (id in self._cache);
44 return (id in self._cache);
44 },
45 },
45
46
46 get() {
47 get() {
47 const t = self._cache[id];
48 const t = self._cache[id];
48 if (t === undefined)
49 if (t === undefined)
49 throw new Error(`The item with with the key ${id} isn't found`);
50 throw new Error(`The item with with the key ${id} isn't found`);
50 return t;
51 return t;
51 },
52 },
52
53
53 enter() {
54 enter() {
54 if (pending)
55 if (self._pending[id])
55 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
56 throw Error(`Cyclic reference detected: the item with the key ${id} is already activating.`);
56 pending = true;
57 self._pending[id] = true;
57 },
58 },
58
59
59 store(item: any, cleanup?: (item: any) => void) {
60 store(item: any, cleanup?: (item: any) => void) {
60 argumentNotNull(id, "id");
61 argumentNotNull(id, "id");
61 argumentNotNull(item, "item");
62 argumentNotNull(item, "item");
62
63
63 if (this.has())
64 if (this.has())
64 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
65 throw new Error(`The item with with the key ${id} already registered with this lifetime manager`);
65 pending = false;
66 delete self._pending[id];
66
67
67 self._cache[id] = item;
68 self._cache[id] = item;
68
69
69 if (self._destroyed)
70 if (self._destroyed)
70 throw new Error("Lifetime manager is destroyed");
71 throw new Error("Lifetime manager is destroyed");
71 if (cleanup) {
72 if (cleanup) {
72 self._cleanup.push(() => cleanup(item));
73 self._cleanup.push(() => cleanup(item));
73 } else if (isDestroyable(item)) {
74 } else if (isDestroyable(item)) {
74 self._cleanup.push(() => item.destroy());
75 self._cleanup.push(() => item.destroy());
75 }
76 }
76 }
77 }
77 };
78 };
78 }
79 }
79
80
80 destroy() {
81 destroy() {
81 if (!this._destroyed) {
82 if (!this._destroyed) {
82 this._destroyed = true;
83 this._destroyed = true;
83 this._cleanup.forEach(safeCall);
84 this._cleanup.forEach(safeCall);
84 this._cleanup.length = 0;
85 this._cleanup.length = 0;
85 }
86 }
86 }
87 }
87
88
88 static readonly empty: ILifetimeManager = {
89 static readonly empty: ILifetimeManager = {
89 initialize(): ILifetime {
90 initialize(): ILifetime {
90 return emptyLifetime;
91 return emptyLifetime;
91 },
92 },
92 destroy() {
93 destroy() {
93 throw new Error("Trying to destroy empty lifetime manager, this is a bug.");
94 throw new Error("Trying to destroy empty lifetime manager, this is a bug.");
94 }
95 }
95
96
96 };
97 };
97
98
98 static readonly hierarchyLifetime: ILifetimeManager = {
99 static readonly hierarchyLifetime: ILifetimeManager = {
99 initialize(id: string, context: ActivationContext<any>): ILifetime {
100 initialize(id: string, context: ActivationContext<any>): ILifetime {
100 return context.getContainer().getLifetimeManager().initialize(id, context);
101 return context.getContainer().getLifetimeManager().initialize(id, context);
101 },
102 },
102 destroy() {
103 destroy() {
103 throw new Error("Trying to destroy hierarchy lifetime manager, this is a bug.");
104 throw new Error("Trying to destroy hierarchy lifetime manager, this is a bug.");
104 }
105 }
105 };
106 };
106
107
107 static readonly singletonLifetime: ILifetimeManager = {
108 static readonly singletonLifetime: ILifetimeManager = {
108 initialize(id: string): ILifetime {
109 initialize(id: string): ILifetime {
109 return singletonLifetimeManager.initialize(id);
110 return singletonLifetimeManager.initialize(id);
110 },
111 },
111 destroy() {
112 destroy() {
112 throw new Error("Trying to destroy singleton lifetime manager, this is a bug.");
113 throw new Error("Trying to destroy singleton lifetime manager, this is a bug.");
113 }
114 }
114 };
115 };
115
116
116 static readonly contextLifetime: ILifetimeManager = {
117 static readonly contextLifetime: ILifetimeManager = {
117 initialize(id: string, context: ActivationContext<any>): ILifetime {
118 initialize(id: string, context: ActivationContext<any>): ILifetime {
118 return {
119 return {
119 enter() {
120 enter() {
120 if (context.visit(id))
121 if (context.visit(id))
121 throw new Error("Cyclic reference detected");
122 throw new Error("Cyclic reference detected");
122 },
123 },
123 get() {
124 get() {
124 return context.get(id);
125 return context.get(id);
125 },
126 },
126 has() {
127 has() {
127 return context.has(id);
128 return context.has(id);
128 },
129 },
129 store(item: any) {
130 store(item: any) {
130 context.store(id, item);
131 context.store(id, item);
131 }
132 }
132
133
133 };
134 };
134 },
135 },
135 destroy() {
136 destroy() {
136 throw new Error("Trying to destroy empty lifetime manager, this is a bug.");
137 throw new Error("Trying to destroy empty lifetime manager, this is a bug.");
137 }
138 }
138 };
139 };
139 }
140 }
140
141
141 const singletonLifetimeManager = new LifetimeManager();
142 const singletonLifetimeManager = new LifetimeManager();
@@ -1,164 +1,157
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ServiceMap, PartialServiceMap, ILifetimeManager } from "./interfaces";
2 import { Descriptor, ServiceMap, PartialServiceMap, ILifetimeManager } from "./interfaces";
3 import { argumentNotNull, isPrimitive, keys, isNull } from "../safe";
3 import { argumentNotNull, 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";
6 import { LifetimeManager } from "./LifetimeManager";
6 import { LifetimeManager } from "./LifetimeManager";
7 import { MatchingMemberKeys } from "../interfaces";
7 import { MatchingMemberKeys } from "../interfaces";
8 import { Container } from "./Container";
8 import { Container } from "./Container";
9
9
10 let cacheId = 0;
10 let cacheId = 0;
11
11
12 const trace = TraceSource.get("@implab/core/di/ActivationContext");
12 const trace = TraceSource.get("@implab/core/di/ActivationContext");
13
13
14 function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) {
14 function injectMethod<T, M extends keyof T, S extends object, A>(target: T, method: M, context: ActivationContext<S>, args: A) {
15
15
16 const m = target[method];
16 const m = target[method];
17 if (!m || typeof m !== "function")
17 if (!m || typeof m !== "function")
18 throw new Error("Method '" + method + "' not found");
18 throw new Error("Method '" + method + "' not found");
19
19
20 if (args instanceof Array)
20 if (args instanceof Array)
21 return m.apply(target, _parse(args, context, "." + method));
21 return m.apply(target, _parse(args, context, "." + method));
22 else
22 else
23 return m.call(target, _parse(args, context, "." + method));
23 return m.call(target, _parse(args, context, "." + method));
24 }
24 }
25
25
26 function makeCleanupCallback<T>(method: Cleaner<T>) {
26 function makeCleanupCallback<T>(method: Cleaner<T>) {
27 if (typeof (method) === "function") {
27 if (typeof (method) === "function") {
28 return (target: T) => {
28 return (target: T) => {
29 method(target);
29 method(target);
30 };
30 };
31 } else {
31 } else {
32 return (target: T) => {
32 return (target: T) => {
33 const m = target[method] as any;
33 const m = target[method] as any;
34 m.apply(target);
34 m.apply(target);
35 };
35 };
36 }
36 }
37 }
37 }
38
38
39 function _parse(value: any, context: ActivationContext<any>, path: string): any {
39 function _parse(value: any, context: ActivationContext<any>, path: string): any {
40 if (isPrimitive(value))
40 if (isPrimitive(value))
41 return value as any;
41 return value as any;
42
42
43 trace.debug("parse {0}", path);
43 trace.debug("parse {0}", path);
44
44
45 if (isDescriptor(value))
45 if (isDescriptor(value))
46 return context.activate(value, path);
46 return context.activate(value, path);
47
47
48 if (value instanceof Array)
48 if (value instanceof Array)
49 return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any;
49 return value.map((x, i) => _parse(x, context, `${path}[${i}]`)) as any;
50
50
51 const t: any = {};
51 const t: any = {};
52
52
53 keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`));
53 keys(value).forEach(p => t[p] = _parse(value[p], context, `${path}.${p}`));
54
54
55 return t;
55 return t;
56 }
56 }
57
57
58 export type Cleaner<T> = ((x: T) => void) | MatchingMemberKeys<() => void, T>;
58 export type Cleaner<T> = ((x: T) => void) | MatchingMemberKeys<() => void, T>;
59
59
60 export type InjectionSpec<T> = {
60 export type InjectionSpec<T> = {
61 [m in keyof T]?: any;
61 [m in keyof T]?: any;
62 };
62 };
63
63
64 export interface ServiceDescriptorParams<S extends object, T, P extends any[]> {
64 export interface ServiceDescriptorParams<S extends object, T, P extends any[]> {
65 owner: Container<S>;
66
67 lifetime?: ILifetimeManager;
65 lifetime?: ILifetimeManager;
68
66
69 params?: P;
67 params?: P;
70
68
71 inject?: InjectionSpec<T>[];
69 inject?: InjectionSpec<T>[];
72
70
73 services?: PartialServiceMap<S>;
71 services?: PartialServiceMap<S>;
74
72
75 cleanup?: Cleaner<T>;
73 cleanup?: Cleaner<T>;
76 }
74 }
77
75
78 export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> {
76 export class ServiceDescriptor<S extends object, T, P extends any[]> implements Descriptor<S, T> {
79 _services: ServiceMap<S>;
77 _services: ServiceMap<S>;
80
78
81 _params: P | undefined;
79 _params: P | undefined;
82
80
83 _inject: InjectionSpec<T>[];
81 _inject: InjectionSpec<T>[];
84
82
85 _cleanup: ((item: T) => void) | undefined;
83 _cleanup: ((item: T) => void) | undefined;
86
84
87 _cacheId = String(++cacheId);
85 _cacheId = String(++cacheId);
88
86
89 _lifetime = LifetimeManager.empty;
87 _lifetime = LifetimeManager.empty;
90
88
91 _owner: Container<S>;
92
93 constructor(opts: ServiceDescriptorParams<S, T, P>) {
89 constructor(opts: ServiceDescriptorParams<S, T, P>) {
94 argumentNotNull(opts && opts.owner, "opts.owner");
95
96 this._owner = opts.owner;
97
90
98 if (opts.lifetime)
91 if (opts.lifetime)
99 this._lifetime = opts.lifetime;
92 this._lifetime = opts.lifetime;
100
93
101 if (!isNull(opts.params))
94 if (!isNull(opts.params))
102 this._params = opts.params;
95 this._params = opts.params;
103
96
104 this._inject = opts.inject || [];
97 this._inject = opts.inject || [];
105
98
106 this._services = (opts.services || {}) as ServiceMap<S>;
99 this._services = (opts.services || {}) as ServiceMap<S>;
107
100
108 if (opts.cleanup) {
101 if (opts.cleanup) {
109 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
102 if (!(typeof (opts.cleanup) === "string" || opts.cleanup instanceof Function))
110 throw new Error(
103 throw new Error(
111 "The cleanup parameter must be either a function or a function name");
104 "The cleanup parameter must be either a function or a function name");
112
105
113 this._cleanup = makeCleanupCallback(opts.cleanup);
106 this._cleanup = makeCleanupCallback(opts.cleanup);
114 }
107 }
115 }
108 }
116
109
117 activate(context: ActivationContext<S>) {
110 activate(context: ActivationContext<S>) {
118 const lifetime = this._lifetime.initialize(this._cacheId, context);
111 const lifetime = this._lifetime.initialize(this._cacheId, context);
119
112
120 if (lifetime.has()) {
113 if (lifetime.has()) {
121 return lifetime.get();
114 return lifetime.get();
122 } else {
115 } else {
123 lifetime.enter();
116 lifetime.enter();
124 const instance = this._create(context);
117 const instance = this._create(context);
125 lifetime.store(this._cacheId, this._cleanup);
118 lifetime.store(this._cacheId, this._cleanup);
126 return instance;
119 return instance;
127 }
120 }
128 }
121 }
129
122
130 _factory(...params: any[]): T {
123 _factory(...params: any[]): T {
131 throw Error("Not implemented");
124 throw Error("Not implemented");
132 }
125 }
133
126
134 _create(context: ActivationContext<S>) {
127 _create(context: ActivationContext<S>) {
135 trace.debug(`constructing ${context._name}`);
128 trace.debug(`constructing ${context._name}`);
136
129
137 if (this._services) {
130 if (this._services) {
138 keys(this._services).forEach(p => context.register(p, this._services[p]));
131 keys(this._services).forEach(p => context.register(p, this._services[p]));
139 }
132 }
140
133
141 let instance: T;
134 let instance: T;
142
135
143 if (this._params === undefined) {
136 if (this._params === undefined) {
144 instance = this._factory();
137 instance = this._factory();
145 } else if (this._params instanceof Array) {
138 } else if (this._params instanceof Array) {
146 instance = this._factory.apply(this, _parse(this._params, context, "args"));
139 instance = this._factory.apply(this, _parse(this._params, context, "args"));
147 } else {
140 } else {
148 instance = this._factory(_parse(this._params, context, "args"));
141 instance = this._factory(_parse(this._params, context, "args"));
149 }
142 }
150
143
151 if (this._inject) {
144 if (this._inject) {
152 this._inject.forEach(spec => {
145 this._inject.forEach(spec => {
153 for (const m in spec)
146 for (const m in spec)
154 injectMethod(instance, m, context, spec[m]);
147 injectMethod(instance, m, context, spec[m]);
155 });
148 });
156 }
149 }
157 return instance;
150 return instance;
158 }
151 }
159
152
160 clone() {
153 clone() {
161 return Object.create(this);
154 return Object.create(this);
162 }
155 }
163
156
164 }
157 }
@@ -1,42 +1,42
1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
2 import { Constructor, Factory } from "../interfaces";
3 import { argumentNotNull, isPrimitive } from "../safe";
2 import { argumentNotNull, isPrimitive } from "../safe";
4
3
5 export interface TypeServiceDescriptorParams<S extends object, T extends object, P extends any[]> extends ServiceDescriptorParams<S, T, P> {
4 export interface TypeServiceDescriptorParams<S extends object, T extends object, P extends any[]> extends ServiceDescriptorParams<S, T, P> {
6 type: Constructor<T>;
5 type: new (...args: any[]) => T;
7 }
6 }
8
7
9 export class TypeServiceDescriptor<S extends object, T extends object, P extends any[]> extends ServiceDescriptor<S, T, P> {
8 export class TypeServiceDescriptor<S extends object, T extends object, P extends any[]> extends ServiceDescriptor<S, T, P> {
10 _type: Constructor;
9 _type: new (...args: any[]) => T;
11
10
12 constructor(opts: TypeServiceDescriptorParams<S, T, P>) {
11 constructor(opts: TypeServiceDescriptorParams<S, T, P>) {
13 super(opts);
12 super(opts);
14 argumentNotNull(opts && opts.type, "opts.type");
13 argumentNotNull(opts && opts.type, "opts.type");
15
14
16 const ctor = this._type = opts.type;
15 const ctor = this._type = opts.type;
17
16
18 if (this._params) {
17 if (this._params) {
19 if (this._params.length) {
18 if (this._params.length) {
20 this._factory = (...args) => {
19 this._factory = (...args) => {
21 const t = Object.create(ctor.prototype);
20 /*const t = Object.create(ctor.prototype);
22 const inst = ctor.apply(t, args);
21 const inst = ctor.apply(t, args);
23 return isPrimitive(inst) ? t : inst;
22 return isPrimitive(inst) ? t : inst;*/
23 return new ctor(...args);
24 };
24 };
25 } else {
25 } else {
26 this._factory = arg => {
26 this._factory = arg => {
27 return new ctor(arg);
27 return new ctor(arg);
28 };
28 };
29 }
29 }
30 } else {
30 } else {
31 this._factory = () => {
31 this._factory = () => {
32 return new ctor();
32 return new ctor();
33 };
33 };
34 }
34 }
35
35
36 }
36 }
37
37
38 toString() {
38 toString() {
39 // @constructor {singleton} foo/bar/Baz
39 // @constructor {singleton} foo/bar/Baz
40 return ``;
40 return ``;
41 }
41 }
42 }
42 }
@@ -1,37 +1,37
1 import { ServiceRecordBuilder, ExtractDependency, RegistrationVisitor } from "./interfaces";
1 import { ServiceRecordBuilder, ExtractDependency, RegistrationVisitor, ServiceRegistration } from "./interfaces";
2 import { ActivationType } from "../interfaces";
2 import { ActivationType } from "../interfaces";
3
3
4 export class RegistrationBuilder<T, S extends object> {
4 export class RegistrationBuilder<T, S extends object> implements ServiceRegistration {
5 private _activationType: ActivationType = "call";
5 private _activationType: ActivationType = "call";
6
6
7 private _overrides: { [m in keyof S]?: (...args: any) => void } | undefined;
7 private _overrides: { [m in keyof S]?: (...args: any) => void } | undefined;
8
8
9
9
10 override<K extends keyof S>(name: K, builder: S[K], raw: true): this;
10 override<K extends keyof S>(name: K, builder: S[K], raw: true): this;
11 override<K extends keyof S>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void): this;
11 override<K extends keyof S>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void): this;
12 override<K extends keyof S, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): this;
12 override<K extends keyof S, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): this;
13 override<K extends keyof S>(name: K, builder: S[K] | ((t: ServiceRecordBuilder<S[K], S>) => void), raw: boolean = false) {
13 override<K extends keyof S>(name: K, builder: S[K] | ((t: ServiceRecordBuilder<S[K], S>) => void), raw: boolean = false) {
14 if (!this._overrides)
14 if (!this._overrides)
15 this._overrides = {};
15 this._overrides = {};
16 if (raw) {
16 if (raw) {
17
17
18 } else if (builder instanceof Function) {
18 } else if (builder instanceof Function) {
19
19
20 } else {
20 } else {
21
21
22 }
22 }
23 return this;
23 return this;
24 }
24 }
25
25
26 activate(activation: ActivationType) {
26 activate(activation: ActivationType) {
27 this._activationType = activation;
27 this._activationType = activation;
28 return this;
28 return this;
29 }
29 }
30 inject<M extends keyof T, P extends any[]>(member: T[M] extends (...params: ExtractDependency<P, S>) => any ? M : never, ...params: P) {
30 inject<M extends keyof T, P extends any[]>(member: T[M] extends (...params: ExtractDependency<P, S>) => any ? M : never, ...params: P) {
31 return this;
31 return this;
32 }
32 }
33
33
34 visit(visitor: RegistrationVisitor<S>) {
34 visit(visitor: RegistrationVisitor) {
35
35
36 }
36 }
37 } No newline at end of file
37 }
@@ -1,88 +1,71
1 import { primitive } from "../../safe";
1 import { primitive } from "../../safe";
2 import { ActivationType } from "../interfaces";
2 import { ActivationType } from "../interfaces";
3 import { AnnotaionBuilder } from "../Annotations";
3 import { AnnotaionBuilder } from "../Annotations";
4 import { LazyDependencyRegistration, DependencyRegistration } from "../Configuration";
4 import { LazyDependencyRegistration, DependencyRegistration } from "../Configuration";
5 import { Container } from "../Container";
5 import { Container } from "../Container";
6
6
7 export interface DependencyOptions<T> {
7 export interface DependencyOptions<T> {
8 optional?: boolean;
8 optional?: boolean;
9 default?: T;
9 default?: T;
10 }
10 }
11
11
12 export interface LazyDependencyOptions<T> extends DependencyOptions<T> {
12 export interface LazyDependencyOptions<T> extends DependencyOptions<T> {
13 lazy: true;
13 lazy: true;
14 }
14 }
15
15
16 export type ExtractService<K, S> = K extends keyof S ? S[K] : K;
16 export type ExtractService<K, S> = K extends keyof S ? S[K] : K;
17
17
18 export type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
18 export type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
19 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
19 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
20 D extends { $type: new (...args: any[]) => infer I } ? I :
20 D extends { $type: new (...args: any[]) => infer I } ? I :
21 D extends { $factory: (...args: any[]) => infer R } ? R :
21 D extends { $factory: (...args: any[]) => infer R } ? R :
22 WalkDependencies<D, S>;
22 WalkDependencies<D, S>;
23
23
24 export type WalkDependencies<D, S> = D extends primitive ? D :
24 export type WalkDependencies<D, S> = D extends primitive ? D :
25 { [K in keyof D]: ExtractDependency<D[K], S> };
25 { [K in keyof D]: ExtractDependency<D[K], S> };
26
26
27 export type ServiceModule<T, S extends object, M extends keyof any = "service"> = {
27 export type ServiceModule<T, S extends object, M extends keyof any = "service"> = {
28 [m in M]: AnnotaionBuilder<T, S>;
28 [m in M]: AnnotaionBuilder<T, S>;
29 };
29 };
30
30
31 export interface ServiceRecordBuilder<T, S extends object> {
31 export interface ServiceRecordBuilder<T, S extends object> {
32 type<P extends any[], C extends new (...args: ExtractDependency<P, S>) => T>(
32 type<P extends any[], C extends new (...args: ExtractDependency<P, S>) => T>(
33 target: C, ...params: P): ConstructorBuilder<C, S>;
33 target: C, ...params: P): ConstructorBuilder<C, S>;
34 factory<P extends any[], F extends (...args: ExtractDependency<P, S>) => T>(
34 factory<P extends any[], F extends (...args: ExtractDependency<P, S>) => T>(
35 target: F, ...params: P): FactoryBuilder<F, S>;
35 target: F, ...params: P): FactoryBuilder<F, S>;
36 wired<M extends keyof any>(module: ServiceModule<T, S, M>, m: M): RegistrationBuilder<T, S>;
36 wired<M extends keyof any>(module: ServiceModule<T, S, M>, m: M): RegistrationBuilder<T, S>;
37 wired(module: ServiceModule<T, S>): RegistrationBuilder<T, S>;
37 wired(module: ServiceModule<T, S>): RegistrationBuilder<T, S>;
38 }
38 }
39
39
40 export interface RegistrationVisitor<S extends object> {
40 export interface RegistrationVisitor {
41 visitDependency(): void;
41 visitDependency(): void;
42
42
43 visitObject(): void;
43 visitObject(): void;
44
44
45 visitTypeRegistration(): void;
45 visitTypeRegistration(): void;
46
46
47 visitFactoryRegistration(): void;
47 visitFactoryRegistration(): void;
48
48
49 }
49 }
50
50
51 export interface RegistrationBuilder<T, S extends object> {
51 export interface ServiceRegistration {
52 override<K extends keyof S>(name: K, builder: S[K], raw: true): this;
52 visit(visitor: RegistrationVisitor): void;
53 override<K extends keyof S>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void): this;
54 override<K extends keyof S, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): this;
55
56 activate(activation: ActivationType): this;
57 inject<M extends keyof T, P extends any[]>(member: T[M] extends (...params: ExtractDependency<P, S>) => any ? M : never, ...params: P): this;
58
59 visit(visitor: RegistrationVisitor<S>): void;
60 }
61
62 export interface ConstructorBuilder<C extends new (...args: any[]) => any, S extends object> extends RegistrationBuilder<InstanceType<C>, S> {
63 $type: C;
64 }
65
66 export interface FactoryBuilder<F extends (...args: any[]) => any, S extends object> extends RegistrationBuilder<ReturnType<F>, S> {
67 $factory: F;
68 }
53 }
69
54
70 export interface ConfigBuilder<S extends object, Y extends keyof S = keyof S> {
55 export interface ConfigBuilder<S extends object, Y extends keyof S = keyof S> {
71 register<K extends Y>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void | Promise<void>): ConfigBuilder<S, Exclude<Y, K>>;
56 register<K extends Y>(name: K, builder: (t: ServiceRecordBuilder<S[K], S>) => void | Promise<void>): ConfigBuilder<S, Exclude<Y, K>>;
72 register<K extends Y, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): ConfigBuilder<S, Exclude<Y, K>>;
57 register<K extends Y, V>(name: S[K] extends ExtractDependency<V, S> ? K : never, value: V): ConfigBuilder<S, Exclude<Y, K>>;
73 register<K extends Y>(name: K, value: S[K], raw: true): ConfigBuilder<S, Exclude<Y, K>>;
58 register<K extends Y>(name: K, value: S[K], raw: true): ConfigBuilder<S, Exclude<Y, K>>;
74
59
75 apply(container: Container<S>): Promise<void>;
60 apply(container: Container<S>): Promise<void>;
76 }
61 }
77
62
78 interface ServicesDeclaration<S extends object> {
63 export interface ServicesDeclaration<S extends object> {
79 build<T>(this: void): ServiceRecordBuilder<T, S>;
64 build<T>(this: void): ServiceRecordBuilder<T, S>;
80 annotate<T>(this: void): AnnotaionBuilder<T, S>;
65 annotate<T>(this: void): AnnotaionBuilder<T, S>;
81
66
82 dependency<K extends keyof S>(this: void, name: K, opts: LazyDependencyOptions<S[K]>): LazyDependencyRegistration<S, K>;
67 dependency<K extends keyof S>(this: void, name: K, opts: LazyDependencyOptions<S[K]>): LazyDependencyRegistration<S, K>;
83 dependency<K extends keyof S>(this: void, name: K, opts?: DependencyOptions<S[K]>): DependencyRegistration<S, K>;
68 dependency<K extends keyof S>(this: void, name: K, opts?: DependencyOptions<S[K]>): DependencyRegistration<S, K>;
84
69
85 configure(): ConfigBuilder<S>;
70 configure(): ConfigBuilder<S>;
86 }
71 }
87
88 export declare function declare<S extends object>(): ServicesDeclaration<S>;
@@ -1,53 +1,53
1 import { ActivationContext } from "./ActivationContext";
1 import { ActivationContext } from "./ActivationContext";
2 import { IDestroyable } from "../interfaces";
2 import { IDestroyable } from "../interfaces";
3
3
4 export interface Descriptor<S extends object = any, T = any> {
4 export interface Descriptor<S extends object = any, T = any> {
5 activate(context: ActivationContext<S>): T;
5 activate(context: ActivationContext<S>): T;
6 }
6 }
7
7
8 export type ServiceMap<S extends object> = {
8 export type ServiceMap<S extends object> = {
9 [k in keyof S]: Descriptor<S, S[k]>;
9 [k in keyof S]: Descriptor<S, S[k]>;
10 };
10 };
11
11
12 export type ContainerKeys<S extends object> = keyof S | keyof ContainerProvided<S>;
12 export type ContainerKeys<S extends object> = keyof S | keyof ContainerProvided<S>;
13
13
14 export type ContainerResolve<S extends object, K> =
14 export type ContainerResolve<S extends object, K> =
15 K extends keyof ContainerProvided<S> ? ContainerProvided<S>[K] :
15 K extends keyof ContainerProvided<S> ? ContainerProvided<S>[K] :
16 K extends keyof S ? S[K] : never;
16 K extends keyof S ? S[K] : never;
17
17
18 export type ContainerServiceMap<S extends object> = {
18 export type ContainerServiceMap<S extends object> = {
19 [K in ContainerKeys<S>]: Descriptor<S, ContainerResolve<S, K>>;
19 [K in ContainerKeys<S>]: Descriptor<S, ContainerResolve<S, K>>;
20 };
20 };
21
21
22 export type PartialServiceMap<S extends object> = {
22 export type PartialServiceMap<S extends object> = {
23 [k in keyof S]?: Descriptor<S, S[k]>;
23 [k in keyof S]?: Descriptor<S, S[k]>;
24 };
24 };
25
25
26 export interface Resolver<S extends object> {
26 export interface Resolver<S extends object> {
27 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>): ContainerResolve<S, K>;
27 resolve<K extends ContainerKeys<S>>(name: K, def?: ContainerResolve<S, K>): ContainerResolve<S, K>;
28 }
28 }
29
29
30 export interface ContainerProvided<S extends object> {
30 export interface ContainerProvided<S extends object> {
31 container: Resolver<S>;
31 container: Resolver<S>;
32 }
32 }
33
33
34 export type ContainerRegistered<S extends object> = /*{
34 export type ContainerRegistered<S extends object> = /*{
35 [K in Exclude<keyof S, keyof ContainerProvided<S>>]: S[K];
35 [K in Exclude<keyof S, keyof ContainerProvided<S>>]: S[K];
36 };*/
36 };*/
37 Exclude<S, ContainerProvided<S>>;
37 Exclude<S, ContainerProvided<S>>;
38
38
39 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
39 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
40
40
41 export interface ILifetimeManager extends IDestroyable {
41 export interface ILifetimeManager extends IDestroyable {
42 initialize(id: string, context: ActivationContext<any>): ILifetime;
42 initialize(id: string, context: ActivationContext<any>): ILifetime;
43 }
43 }
44
44
45 export interface ILifetime {
45 export interface ILifetime {
46 has(): boolean;
46 has(): boolean;
47
47
48 get(): any;
48 get(): any;
49
49
50 enter(): void;
50 enter(): void;
51
51
52 store(item: any, cleanup?: (item: any) => void): void;
52 store(item: any, cleanup?: (item: any) => void): void;
53 } No newline at end of file
53 }
@@ -1,7 +1,50
1 import { isPrimitive } from "../safe";
1 import { isPrimitive } from "../safe";
2 import { Descriptor } from "./interfaces";
2 import { Descriptor } from "./interfaces";
3 import { ServicesDeclaration, ServiceRecordBuilder, ServiceModule, RegistrationBuilder, ExtractDependency } from "./fluent/interfaces";
4 import { AnnotaionBuilder } from "./Annotations";
5 import { FactoryBuilder } from "./fluent/FactoryBuilder";
6 import { ConstructorBuilder } from "./fluent/ConstructorBuiler";
3
7
4 export function isDescriptor(x: any): x is Descriptor {
8 export function isDescriptor(x: any): x is Descriptor {
5 return (!isPrimitive(x)) &&
9 return (!isPrimitive(x)) &&
6 (x.activate instanceof Function);
10 (x.activate instanceof Function);
7 } No newline at end of file
11 }
12
13 export function declare<S extends object>(): ServicesDeclaration<S> {
14 return {
15 annotate<T>() {
16 return new AnnotaionBuilder<T, S>();
17 },
18 build<T>(): ServiceRecordBuilder<T, S> {
19 return {
20 factory<P extends any[], F extends (...args: ExtractDependency<P, S>) => T>(
21 target: F,
22 ...params: P
23 ): FactoryBuilder<F, S> {
24 return new FactoryBuilder(target, params);
25 },
26
27 type<P extends any[], C extends new (...args: ExtractDependency<P, S>) => T>(
28 target: C, ...params: P
29 ): ConstructorBuilder<C, S> {
30 return new ConstructorBuilder(target, params);
31 },
32
33 wired<M extends keyof any>(module: ServiceModule<T, S, M>, m?: M): RegistrationBuilder<T, S> {
34 const service = m ?
35 module[m] :
36 (module as ServiceModule<T, S>).service;
37 if (!service)
38 throw new Error("The specified module doen's provides a service annotation");
39 return service.getRegistrationBuilder();
40 }
41 };
42 },
43 configure() {
44 throw new Error();
45 },
46 dependency() {
47 throw new Error();
48 }
49 };
50 }
@@ -1,1 +1,1
1 export { ConsoleLogger as ConsoleWriter } from "./ConsoleLogger"; No newline at end of file
1 export { ConsoleLogger as ConsoleWriter } from "./ConsoleLogger";
General Comments 0
You need to be logged in to leave comments. Login now