##// END OF EJS Templates
configuration interfaces moved to di/Configuration module...
cin -
r118:6738ac4c3072 ioc ts support
parent child
Show More
@@ -0,0 +1,7
1 import { isPrimitive } from "../safe";
2 import { Descriptor } from "./interfaces";
3
4 export function isDescriptor(x: any): x is Descriptor {
5 return (!isPrimitive(x)) &&
6 (x.activate instanceof Function);
7 }
@@ -1,19 +1,20
1 import { Descriptor, isDescriptor, Parse } from "./interfaces";
1 import { Descriptor } from "./interfaces";
2 2 import { ActivationContext } from "./ActivationContext";
3 3 import { isPrimitive } from "../safe";
4 import { isDescriptor } from "./traits";
4 5
5 export class AggregateDescriptor<T> implements Descriptor<Parse<T>> {
6 _value: T;
6 export class AggregateDescriptor<S, T> implements Descriptor<S, T> {
7 _value: any;
7 8
8 constructor(value: T) {
9 constructor(value: any) {
9 10 this._value = value;
10 11 }
11 12
12 activate<S>(context: ActivationContext<S>) {
13 activate(context: ActivationContext<S>): T {
13 14 return this._parse(this._value, context, "$value");
14 15 }
15 16
16 _parse<S, V>(value: V, context: ActivationContext<S>, path: string): Parse<V> {
17 _parse(value: any, context: ActivationContext<S>, path: string): any {
17 18 if (isPrimitive(value))
18 19 return value as any;
19 20
@@ -1,4 +1,5
1 1 import { Constructor } from "../interfaces";
2 import { primitive } from "../safe";
2 3
3 4 export interface InjectOptions {
4 5 lazy?: boolean;
@@ -14,25 +15,28 interface Lazy<K extends keyof any> exte
14 15 lazy: true;
15 16 }
16 17
17 type Setter<T = any> = (v: T) => void;
18
19 18 type Compatible<T1, T2> = T1 extends T2 ? any : never;
20 19
21 20 type ExtractService<K, S> = K extends keyof S ? S[K] : K;
22 21
23 type ExtractDependency<D, S> = D extends { $dependency: infer K } ? D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> : VisitDependency<D, S>;
22 type ExtractDependency<D, S> = D extends { $dependency: infer K } ?
23 D extends { lazy: true } ? () => ExtractService<K, S> : ExtractService<K, S> :
24 WalkDependencies<D, S>;
24 25
25 type VisitDependency<D, S> = D extends {} ? { [K in keyof D]: ExtractDependency<D[K], S> } : D;
26 type WalkDependencies<D, S> = D extends primitive ? D :
27 { [K in keyof D]: ExtractDependency<D[K], S> };
26 28
27 interface Config<S> {
28 dependency<K extends keyof S>(name: K): Dependency<K>;
29 interface Services<S> {
30 get<K extends keyof S>(name: K): Dependency<K>;
29 31
30 32 lazy<K extends keyof S>(name: K): Lazy<K>;
31 33
32 build<T>(): Builder<T, S>;
34 build<T extends object>(): Builder<T, S>;
33 35 }
34 36
35 export declare function services<S extends object>(): Config<S>;
37 export declare function services<S extends object>(): Services<S>;
38
39 export declare function build<T = never, S = any>(): Builder<T, S>;
36 40
37 41 export class Builder<T, S> {
38 42 consume<P extends any[]>(...args: P) {
@@ -40,13 +44,17 export class Builder<T, S> {
40 44 };
41 45 }
42 46
43 inject<K extends keyof S>(dependency: K) {
47 inject<P extends any[]>(...args: P) {
44 48 // K = "bar"
45 49 // M = "setValue"
46 50 // S[K] = Bar
47 51 // T[M] = (value: string) => void
48 52 // P[m] = (value: V) => void
49 return <P, M extends keyof (T | P)>(target: P, memberName: M, descriptor: TypedPropertyDescriptor<Compatible<T[M], Setter<S[K]>>>) => {
53 return <X extends { [m in M]: (...args: any) => any }, M extends keyof (T | X)>(
54 target: X,
55 memberName: M,
56 descriptor: TypedPropertyDescriptor<Compatible<(...args: ExtractDependency<P, S>) => any, T[M]>>
57 ) => {
50 58
51 59 };
52 60 }
@@ -55,4 +63,13 export class Builder<T, S> {
55 63 return this as Builder<T2, S>;
56 64 }
57 65
66 get<K extends keyof S>(name: K): Dependency<K> {
67 throw new Error();
58 68 }
69
70 lazy<K extends keyof S>(name: K): Lazy<K> {
71 throw new Error();
72 }
73
74
75 }
@@ -1,17 +1,6
1 1 import {
2 ServiceRegistration,
3 TypeRegistration,
4 FactoryRegistration,
5 ServiceMap,
6 isDescriptor,
7 isDependencyRegistration,
8 DependencyRegistration,
9 ValueRegistration,
10 ActivationType,
11 isValueRegistration,
12 isTypeRegistration,
13 isFactoryRegistration,
14 PartialServiceMap
2 PartialServiceMap,
3 ActivationType
15 4 } from "./interfaces";
16 5
17 6 import { argumentNotEmptyString, isPrimitive, isPromise, delegate, argumentOfType, argumentNotNull, get } from "../safe";
@@ -25,7 +14,81 import { TraceSource } from "../log/Trac
25 14 import { ConfigError } from "./ConfigError";
26 15 import { Cancellation } from "../Cancellation";
27 16 import { makeResolver } from "./ResolverHelper";
28 import { ICancellation } from "../interfaces";
17 import { ICancellation, Constructor, Factory } from "../interfaces";
18 import { isDescriptor } from "./traits";
19
20 export interface RegistrationScope<S> {
21
22 /** сервисы, которые регистрируются в контексте активации и таким образом
23 * могут переопределять ранее зарегистрированные сервисы. за это свойство
24 * нужно платить, кроме того порядок активации будет влиять на результат
25 * разрешения зависимостей.
26 */
27 services?: PartialServiceMap<S>;
28 }
29
30 /**
31 * Базовый интефейс конфигурации сервисов
32 */
33 export interface ServiceRegistration<T, P, S> extends RegistrationScope<S> {
34
35 activation?: ActivationType;
36
37 params?: P;
38
39 inject?: object | object[];
40
41 cleanup?: ((instance: T) => void) | string;
42 }
43
44 export interface TypeRegistration<T, P extends any[], S> extends ServiceRegistration<T, P, S> {
45 $type: string | (new (...params: P) => T);
46
47 }
48
49 export interface FactoryRegistration<T, P extends any[], S> extends ServiceRegistration<T, P, S> {
50 $factory: string | ( (...params: P) => T);
51 }
52
53 export interface ValueRegistration<T> {
54 $value: T;
55 parse?: boolean;
56 }
57
58 export interface DependencyRegistration<S, K extends keyof S> extends RegistrationScope<S> {
59 $dependency: K;
60 lazy?: boolean;
61 optional?: boolean;
62 default?: S[K];
63 }
64
65 const _activationTypes: { [k in ActivationType]: number; } = {
66 singleton: 1,
67 container: 2,
68 hierarchy: 3,
69 context: 4,
70 call: 5
71 };
72
73 export function isTypeRegistration(x: any): x is TypeRegistration<any, any, any> {
74 return (!isPrimitive(x)) && ("$type" in x);
75 }
76
77 export function isFactoryRegistration(x: any): x is FactoryRegistration<any, any, any> {
78 return (!isPrimitive(x)) && ("$factory" in x);
79 }
80
81 export function isValueRegistration(x: any): x is ValueRegistration<any> {
82 return (!isPrimitive(x)) && ("$value" in x);
83 }
84
85 export function isDependencyRegistration<S>(x: any): x is DependencyRegistration<S, keyof S> {
86 return (!isPrimitive(x)) && ("$dependency" in x);
87 }
88
89 export function isActivationType(x: string): x is ActivationType {
90 return typeof x === "string" && x in _activationTypes;
91 }
29 92
30 93 const trace = TraceSource.get("@implab/core/di/Configuration");
31 94 async function mapAll(data: any[], map?: (v: any, k: number) => any): Promise<any[]>;
@@ -180,7 +243,7 export class Configuration<S> {
180 243 trace.debug("<{0}", name);
181 244 }
182 245
183 async _visit<T>(data: T, name: string): Promise<any> {
246 async _visit(data: any, name: string): Promise<any> {
184 247 if (isPrimitive(data) || isDescriptor(data))
185 248 return data;
186 249
@@ -196,10 +259,10 export class Configuration<S> {
196 259 return this._visitArray(data, name);
197 260 }
198 261
199 return this._visitObject(data as T & object, name);
262 return this._visitObject(data, name);
200 263 }
201 264
202 async _visitObject<T extends object>(data: T, name: string) {
265 async _visitObject(data: any, name: string) {
203 266 if (data.constructor &&
204 267 data.constructor.prototype !== Object.prototype)
205 268 return new ValueDescriptor(data);
@@ -259,30 +322,7 export class Configuration<S> {
259 322 this._visit(data.params, "params");
260 323
261 324 if (data.activation) {
262 if (typeof (data.activation) === "string") {
263 switch (data.activation.toLowerCase()) {
264 case "singleton":
265 opts.activation = ActivationType.Singleton;
266 break;
267 case "container":
268 opts.activation = ActivationType.Container;
269 break;
270 case "hierarchy":
271 opts.activation = ActivationType.Hierarchy;
272 break;
273 case "context":
274 opts.activation = ActivationType.Context;
275 break;
276 case "call":
277 opts.activation = ActivationType.Call;
278 break;
279 default:
280 throw new Error("Unknown activation type: " +
281 data.activation);
282 }
283 } else {
284 opts.activation = Number(data.activation);
285 }
325 opts.activation = data.activation;
286 326 }
287 327
288 328 if (data.cleanup)
@@ -312,7 +352,7 export class Configuration<S> {
312 352 return d;
313 353 }
314 354
315 async _visitTypeRegistration<T, P>(data: TypeRegistration<T, P, S>, name: string) {
355 async _visitTypeRegistration(data: TypeRegistration<any, any, S>, name: string) {
316 356 argumentNotNull(data.$type, "data.$type");
317 357 this._enter(name);
318 358
@@ -324,7 +364,7 export class Configuration<S> {
324 364 opts.type = this._resolveType(moduleName, typeName);
325 365 }
326 366
327 const d = new TypeServiceDescriptor(
367 const d = new TypeServiceDescriptor<S, any, any[]>(
328 368 await mapAll(opts)
329 369 );
330 370
@@ -333,14 +373,14 export class Configuration<S> {
333 373 return d;
334 374 }
335 375
336 async _visitFactoryRegistration<T, P>(data: FactoryRegistration<T, P, S>, name: string) {
376 async _visitFactoryRegistration(data: FactoryRegistration<any, any, S>, name: string) {
337 377 argumentOfType(data.$factory, Function, "data.$factory");
338 378 this._enter(name);
339 379
340 380 const opts = this._makeServiceParams(data);
341 381 opts.factory = data.$factory;
342 382
343 const d = new FactoryServiceDescriptor(
383 const d = new FactoryServiceDescriptor<S, any, any[]>(
344 384 await mapAll(opts)
345 385 );
346 386
@@ -1,16 +1,17
1 1 import { ActivationContext } from "./ActivationContext";
2 2 import { ValueDescriptor } from "./ValueDescriptor";
3 3 import { ActivationError } from "./ActivationError";
4 import { isDescriptor, ServiceMap, Descriptor, PartialServiceMap, ContainerServices, Resolver } from "./interfaces";
4 import { ServiceMap, Descriptor, PartialServiceMap, ContainerServices, Resolver } from "./interfaces";
5 5 import { TraceSource } from "../log/TraceSource";
6 6 import { Configuration } from "./Configuration";
7 7 import { Cancellation } from "../Cancellation";
8 8 import { MapOf } from "../interfaces";
9 import { isDescriptor } from "./traits";
9 10
10 11 const trace = TraceSource.get("@implab/core/di/ActivationContext");
11 12
12 13 export class Container<S = any> implements Resolver<S> {
13 readonly _services: PartialServiceMap<S, ContainerServices<S>>;
14 readonly _services: PartialServiceMap<ContainerServices<S>>;
14 15
15 16 readonly _cache: MapOf<any>;
16 17
@@ -1,6 +1,5
1 1 import { ServiceDescriptor, ServiceDescriptorParams } from "./ServiceDescriptor";
2 2 import { argumentNotNull, oid } from "../safe";
3 import { ActivationType } from "./interfaces";
4 3
5 4 export interface FactoryServiceDescriptorParams<S, T, P extends any[]> extends ServiceDescriptorParams<S, T, P> {
6 5 factory: (...args: P) => T;
@@ -15,7 +14,7 export class FactoryServiceDescriptor<S,
15 14 // bind to null
16 15 this._factory = (...args) => opts.factory.apply(null, args as any);
17 16
18 if (opts.activation === ActivationType.Singleton) {
17 if (opts.activation === "singleton") {
19 18 this._cacheId = oid(opts.factory);
20 19 }
21 20 }
@@ -26,7 +26,7 export class ReferenceDescriptor<S = any
26 26
27 27 _default: S[K] | undefined;
28 28
29 _services: ServiceMap<S>;
29 _services: PartialServiceMap<S>;
30 30
31 31 constructor(opts: ReferenceDescriptorParams<S, K>) {
32 32 argumentNotEmptyString(opts && opts.name, "opts.name");
@@ -53,7 +53,7 export class ReferenceDescriptor<S = any
53 53 const ct = saved.clone();
54 54 try {
55 55 if (cfg) {
56 each(cfg as ServiceMap<S>, (v, k) => ct.register(k, v));
56 each(cfg, (v, k) => ct.register(k, v));
57 57 }
58 58
59 59 return this._optional ? ct.resolve(this._name, this._default) : ct
@@ -1,8 +1,9
1 1 import { ActivationContext } from "./ActivationContext";
2 import { Descriptor, ActivationType, ServiceMap, isDescriptor, Parse, PartialServiceMap } from "./interfaces";
2 import { Descriptor, ServiceMap, PartialServiceMap, ActivationType } from "./interfaces";
3 3 import { Container } from "./Container";
4 import { argumentNotNull, isPrimitive, each, keys, isNull } from "../safe";
4 import { argumentNotNull, isPrimitive, keys, isNull } from "../safe";
5 5 import { TraceSource } from "../log/TraceSource";
6 import { isDescriptor } from "./traits";
6 7
7 8 let cacheId = 0;
8 9
@@ -33,7 +34,7 function makeClenupCallback(target: any,
33 34 }
34 35 }
35 36
36 function _parse<T, S>(value: T, context: ActivationContext<S>, path: string): Parse<T> {
37 function _parse(value: any, context: ActivationContext<any>, path: string): any {
37 38 if (isPrimitive(value))
38 39 return value as any;
39 40
@@ -77,7 +78,7 export class ServiceDescriptor<S, T, P e
77 78
78 79 _hasInstance = false;
79 80
80 _activationType = ActivationType.Call;
81 _activationType: ActivationType = "call";
81 82
82 83 _services: ServiceMap<S>;
83 84
@@ -125,7 +126,7 export class ServiceDescriptor<S, T, P e
125 126 this._cacheId = ++cacheId;
126 127
127 128 switch (this._activationType) {
128 case ActivationType.Singleton: // SINGLETON
129 case "singleton": // SINGLETON
129 130 // if the value is cached return it
130 131 if (this._hasInstance)
131 132 return this._instance;
@@ -146,7 +147,7 export class ServiceDescriptor<S, T, P e
146 147 this._hasInstance = true;
147 148 return (this._instance = instance);
148 149
149 case ActivationType.Container: // CONTAINER
150 case "container": // CONTAINER
150 151 // return a cached value
151 152
152 153 if (this._hasInstance)
@@ -163,17 +164,17 export class ServiceDescriptor<S, T, P e
163 164 // cache and return the instance
164 165 this._hasInstance = true;
165 166 return (this._instance = instance);
166 case ActivationType.Context: // CONTEXT
167 case "context": // CONTEXT
167 168 // return a cached value if one exists
168 169
169 170 if (context.has(this._cacheId))
170 171 return context.get(this._cacheId);
171 172 // context context activated instances are controlled by callers
172 173 return context.store(this._cacheId, this._create(context));
173 case ActivationType.Call: // CALL
174 case "call": // CALL
174 175 // per-call created instances are controlled by callers
175 176 return this._create(context);
176 case ActivationType.Hierarchy: // HIERARCHY
177 case "hierarchy": // HIERARCHY
177 178 // hierarchy activated instances are behave much like container activated
178 179 // except they are created and bound to the child container
179 180
@@ -209,7 +210,7 export class ServiceDescriptor<S, T, P e
209 210 _create(context: ActivationContext<S>) {
210 211 trace.debug(`constructing ${context._name}`);
211 212
212 if (this._activationType !== ActivationType.Call &&
213 if (this._activationType !== "call" &&
213 214 context.visit(this._cacheId) > 0)
214 215 throw new Error("Recursion detected");
215 216
@@ -1,68 +1,16
1 import { isPrimitive, primitive } from "../safe";
2 1 import { ActivationContext } from "./ActivationContext";
3 import { Constructor, Factory } from "../interfaces";
4 2
5 3 export interface Descriptor<S = any, T = any> {
6 4 activate(context: ActivationContext<S>): T;
7 5 }
8 6
9 export function isDescriptor(x: any): x is Descriptor {
10 return (!isPrimitive(x)) &&
11 (x.activate instanceof Function);
12 }
13
14 export type ServiceMap<S, S2 extends S = S> = {
15 [k in keyof S2]: Descriptor<S, S2[k]>;
7 export type ServiceMap<S> = {
8 [k in keyof S]: Descriptor<S, S[k]>;
16 9 };
17 10
18 export type PartialServiceMap<S, S2 extends S = S> = Partial<ServiceMap<S, S2>>;
19
20 export enum ActivationType {
21 Singleton = 1,
22 Container,
23 Hierarchy,
24 Context,
25 Call
26 }
27
28 export interface RegistrationWithServices<S> {
29 services?: ServiceMap<S>;
30 }
31
32 export interface ServiceRegistration<T, P, S> extends RegistrationWithServices<S> {
33
34 activation?: "singleton" | "container" | "hierarchy" | "context" | "call";
35
36 params?: P;
37
38 inject?: object | object[];
39
40 cleanup?: ((instance: T) => void) | string;
41 }
42
43 export interface TypeRegistration<T, P, S> extends ServiceRegistration<T, P, S> {
44 $type: string | Constructor<T>;
45 }
46
47 export interface FactoryRegistration<T, P, S> extends ServiceRegistration<T, P, S> {
48 $factory: string | Factory<T>;
49 }
50
51 export interface ValueRegistration<T> {
52 $value: T;
53 parse?: boolean;
54 }
55
56 export interface DependencyRegistration<S, K extends keyof S> extends RegistrationWithServices<S> {
57 $dependency: K;
58 lazy?: boolean;
59 optional?: boolean;
60 default?: S[K];
61 }
62
63 export type Parse<T> = T extends primitive ? T:
64 T extends Descriptor<infer V> ? V :
65 { [K in keyof T]: Parse<T[K]> };
11 export type PartialServiceMap<S> = {
12 [k in keyof S]?: Descriptor<S, S[k]>;
13 };
66 14
67 15 export interface Resolver<S> {
68 16 resolve<K extends keyof ContainerServices<S>, T extends ContainerServices<S>[K] = ContainerServices<S>[K]>(name: K, def?: T): T;
@@ -70,19 +18,4 export interface Resolver<S> {
70 18 export type ContainerServices<S> = S & {
71 19 container: Resolver<S>;
72 20 };
73
74 export function isTypeRegistration(x: any): x is TypeRegistration<any, any, any> {
75 return (!isPrimitive(x)) && ("$type" in x);
76 }
77
78 export function isFactoryRegistration(x: any): x is FactoryRegistration<any, any, any> {
79 return (!isPrimitive(x)) && ("$factory" in x);
80 }
81
82 export function isValueRegistration(x: any): x is ValueRegistration<any> {
83 return (!isPrimitive(x)) && ("$value" in x);
84 }
85
86 export function isDependencyRegistration<S>(x: any): x is DependencyRegistration<S, keyof S> {
87 return (!isPrimitive(x)) && ("$dependency" in x);
88 }
21 export type ActivationType = "singleton" | "container" | "hierarchy" | "context" | "call";
@@ -1,22 +1,22
1 1 import { Foo } from "./Foo";
2 // import { config } from "./config";
2 import { build } from "./config";
3 3
4 // const service = config.build("bar");
4 const service = build<Bar>();
5 5
6 // @service.consume({
7 // f: config.dependency("foo"),
8 // nested: {
9 // lazy: config.lazy("foo")
10 // }
11 // })
6 @service.consume({
7 foo: service.get("foo"),
8 nested: {
9 lazy: service.lazy("foo")
10 }
11 })
12 12 export class Bar {
13 13 barName = "bar";
14 14
15 15 _v: Foo | undefined;
16 16
17 constructor(_opts: {
18 foo: Foo;
19 nested: {
17 constructor(_opts?: {
18 foo?: Foo;
19 nested?: {
20 20 lazy: () => Foo
21 21 }
22 22 }) {
@@ -1,15 +1,17
1 1 import { services } from "../di/Annotations";
2 2 import { Bar } from "./Bar";
3 import { Foo } from "./Foo";
3 4
4 5 // declare required dependencies
5 6 const config = services<{
6 7 bar: Bar;
8 foo: Foo;
7 9 }>();
8 10
9 11 // export service descriptor
10 12 export const service = config.build<Box<Bar>>();
11 13
12 @service.consume(config.dependency("bar"))
14 @service.consume(config.get("bar"))
13 15 export class Box<T> {
14 16 private _value: T | undefined;
15 17
@@ -17,9 +19,10 export class Box<T> {
17 19 this._value = value;
18 20 }
19 21
20 // @service.inject("bar")
21 setValue(value: T) {
22 @service.inject( config.get("bar"))
23 setValue(value?: T) {
22 24 this._value = value;
25 return value;
23 26 }
24 27
25 28 setObj(value: object) {
@@ -1,8 +1,7
1 1 import { Foo } from "./Foo";
2 2 import { Bar } from "./Bar";
3 3 import { Box } from "./Box";
4 import { primitive } from "../safe";
5 import { Constructor } from "../interfaces";
4 import { Builder } from "../di/Annotations";
6 5
7 6 interface Services {
8 7 foo: Foo;
@@ -15,24 +14,14 interface Services {
15 14
16 15 }
17 16
18 interface TypeDescriptor<T, C extends Constructor<T>> {
19 $type: C;
17 const services = {
18 build: <T>() => {
19 return new Builder<T, Services>();
20 }
21 };
20 22
21 params: Wrap<ConstructorParameters<C>>;
22 }
23 namespace services {
23 24
24 function typeRegistration<T, C extends Constructor<T>>(target: C, params: Wrap<ConstructorParameters<C>>): TypeDescriptor<T, C> {
25 throw new Error();
26 25 }
27 26
28 declare function register<T>(): { type<C extends Constructor<T>>(target: C, params: Wrap<ConstructorParameters<C>>): TypeDescriptor<T, C>};
29
30 type Wrap<T> = T extends primitive ? T :
31 { [k in keyof T]: Wrap<T[k]> } | TypeDescriptor<T, Constructor<T>>;
32
33 const config: Wrap<Services> = {
34 foo: typeRegistration(Foo, []),
35 bar: typeRegistration(Bar, [{ foo: null as any, nested: null as any }]),
36 box: register<Box<Foo>>().type(Box, [{ $type: Bar, params: [] }]),
37 host: ""
38 };
27 export = services;
@@ -91,4 +91,5 test("Load configuration from module", a
91 91
92 92 t.assert(!isNull(b1), "bar should not be null");
93 93 t.assert(!isNull(b1._v), "bar.foo should not be null");
94
94 95 });
General Comments 0
You need to be logged in to leave comments. Login now